C# 无法删除需要父属性的ef实体
我对EF不太满意,所以也许这很简单 我有C# 无法删除需要父属性的ef实体,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,我对EF不太满意,所以也许这很简单 我有 public void DeleteLicense(int licenseId) { var entityToDelete = context.Licenses.Find(licenseId); context.Licenses.Remove(entityToDelete); } 我已经检查了它是否找到了正确的许可证,并且上下文是一个ninject(每个请求一个)DbContext 但在运行上述函
public void DeleteLicense(int licenseId)
{
var entityToDelete = context.Licenses.Find(licenseId);
context.Licenses.Remove(entityToDelete);
}
我已经检查了它是否找到了正确的许可证,并且上下文是一个ninject(每个请求一个)DbContext
但在运行上述函数后,在上下文上调用SaveChanges()时,我遇到了一个奇怪的错误。我得到:“客户名称字段是必需的。”
现在这很奇怪,因为CustomerName在帐户(不是许可证)中,它们是链接的,但仍然存在。下面是更多:
我的账户实体
[Required]
public String CustomerName { get; set; }
public virtual ICollection<License> Licenses { get; set; }
...
我的流畅设置
modelBuilder.Entity<Account>().HasMany(x => x.Licenses)
.WithRequired(x => x.Account).WillCascadeOnDelete(false);
\u accountRepository如下所示
public class EFAccountRepository : EntityFrameworkRepository<Account>
, IAccountRepository
public EFAccountRepository(EvercateContext context) : base(context)
{
}
public类EFAccountRepository:EntityFrameworkRepository
,IAccountRepository
public EFAccountRepository(EvercateContext):基(context)
{
}
这是Ninject中的代码,它设置了所有这些
kernel.Bind<EvercateContext>()
.To<EvercateContext>()
.InRequestScope()
.WithConstructorArgument("connectionStringOrName", "EvercateConnection");
kernel.Bind<IAccountRepository>().To<EFAccountRepository>();
kernel.Bind()
.至()
.InRequestScope()
.带有Constructor参数(“连接字符串名称”、“EvercateConnection”);
kernel.Bind().To();
因此,即使我使用工作单元,在运行SaveChanges之前,在这个请求中也不会调用其他任何东西。
是否有任何方法可以查看DbContext在SaveChanges上会做什么,而不实际运行该方法(因为它抛出了DbEntityValidationException)我尝试按照建议重写SaveChanges。 当我这样做时,我发现一个许可证即将被删除(它应该被删除),但我也发现了一个即将创建的帐户 我更改了DeleteLicense,如下所示
public void DeleteLicense(int licenseId)
{
var entityToDelete = context.Licenses.Find(licenseId);
entityToDelete.Account = null;
context.Licenses.Remove(entityToDelete);
}
代码马上就可以工作了。许可证已删除,帐户仍在,但未创建新帐户
但是为什么,我一点也不明白为什么。
这是我与fluent api设置的关系吗?我可以想象,如果您像这样初始化
许可证
构造函数中的帐户
导航属性,可能会发生这种奇怪的异常:
public License
{
Account = new Account();
}
当你打电话的时候
var entityToDelete = context.Licenses.Find(licenseId);
context.Licenses.Remove(entityToDelete);
…那么可能是:
实体被加载(无导航属性许可证
)并附加到上下文(状态帐户
)未更改
- 构造函数设置
导航属性,但它不会附加(状态为帐户
)已分离
- 当您为
实体调用许可证
时,EF会在内部调用Remove
。它检测到DetectChanges
正在引用分离的实体,并将其附加到上下文(状态为License.Account
)。Added
许可证的状态更改为
已删除
- 当您调用
时,更改跟踪器会找到两个实体:状态为SaveChanges
已删除的
和状态为许可证
已添加的
帐户
- 验证运行并发现实体
所需的属性帐户
为CustomerName
(因为只调用了null
的默认构造函数)帐户
- 将引发验证异常
在任何情况下,您都应该删除
帐户=新帐户()从许可证
构造函数中选择code>,并检查是否在代码库中的实体构造函数中初始化其他引用导航属性。(初始化空的导航集合是可以的。)这是一个众所周知的奇怪问题的常见来源,这些问题很难找到和理解。在我的例子中,发生这种情况是因为我的实体有一个[必需的]
属性,该属性的类型为int?
,使其可以为空。在检查从数据库返回的模型时,我看到属性有一个值,但由于某种原因,最终保存到数据库的实体在SaveChanges
过程中被删除了该值。当我将值切换到预期的int
类型时,所有操作都正常:耸耸肩:我有一个类似的问题,对我来说,我似乎没有正确地建立父母和孩子在各自班级中的关系
我的修复方法是将下面指定的属性添加到表示其父类Id的属性的子类中
public class Child
{
[Key, Column(Order = 1)]
public string Id { get; set; }
[Key, ForeignKey("Parent"), Column(Order = 2)] // adding this line fixed things for me
public string ParentId {get; set;}
}
public class Parent
{
[Key, Column(Order = 1)]
public string Id { get; set; }
...
public virtual ICollection<Child> Children{ get; set; }
}
公共类子类
{
[键,列(顺序=1)]
公共字符串Id{get;set;}
[Key,ForeignKey(“父”),Column(Order=2)]//添加此行为我解决了一些问题
公共字符串ParentId{get;set;}
}
公共类父类
{
[键,列(顺序=1)]
公共字符串Id{get;set;}
...
公共虚拟ICollection子项{get;set;}
}
似乎没有什么意义。这个DeleteLicense
方法是否是您可能无意中创建了帐户的更大工作单元的一部分?很高兴知道不仅仅是我。我更新了问题中的更多细节。但是总结是:我可以看看SaveChanges不运行它会做什么吗?您可以在DbContext
中重写SaveChanges
,并检查this.ChangeTracker.Entries()
。读到这篇文章,我会怀疑您有一个帐户=新帐户()代码>在您的许可证中
实体构造函数。当您调用…Remove(…)
时,由于在Remove
内部发生的自动更改检测,该帐户可能会添加到上下文中(状态为added
)。因为帐户
仅使用默认构造函数创建客户名称
可能是null
,这导致了验证异常。删除构造函数中的那一行(假设它真的存在)。太棒了!!就这样!如果你把它作为一个答案,我可以接受这个问题。我们每天都学到新的东西。非常感谢您出色的回答!斯劳玛,可以吗
var entityToDelete = context.Licenses.Find(licenseId);
context.Licenses.Remove(entityToDelete);
public class Child
{
[Key, Column(Order = 1)]
public string Id { get; set; }
[Key, ForeignKey("Parent"), Column(Order = 2)] // adding this line fixed things for me
public string ParentId {get; set;}
}
public class Parent
{
[Key, Column(Order = 1)]
public string Id { get; set; }
...
public virtual ICollection<Child> Children{ get; set; }
}