entity-framework – 删除具有一对多关系的子项的父项

我有一个带有Entity Framework 5.0 e sql Server CE 4.0的.NET4.0应用程序.

我有两个实体,一对多(父/子)关系.我已将其配置为在父删除时级联删除,但由于某种原因它似乎不起作用.

这是我的实体的简化版本:

public class Account
    {
        public int AccountKey { get; set; }
        public string Name { get; set; }

        public ICollection<User> Users { get; set; }
    }

    internal class AccountMap : EntityTypeConfiguration<Account>
    {
        public AccountMap()
        {
            this.HasKey(e => e.AccountKey);
            this.Property(e => e.AccountKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            this.Property(e => e.Name).Isrequired();
        }
    }


    public class User
    {
        public int UserKey { get; set; }
        public string Name { get; set; }

        public Account Account { get; set; }
        public int AccountKey { get; set; }
    }

    internal class UserMap : EntityTypeConfiguration<User>
    {
        public UserMap()
        {
            this.HasKey(e => e.UserKey);
            this.Property(e => e.UserKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            this.Property(e => e.Name).Isrequired();


            this.Hasrequired(e => e.Account)
                .WithMany(e => e.Users)
                .HasForeignKey(e => e.AccountKey);
        }
    }

    public class TestContext : DbContext
    {
        public TestContext()
        {
            this.Configuration.LazyLoadingEnabled = false;
        }

        public DbSet<User> Users { get; set; }
        public DbSet<Account> Accounts { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();
            modelBuilder.LoadConfigurations();
        }

    }

连接字符串:

<connectionStrings>
    <add name="TestContext" connectionString="Data Source=|DataDirectory|\TestDb.sdf;" providerName="System.Data.sqlServerCe.4.0" />
  </connectionStrings>

以及我应用程序工作流程的简化版本:

static void Main(string[] args)
{
    try
    {
        Database.Setinitializer(new DropCreateDatabaseAlways<TestContext>());
        using (var context = new TestContext())
            context.Database.Initialize(false);

        Account account = null;
        using (var context = new TestContext())
        {
            var account1 = new Account() { Name = "Account1^" };
            var user1 = new User() { Name = "User1",Account = account1 };

            context.Accounts.Add(account1);
            context.Users.Add(user1);

            context.SaveChanges();

            account = account1;
        }

        using (var context = new TestContext())
        {
            context.Entry(account).State = EntityState.Deleted;
                    context.SaveChanges();
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }

    Console.WriteLine("\nPress any key to exit...");
    Console.ReadLine();
}

当我尝试删除父实体时,它会抛出:

The relationship Could not be changed because one or more of the
foreign-key properties is non-nullable. When a change is made to a
relationship,the related foreign-key property is set to a null value.
If the foreign-key does not support null values,a new relationship
must be defined,the foreign-key property must be assigned another
non-null value,or the unrelated object must be deleted.

我相信我的关系配置还可以(followed the documentation).我也搜索guidelines on deleting detached entities.

我真的不明白为什么删除不起作用.我想避免加载所有子项,逐个删除它们并删除父项,因为必须有一个更好的解决方案.

解决方法

将实体的状态设置为已删除并为此实体调用DbSet< T> .Remove是不一样的.

不同之处在于,设置状态仅将根实体(传递给context.Entry的实体)的状态更改为已删除但不更改相关实体的状态,而如果使用级联删除配置关系,则删除会执行此操作.

如果你得到一个例外实际上取决于孩子(全部或只是一部分)被附加到上下文.这导致了一种有点难以遵循的行为:

>如果您调用删除,则无论子项是否已加载,都不会出现异常.仍然存在差异:

>如果子项附加到上下文,EF将为每个附加的子项生成DELETE语句,然后为父项生成(因为删除确实将它们全部标记为已删除)
>如果子项没有附加到上下文,EF将只向父数据库发送DELETE语句,并且因为启用了级联删除,数据库也将删除子项.

>如果将根实体的状态设置为“已删除”,则可能会出现异常:

>如果孩子被附加到上下文,他们的状态将不会被设置为已删除,并且EF会抱怨您尝试删除所需关系中的主体(根实体)而不删除受抚养人(子女)或至少没有将其外键设置为另一个未处于“已删除”状态的根实体.这是您的例外:帐户是根,user1是帐户和调用context.Entry(帐户).State = EntityState.Deleted;还会将状态为“未更改”的user1附加到上下文中(或者在SaveChanges中更改检测将执行此操作,我不确定是否与此相关). user1是account.Users集合的一部分,因为关系fixup在第一个上下文中将它添加到集合中,尽管您没有在代码中明确添加它.
>如果没有子项附加到上下文设置,则根状态为Deleted将向数据库发送DELETE语句,并且数据库中的级联删除也将删除子项.这无一例外地起作用.例如,如果在第二个上下文中将状态设置为Deleted之前或在输入第二个上下文之前设置account.Users = null,则代码将起作用.

在我看来使用删除

using (var context = new TestContext())
{
    context.Accounts.Attach(account);
    context.Accounts.Remove(account);
    context.SaveChanges();
}

…显然是首选方式,因为Remove的行为更像是您对级联删除所需的关系(在您的模型中就是这种情况).手动状态改变的行为对其他实体的状态的依赖性使得它更难以使用.我认为它仅作为特殊情况的高级用法.

这种差异并不广为人知或有记载.我看过很少有关于它的帖子.我现在唯一能找到的就是this one by Zeeshan Hirani.

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 &#39;EastRiver&#39; 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...