You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

操作Observable Collection时触发ObjectDisposedException问题求助

问题根源

你遇到的ObjectDisposedException是因为在AddUser方法中使用using (context)导致全局的RecipeDBContext实例被提前释放:

  • 首次添加时,using代码块内执行Users.Add(user)触发CollectionChanged,此时context尚未被释放,SaveChanges正常执行;
  • using代码块结束后,context.Dispose()被自动调用,context实例失效;
  • 后续第二次添加、删除本次运行添加的项时,CollectionChanged事件中尝试使用已释放的context,就会抛出异常;
  • 删除上一次运行添加的项看似正常,是因为这些实体是程序启动时从数据库加载的,可能已经脱离了当前context的跟踪,但本质上此时context已经失效,后续操作仍会出问题。
解决方案

1. 移除AddUser中的using (context)

ViewModel中持有全局的context实例,不需要在单次操作中手动释放,直接移除using块:

[RelayCommand]
public void AddUser(object o)
{
    if (o is not string username || string.IsNullOrEmpty(username))
        return;

    var user = new User()
    {
        Username = username,
    };
    Users.Add(user);
}

2. 让ViewModel实现IDisposable,正确释放资源

ViewModel销毁时,需要取消事件订阅并释放context:

public partial class UserViewModel : ObservableObject, IDisposable
{
    private RecipeDBContext _context;

    [ObservableProperty]
    private ObservableCollection<User> _users;

    public UserViewModel()
    {
        _context = new RecipeDBContext();
        _users = new ObservableCollection<User>();
        UpdateUsers(_context.User.ToList());
        _users.CollectionChanged += OnCollectionChanged;
    }

    public void UpdateUsers(List<User> users)
    {
        _users.Clear();
        foreach (User user in users)
        {
            Users.Add(user);
        }
    }

    void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (User newItem in e.NewItems)
            {
                _context.User.Add(newItem);
            }
        }

        if (e.OldItems != null)
        {
            foreach (User oldItem in e.OldItems)
            {
                _context.User.Remove(oldItem);
            }
        }
        _context.SaveChanges();
    }

    [RelayCommand]
    private void DeleteUser(User user)
    {
        Users.Remove(user);
    }

    [RelayCommand]
    public void AddUser(object o)
    {
        if (o is not string username || string.IsNullOrEmpty(username))
            return;

        var user = new User()
        {
            Username = username,
        };
        Users.Add(user);
    }

    public void Dispose()
    {
        _users.CollectionChanged -= OnCollectionChanged;
        _context.Dispose();
    }
}

3. 推荐使用依赖注入管理DbContext生命周期(最佳实践)

手动创建DbContext容易出现生命周期管理问题,WinUI 3中可以通过依赖注入来托管DbContext:

步骤1:在App.xaml.cs中注册服务

using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;

namespace Test;

public partial class App : Application
{
    public IServiceProvider Services { get; }

    public App()
    {
        InitializeComponent();

        // 构建服务容器
        var services = new ServiceCollection();
        // 注册DbContext,使用SQLite
        services.AddDbContext<RecipeDBContext>(options =>
            options.UseSqlite("Data Source=recipe.db"));
        // 注册ViewModel
        services.AddTransient<UserViewModel>();

        Services = services.BuildServiceProvider();
    }

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        var window = new Window();
        var userPage = new UserPage();
        // 从服务容器获取ViewModel并赋值
        userPage.ViewModel = Services.GetRequiredService<UserViewModel>();
        window.Content = userPage;
        window.Activate();
    }
}

步骤2:修改ViewModel构造函数,通过注入获取DbContext

public partial class UserViewModel : ObservableObject, IDisposable
{
    private readonly RecipeDBContext _context;

    [ObservableProperty]
    private ObservableCollection<User> _users;

    // 通过构造函数注入获取DbContext
    public UserViewModel(RecipeDBContext context)
    {
        _context = context;
        _users = new ObservableCollection<User>();
        UpdateUsers(_context.User.ToList());
        _users.CollectionChanged += OnCollectionChanged;
    }

    // ... 其他方法保持不变 ...

    public void Dispose()
    {
        _users.CollectionChanged -= OnCollectionChanged;
        // 依赖注入会管理DbContext的释放,这里可以不用手动调用Dispose,
        // 但如果是Transient生命周期,手动释放也没问题
        _context.Dispose();
    }
}

步骤3:确保View中正确使用ViewModel

在UserPage.xaml.cs中添加ViewModel属性:

public sealed partial class UserPage : Page
{
    public UserViewModel ViewModel { get; set; }

    public UserPage()
    {
        InitializeComponent();
    }
}

这样依赖注入容器会自动处理DbContext的创建和释放,彻底避免手动管理生命周期带来的问题。

内容的提问来源于stack exchange,提问作者Nuvolari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:36:08