使用存储库模式更新实体框架中的集合导航属性
本文关键字:集合 导航 属性 框架 实体 存储 模式 更新 | 更新日期: 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(宠物宠物)方法。对于一个复杂和更深嵌套的模型,这很快就会变得很麻烦。
- 直接获取DbContext,准备修改或添加宠物。但是,我实现了一个不直接访问DbContext的存储库。
- 修改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;
}
}
}