无法使用工作单元模式从 POCO 中删除子实体
我在 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()
时出现的错误>
操作失败:无法更改关系,因为一个或多个外键属性不可为空。当关系发生更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。
发生这种情况是因为 EF 不会只是自动从数据库中删除 Agent 实体,因为它已从我的 Tour 类的 Agents 集合中删除。
我需要显式调用 dbContext.Agents.DeleteObject(a); ,但我的问题是,我无法从 POCO 中访问 dbContext。
有什么办法可以处理这种情况吗?
I am using POCO classes on an EF4 CTP5 project and I am having trouble deleting child properties. Here's my example (hopefully not too long).
Relevant Portions of the Tour Class
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));
}
}
Command Handler
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();
}
}
Here's the error that I get when my UnitOfWork calls DbContext.SaveChanges()
The operation failed: 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.
This is happening because EF wont just automatically delete the an Agent entity from the database just because it has been removed from the Agents collection in my Tour class.
I need to explicitly call dbContext.Agents.DeleteObject(a);
, but my problem is, I don't have access to the dbContext from within my POCO.
Is there any way to handle this scenario?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
根据您当前的架构,恐怕您需要为您的
DeleteAgentCommandHandler
提供第二个存储库(IRepository
,我猜),然后调用类似 <在第二个存储库上执行 code>Delete(command.AgentId) 。或者,您可以将
IUnitOfWork
扩展为存储库工厂,因此该接口将获得一个额外的方法,例如T CreateRepository()
,它允许您拉取任何实例工作单元中的通用存储库。 (然后您只需将IUnitOfWork
注入到DeleteAgentCommandHandler
中,而不再是存储库。)或者远离业务/UI 层中的通用存储库。如果
Agent
完全依赖于Tour
它根本不需要有存储库。非通用的ITourRepository
可以有方法来适当地处理从数据库层中的游览中删除代理的情况。With your current architecture I am afraid you need to feed your
DeleteAgentCommandHandler
with a second repository (IRepository<Core.Domain.Agent>
, I guess) and then call something likeDelete(command.AgentId)
on that second repository.Or you could extend your
IUnitOfWork
to be a factory of repositories, so the interface would get an additional method likeT CreateRepository<T>()
which allows you to pull any instance of your generic repository from the unit of work. (Then you only need to injectIUnitOfWork
into theDeleteAgentCommandHandler
, and not the repositories anymore.)Or stay away from generic repositories in your business/UI layer. If
Agent
is completely dependent onTour
it doesn't need to have a repository at all. A non-genericITourRepository
could have methods to handle the case of removing an agent from a tour in the database layer appropriately.这看起来确实应该有效。我发现这篇文章表明正在针对未来版本研究此功能:
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/58a31f34-9d2c-498d-aff3-fc96988a3ddc/
我已经还发现了另一篇文章(在某个地方 - 不幸的是我丢失了它),建议将父实体的密钥添加到 DbContext OnModelCreating 方法中的子实体,如下所示:
目前,这会在运行时使用代码优先抛出异常,尽管我已经在以下情况下工作了通过修改 XAML 来使用 EDMX 文件,将父键包含在存储数据模型以及概念数据模型中。我认为这种行为差异是因为对于 EDMX 文件,EF 相信它保存的存储元数据是准确的,而代码优先检查数据库以查看其模型是否匹配。
尽管我还没有尝试过,但可能有效的另一种方法是将父键作为复合键包含在子表中,以便代码优先是满意的。显然,更改数据库或破解 XAML 都不太理想,充其量只是解决方法。
This does seem like something that should work. I've found this post which suggests this feature is being investigated for future versions:
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/58a31f34-9d2c-498d-aff3-fc96988a3ddc/
I've also found another post (somewhere - unfortunately I lost it) which suggested adding the parent entity's key to the child entity in your DbContext OnModelCreating method like this:
Currently this throws an exception at runtime using code-first, although I have got this working when using an EDMX file by hacking the XAML to include the parent key in the store data model as well as the conceptual data model. I think this difference in behaviour is because in the case of the EDMX file, EF trusts that the store metadata it holds is accurate, whereas code-first checks the database to see whether it's model matches.
Another way which may work although I haven't yet tried it yet, is to include the parent key as a compound key in the child table so that code-first is happy. Obviously changing the database or hacking the XAML are both less than ideal and workarounds at best.