代码之家  ›  专栏  ›  技术社区  ›  Nuvolari

从可观测集合添加/删除时出现对象处理异常

  •  0
  • Nuvolari  · 技术社区  · 1 年前

    在向Observable集合添加和删除项时,我收到一个ObjectDisposed异常。

    正在添加

    如果我向集合中添加一个新项目,它可以正常工作,但如果我试图在程序的同一运行中添加第二个项目,我会得到一个Object Disposed Exception。如果我关闭并重新启动程序,它将允许我再添加1个,但不能再添加了。

    正在删除

    如果我删除上次运行程序时添加到数据库中的项,它会正常工作并将其删除。但是,如果我在同一次运行中添加了一些内容,然后尝试删除刚刚添加的内容,它会引发异常。

    错误消息:

    系统ObjectDisposedException:“无法访问已释放的上下文实例。”。导致此错误的一个常见原因是处理通过依赖项注入解决的上下文实例,然后尝试在应用程序的其他地方使用相同的上下文实例。如果对上下文实例调用“Dispose”,或将其包装在using语句中,则可能会发生这种情况。如果使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。 对象名称:“RecipeDBContext”。“

    我正在使用实体框架核心与SqlLite数据库,WinUi 3和MVVM社区工具包。

    查看模型

    public partial class UserViewModel : ObservableObject
    {
        RecipeDBContext context;
    
        [ObservableProperty]
        private ObservableCollection<User> _users;      
    
        public UserViewModel()
        {
            context = new RecipeDBContext();
            _users = new ObservableCollection<User>();
            UpdateUsers(context.User.ToList());
            _users.CollectionChanged += this.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 != null)
          {
              string username = o as string;
             if (username != String.Empty)
             {
                using (context)
                {
                    var user = new User()
                    {                            
                        Username = username,
                    };
                    Users.Add(user);                                                
                }
            }
        }
    }
    

    模型

    public partial class User: ObservableObject
    {
        public int Id { get; set; }
    
        [ObservableProperty]
        private string _username = string.Empty;
    
        
        partial void OnUsernameChanged(string? oldValue, string newValue)
        {
            
        }
    }
    

    DbContext

    public class RecipeDBContext: DbContext
    {    
        public DbSet<User> User { get; set; }
    
        public RecipeDBContext() 
        {
            Database.EnsureCreated();
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Data Source=recipe.db");
        }
    }
    

    看法

    <Page
        x:Class="Test.Views.UserPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Test.Views"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="using:Test.ViewModels"    
        mc:Ignorable="d"    
        xmlns:m="using:Test.Models"
        x:Name="ThisPage">
    
        <Grid>
    
            <Grid.RowDefinitions>
                <RowDefinition Height="0.2*"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.3*"></ColumnDefinition>
                <ColumnDefinition Width="0.3*"></ColumnDefinition>
                <ColumnDefinition Width="0.3*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Border BorderThickness="1" BorderBrush="Black" />
            <TextBlock FontSize="30" Grid.Row="0" Grid.Column="0">LOGIN</TextBlock>
            <TextBlock Grid.Row="1" Grid.Column="0">User</TextBlock>
            <ListView x:Name="usersListView" Grid.Row="1" Grid.Column="1" ItemsSource="{x:Bind ViewModel.Users, Mode=TwoWay}">
                <ListView.Header>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="0.3*"></ColumnDefinition>
                            <ColumnDefinition Width="0.3*"></ColumnDefinition>
                            <ColumnDefinition Width="0.3*"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <ListViewHeaderItem  Grid.Column="0">
                            <TextBlock>Username</TextBlock>
                        </ListViewHeaderItem>
                        <ListViewHeaderItem  Grid.Column="1">
                            <TextBlock>Recipes</TextBlock>
                        </ListViewHeaderItem>
                        <ListViewHeaderItem  Grid.Column="2">
                            <TextBlock>Delete</TextBlock>
                        </ListViewHeaderItem>
                    </Grid>
                </ListView.Header>
    
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="m:User">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.3*"></ColumnDefinition>
                                <ColumnDefinition Width="0.3*"></ColumnDefinition>
                                <ColumnDefinition Width="0.3*"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="{x:Bind Username}" Grid.Column="0"></TextBlock>
                            <Button Grid.Column="2" 
                                    Command="{Binding ElementName=ThisPage, Path=ViewModel.DeleteUserCommand}"
                                    CommandParameter="{x:Bind}" 
                                    Content="X"/>
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    
            <TextBlock Grid.Row="3" Grid.Column="0" FontSize="30">New User</TextBlock>
            <TextBlock Grid.Row="4" Grid.Column="0">Name</TextBlock>
            <TextBox x:Name="newUserName" Grid.Row="4" Grid.Column="1"></TextBox>
            <Button x:Name="addUserBtn1" Command="{x:Bind ViewModel.AddUserCommand}" CommandParameter="{Binding Text, ElementName=newUserName}" Grid.Row="5" Grid.Column="2">Add User</Button>
        </Grid>
    </Page>
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Ivan Petrov    1 年前

    查看您的代码 AddUser 方法具有 using(context) 哪个在呼叫 context.Dispose() 在街区的尽头,你会得到一个 System.ObjectDisposedException 。只需省略 using 暂时。

    [RelayCommand]
    public void AddUser(object o) {
        if (o != null) {
            string username = o as string;
            if (username != String.Empty) {
                using (context) { // you are disposing
                    var user = new User() {
                        Username = username,
                    };
                    Users.Add(user);
                }
            }
        }
    }