使用存储库模式更新实体框架中的集合导航属性

本文关键字:集合 导航 属性 框架 实体 存储 模式 更新 | 更新日期: 2023-09-27 18:02:33

我编写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;

然而,这打乱了我的存储库模式。在我看来,我有三个选项:

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

我在这里错过了什么?有没有更好的方法?

使用存储库模式更新实体框架中的集合导航属性

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

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

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

我在Pluralsight的企业实体框架课程中学到了这一点。在其中,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;
        }
    }
}