Cat*_*lin 9 c# domain-driven-design entity-framework ef-code-first
我正在使用DDD.我有一个类Product,它是一个聚合根.
public class Product : IAggregateRoot
{
public virtual ICollection<Comment> Comments { get; set; }
public void AddComment(Comment comment)
{
Comments.Add(comment);
}
public void DeleteComment(Comment comment)
{
Comments.Remove(comment);
}
}
Run Code Online (Sandbox Code Playgroud)
保持模型的层根本不知道EF.问题是,当我打电话时DeleteComment(comment),EF抛出异常
"Product_Comments"AssociationSet中的关系处于"已删除"状态.给定多重约束,相应的'Product_Comments_Target'也必须处于'已删除'状态.
即使从集合中删除元素,EF也不会删除它.如何在不破坏DDD的情况下解决这个问题?(我正在考虑建立一个评论库,但是不对)
代码示例:
因为我正在尝试使用DDD,它Product是一个聚合根,它有一个存储库IProductRepository.如果没有产品,则注释不能存在,因此是ProductAggregate 的子项,并Product负责创建和删除注释.Comment没有存储库.
public class ProductService
{
public void AddComment(Guid productId, string comment)
{
Product product = _productsRepository.First(p => p.Id == productId);
product.AddComment(new Comment(comment));
}
public void RemoveComment(Guid productId, Guid commentId)
{
Product product = _productsRepository.First(p => p.Id == productId);
Comment comment = product.Comments.First(p => p.Id == commentId);
product.DeleteComment(comment);
// Here i get the error. I am deleting the comment from Product Comments Collection,
// but the comment does not have the 'Deleted' state for Entity Framework to delete it
// However, i can't change the state of the Comment object to 'Deleted' because
// the Domain Layer does not have any references to Entity Framework (and it shouldn't)
_uow.Commit(); // UnitOfWork commit method
}
}
Run Code Online (Sandbox Code Playgroud)
Mos*_*osh 12
我见过很多人报道这个问题.它实际上很容易修复,但让我觉得没有足够的文档说明EF在这种情况下的行为方式.
技巧:在设置父和子之间的关系时,你必须在孩子身上创建一个"复合"键.这样,当您告诉Parent删除其1个或所有子节点时,实际上将从数据库中删除相关记录.
要使用Fluent API配置组合键:
modelBuilder.Entity<Child>.HasKey(t => new { t.ParentId, t.ChildId });
Run Code Online (Sandbox Code Playgroud)
然后,删除相关的孩子:
var parent = _context.Parents.SingleOrDefault(p => p.ParentId == parentId);
var childToRemove = parent.Children.First(); // Change the logic
parent.Children.Remove(childToRemove);
// or, you can delete all children
// parent.Children.Clear();
_context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
完成!
我已经看到3种解决EF不足的方法:
DbContext的SaveChanges()并处理删除有(按欣快的答案)我最喜欢选项3,因为它不需要修改数据库结构(1)或域模型(2),但首先将变通方法放在存在缺陷的组件(EF)中。
因此,这是从欣快的答案/博客文章中获取的更新解决方案:
public class MyDbContext : DbContext
{
//... typical DbContext stuff
public DbSet<Product> ProductSet { get; set; }
public DbSet<Comment> CommentSet { get; set; }
//... typical DbContext stuff
public override int SaveChanges()
{
MonitorForAnyOrphanedCommentsAndDeleteThemIfRequired();
return base.SaveChanges();
}
public override Task<int> SaveChangesAsync()
{
MonitorForAnyOrphanedCommentsAndDeleteThemIfRequired();
return base.SaveChangesAsync();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
MonitorForAnyOrphanedCommentsAndDeleteThemIfRequired();
return base.SaveChangesAsync(cancellationToken);
}
private void MonitorForAnyOrphanedCommentsAndDeleteThemIfRequired()
{
var orphans = ChangeTracker.Entries().Where(e =>
e.Entity is Comment
&& (e.State == EntityState.Modified || e.State == EntityState.Added)
&& (e.Entity as Comment).ParentProduct == null);
foreach (var item in orphans)
CommentSet.Remove(item.Entity as Comment);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:假设这ParentProduct是导航属性Comment回到其所有Product。
使用您的方法从产品中删除评论只会删除产品和评论之间的关联。这样评论仍然存在。
您需要做的是使用 方法告诉 ObjectContext Comment 也被删除DeleteObject()。
我这样做的方式是使用我的存储库的 Update 方法(了解实体框架)来检查已删除的关联并删除过时的实体。您可以使用 ObjectContext 的 ObjectStateManager 来完成此操作。
public void UpdateProduct(Product product) {
var modifiedStateEntries = Context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedStateEntries) {
var comment = entry.Entity as Comment;
if (comment != null && comment.Product == null) {
Context.DeleteObject(comment);
}
}
}
Run Code Online (Sandbox Code Playgroud)
样本:
public void RemoveComment(Guid productId, Guid commentId) {
Product product = _productsRepository.First(p => p.Id == productId);
Comment comment = product.Comments.First(p => p.Id == commentId);
product.DeleteComment(comment);
_productsRepository.Update(product);
_uow.Commit();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13109 次 |
| 最近记录: |