我有一个带有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();
}
当我尝试删除父实体时,它会抛出:
因为一个或多个关系无法改变 外键属性是不可为空的。当对a进行更改时 关系,相关的外键属性设置为空值。 如果外键不支持空值,则为新关系 必须定义外键属性必须另外分配 必须删除非空值或不相关的对象。
我相信我的关系配置正常(followed the documentation)。我还搜索了guidelines on deleting detached entities。
我真的不明白为什么删除不起作用。我想避免加载所有子项,逐个删除它们并删除父项,因为必须有一个更好的解决方案。
答案 0 :(得分:12)
将实体的状态设置为Deleted
并为此实体调用DbSet<T>.Remove
并不相同。
不同之处在于,设置状态只会将根实体(您传递到context.Entry
的那个)的状态更改为Deleted
,而不会更改相关实体的状态Remove
如果使用级联删除配置关系。
如果您获得异常,实际上取决于附加到上下文的子(全部或部分)。这导致了一种有点难以遵循的行为:
Remove
,你就不会得到例外,无论孩子是否被加载。仍然存在差异:
DELETE
语句,然后为父项生成{因为Remove
确实将它们全部标记为Deleted
)< / LI>
DELETE
语句到数据库,并且因为启用了级联删除,数据库也会删除子级。Deleted
,则可能会获得异常:
Deleted
,并且EF会抱怨您尝试删除所需关系中的主体(根实体)而不删除受抚养人(或者至少没有将其外键设置为另一个不在Deleted
状态的根实体。这是您的例外情况:account
是根,user1
是account
的依赖,调用context.Entry(account).State = EntityState.Deleted;
也会在状态user1
附加Unchanged
上下文(或SaveChanges
中的更改检测将执行此操作,我不确定是否相同)。 user1
是account.Users
集合的一部分,因为关系修正将其添加到第一个上下文中的集合中,尽管您没有在代码中明确添加它。Deleted
的根状态将向数据库发送DELETE
语句,并且数据库中的级联删除也将删除子项。这无一例外地起作用。例如,如果在第二个上下文中将状态设置为account.Users = null
之前或在输入第二个上下文之前设置Deleted
,则代码将起作用。在我看来使用Remove
...
using (var context = new TestContext())
{
context.Accounts.Attach(account);
context.Accounts.Remove(account);
context.SaveChanges();
}
...显然是首选方式,因为Remove
的行为更像您对级联删除所需的关系(模型中的情况)。手动状态改变的行为对其他实体的状态的依赖性使得它更难以使用。我认为它只是特殊情况下的高级用法。
差异并不广为人知或有记载。我看过很少有关于它的帖子。我现在唯一能找到的就是this one by Zeeshan Hirani。
答案 1 :(得分:1)
我尝试了一种略微不同的方法,奇怪的是,它起作用了。如果我替换此代码:
using (var context = new TestContext())
{
context.Entry(account).State = EntityState.Deleted;
context.SaveChanges();
}
通过这个:
using (var context = new TestContext())
{
context.Entry(account).State = EntityState.Unchanged;
context.Accounts.Remove(account);
context.SaveChanges();
}
没有其他问题。不确定这是一个错误还是我错过了什么。我真的很欣赏这个问题的一些亮点,因为我很确定第一种方式(EntityState.Deleted)是推荐的方式。