【问题标题】:Updating navigation properties that are collections in Entity Framework with Repository pattern使用存储库模式更新作为实体框架中集合的导航属性
【发布时间】:2014-05-08 22:41:57
【问题描述】:

我已经编写 C# 代码有一段时间了,我通常使用实体框架并实现存储库模式。存储库模式告诉我们,我们通常应该只维护和访问聚合根的存储库。考虑以下示例,其中 Person 是根:

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Pet> Pets { get; set; }
}

public class Pet 
{
    public int ID { get; set; }
    public string Name { get; set; }
}

上述模型意味着我们通常应该通过 PersonRepository 访问宠物。但是,如果我想为一个人修改或添加宠物,我从来没有找到一种优雅的方式来做到这一点。

为了正确识别要更新的内容,我需要调用

DbContext.Entry(myPet).State = EntityState.Modified;

但是,这与我的存储库模式相混淆。据我所知,我有三个选择:

  1. 创建一个 PersonRepository.AttachPet(Pet pet) 方法。对于复杂且更深的嵌套模型,这很快就会变得很麻烦。
  2. 直接获取 DbContext 以准备修改或添加宠物。但是,我实现了一个不直接访问 DbContext 的存储库。
  3. 修改 PersonRepository.Update(Person person) 自动更新底层宠物的状态。也不是很优雅,而且可能是一项艰巨的任务。

我在这里缺少什么?有更好的方法吗?

【问题讨论】:

  • 如果你将现有的宠物分配给一个人,我觉得你的宠物可能是它自己的聚合根(这就是我的意思)。有时将聚合视为依赖或独立存在的问题会有所帮助。如果宠物可以在没有 Person 的情况下存在,那么 Pet 应该是它自己的 AggregateRoot。
  • 在某些方面存在争议,但您遇到的问题可能是因为您试图在 ORM 之上实现存储库模式...stackoverflow.com/questions/15734485/…

标签: c# entity-framework repository-pattern


【解决方案1】:

是的,有更好的方法。对于新人,请使用:

_context.People.Add(myPerson); //myPerson can have Pets attached

这将遍历所有子对象并将它们标记为 NEW。 更新人物时,调用上述代码后,需要设置修改/删除哪些宠物对象。

我在 Pluralsight 课程 Entity Framework in the Enterprise 中学到了这一点。在其中,Julie 为 Pets 添加了一个额外的字段。

public enum ObjectState
{
    Unchanged,
    Added,
    Deleted,
    Modified
}

public interface IObjectWithState
{
    [NotMapped]
    [JsonIgnore]
    ObjectState ObjectState { get; set; }
}

public class Pet : IObjectWithState
{
    public int ID { get; set; }
    public string Name { get; set; }
}

您可能希望在所有数据库实体上使用此功能。

在您的存储库中

public void InsertOrUpdateGraph(Person entity)
{
    _context.People.Add(entity);
    if (entity.ID != default(int)) _context.ApplyStateChanges();
}

一些扩展

public static class ContextExtension
{
    public static void ApplyStateChanges(this DbContext context)
    {
        foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
        {
            IObjectWithState stateInfo = entry.Entity;
            entry.State = stateInfo.ObjectState.ConvertState();
        }
    }

    public static EntityState ConvertState(this ObjectState state)
    {
        switch (state)
        {
            case ObjectState.Modified:
                return EntityState.Modified;
            case ObjectState.Added:
                return EntityState.Added;
            case ObjectState.Deleted:
                return EntityState.Deleted;
            default:
                return EntityState.Unchanged;
        }
    }
}

每次都有效。

【讨论】:

  • 这是一个非常有希望的答案。我还没有时间尝试它,但它并没有被遗忘。但是,有一个小细节:由于 ChangeTracker 中的所有条目都是迭代的,这不会导致所有对象都被保存,包括那些与当前 Person 无关的对象?
  • 不,它不会导致通过迭代所有对象来保存它们。更改跟踪器连接到上下文,因此当您创建它时,其中唯一的人员对象应该是您刚刚输入的对象。请记住,它正在寻找将在数据库中更新的更改的对象。
猜你喜欢
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多