无法使用工作单元模式从POCO中删除子实体

时间:2011-03-17 19:56:36

标签: poco unit-of-work entity-framework-4.1

我在EF4 CTP5项目中使用POCO类,我在删除子属性时遇到问题。这是我的例子(希望不会太久)。

旅游类的相关部分

public partial class Tour
{
  public Guid TourId { get; private set; }
  protected virtual List<Agent> _agents { get; set; }

  public void AddAgent(Agent agent)
  {
    _agents.Add(agent);
  }

  public void RemoveAgent(Guid agentId)
  {
    var a = Agents.Single(x => x.AgentId == agentId);
    _agents.Remove(Agents.Single(x => x.AgentId == agentId));
  }
}

命令处理程序

public class DeleteAgentCommandHandler : ICommandHandler<DeleteAgentCommand>
{
  private readonly IRepository<Core.Domain.Tour> _repository;
  private readonly IUnitOfWork _unitOfWork;

  public DeleteAgentCommandHandler(
      IRepository<Core.Domain.Tour> repository, 
      IUnitOfWork unitOfWork
    )
  {
    _repository = repository;
    _unitOfWork = unitOfWork;
  }

  public void Receive(DeleteAgentCommand command)
  {
    var tour = _repository.GetById(command.TourId);
    tour.RemoveAgent(command.AgentId);

    // The following line just ends up calling
    // DbContext.SaveChanges(); on the current context.

    _unitOfWork.Commit();
  }
}

以下是我的UnitOfWork调用DbContext.SaveChanges()

时出现的错误
  

操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为foreign-key属性分配另一个非空值,或者必须删除不相关的对象。

这种情况正在发生,因为EF不会仅仅因为它已从我的Tour类中的Agents集合中删除而自动从数据库中删除了Agent实体。

我需要显式调用dbContext.Agents.DeleteObject(a);,但我的问题是,我无法从我的POCO中访问dbContext。

有没有办法处理这种情况?

2 个答案:

答案 0 :(得分:1)

使用您当前的体系结构,我担心您需要向DeleteAgentCommandHandler提供第二个存储库(我猜是IRepository<Core.Domain.Agent>),然后在第二个存储库中调用类似Delete(command.AgentId)的内容。< / p>

或者您可以将IUnitOfWork扩展为存储库的工厂,因此接口将获得一个额外的方法,如T CreateRepository<T>(),它允许您从工作单元中提取通用存储库的任何实例。 (那么你只需要将IUnitOfWork注入DeleteAgentCommandHandler,而不是注册库。)

或远离业务/ UI层中的通用存储库。如果Agent完全依赖于Tour,则根本不需要存储库。非通用ITourRepository可以有方法来处理从数据库层中的游览中删除代理的情况。

答案 1 :(得分:0)

这似乎应该有用。我发现这篇文章表明正在调查此功能的未来版本:

http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/58a31f34-9d2c-498d-aff3-fc96988a3ddc/

我还发现了另一篇帖子(某个地方 - 不幸的是我丢失了它),建议在DbContext OnModelCreating方法中将父实体的密钥添加到子实体,如下所示:

modelBuilder.Entity<Agent>()
.HasKey(AgentId)
.HasKey(TourId);

目前,这会在运行时使用代码优先抛出异常,尽管我在使用EDMX文件时通过黑客攻击XAML以在存储数据模型中包含父键以及概念数据模型来实现此功能。我认为这种行为差异是因为在EDMX文件的情况下,EF信任它保存的商店元数据是准确的,而代码优先检查数据库以查看它的模型是否匹配。

虽然我尚未尝试过,但可能有效的另一种方法是将父键作为复合键包含在子表中,以便代码优先。显然,更改数据库或黑客攻击XAML都不是理想的,也是最好的解决方法。