【问题标题】:Entity Framework proper way to replace collection in one to many实体框架以一对多替换集合的正确方法
【发布时间】:2015-07-18 22:03:43
【问题描述】:

假设一个客户有很多电话号码,而一个电话号码只有一个客户。

public class PhoneNumber : IValueObject {
  public string Number {get; set;}
  public string Type {get; set;}
}

public class Customer : IEntity {
   public ICollection<PhoneNumber> phones {get; private set;} //ew at no encapsulated collection support
   public void SetPhones(params PhoneNumber[] phones) {
       this.phones.Clear();
       this.phones.AddRange(phones);
   }
}

如果我像这样进行 EF 映射并运行它,每次我设置电话号码时,它都会创建新的 PhoneNumbers,但不会删除旧的。没有其他实体引用电话号码,我什至没有在我的 dbcontext 上公开它,有没有办法告诉 EF Customer 完全拥有 PhoneNumbers,因此如果电话号码从集合中删除,它们应该被删除?

我意识到有十几种方法可以解决这个问题,但这并不是一个奇怪的极端情况,处理这个问题的“正确”方法是什么。

【问题讨论】:

  • 我认为如果您从 dbset 中删除电话号码并打开删除时的 cascasde ,那么它会产生您想要的结果:)
  • @JenishRabadiya 我认为删除时的级联可能会在删除Customer 时删除电话号码,但这特别是关于修改...
  • 请发布您尝试从Customer 中删除PhoneNumber 的代码部分。
  • @Kilouco SetPhones 方法是迄今为止唯一可以做任何事情的方法。

标签: c# entity-framework orm mapping entity-framework-6


【解决方案1】:

第一个(可选):

我建议你做

public ICollection&lt;PhoneNumber&gt; phones {get; private set;}

virtual 属性,让 Entity Framework 知道它应该是延迟加载的(即使您没有启用 Lazy Load,这也是一个不错的选择练习)。

public virtual ICollection&lt;PhoneNumber&gt; phones {get; private set;}

第二

在您的PhoneNumber 类上添加一个反向导航属性(这将是必需,以实现我在下面为您提供的解决方案):

public class PhoneNumber : IValueObject {
  public string Number {get; set;}
  public string Type {get; set;}

  public virtual Customer {get; set;}
}

public class Customer : IEntity {
   public ICollection<PhoneNumber> phones {get; private set;} //ew at no encapsulated collection support
   public void SetPhones(params PhoneNumber[] phones) {
       this.phones.Clear();
       this.phones.AddRange(phones);
   }
}

第三个(您的问题的可能解决方案):

从 Context 中删除 PhoneNumber 对象,而不是从 Customer 中删除:

public ICollection<PhoneNumber> phones {get; private set;} //ew at no encapsulated collection support
   public void SetPhones(params PhoneNumber[] phones) {
       Context.PhoneNumbers.RemoveRange(this.phones);
       this.phones.AddRange(phones);
   }
}

【讨论】:

  • Update-Database 期间出错。 An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types...A relationship from the 'Customer_phones' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'Customer_phones_Target' must also in the 'Deleted' state.
  • @GeorgeMauer 尝试评论第三步,只做前两步。它们应该足以解决您的问题。
  • 删除第三步(即使我保持PhoneNumber->Customer 关系)会导致上面截图中的问题。旧电话号码现在只有一个空客户,它们不会被删除。
  • 另外,您可以尝试在public virtual Customer {get; set;} 正上方添加[Required] 或添加属性public int CustomerId {get; set;},而不是执行我的新“第三步”,或者添加属性public int CustomerId {get; set;},该属性表示客户不得为空。
  • 如果我的 cmets 的任何新想法有效,请告诉我,以便我编辑答案。
【解决方案2】:

我也有同样的问题:)

identifying relationships 上的这个答案解决了我的问题。

注意:您必须(急切地、显式地或延迟地)加载集合,以便在设置新值和调用 save 之前对其进行跟踪。否则,您将不会替换集合,而只是将其添加到其中。

例如:

var entity = unitOfWork.EntityRepository.GetById(model.Id);
// I have something like this to load collection because
// I don't have the collection's entities exposed to the context
unitOfWork.EntityRepository.LoadCollection(entity, e => e.CollectionProperty);
entity.CollectionProperty = newCollectionValuesList;
unitOfWork.Save();

这将从“集合表”中删除以前的集合值,只添加新设置的值。

希望对您有所帮助。

【讨论】:

  • +1 用于实际正确地做事并使存储库“流出”一个工作单元。我仍然没有真正得到答案。 Entity 在这里会是什么样子?你是说这个带有电话号码ID的技巧单独足以让它正确触发删除?
  • @GeorgeMauer - 这里的实体将是您的 CustomerCollectionProperty 将是您的 phones 属性。就我而言,我正在明确加载我的收藏,所以 LoadCollection 在引擎盖下看起来像 context.Entry(entity).Collection(navigationProperty).Load();。如果您在 Customer 和 PhoneNumber 之间存在识别关系,并且您已经为客户加载了 phones,那么您屏幕截图中的代码应该只在 PhoneNumbers 表中留下 123-456-7890。
  • @QuintonSmith 是您编写的“Save”方法,它实际上最终只是调用“SaveChanges()?”
  • @KieranOjakangas 你是对的。在引擎盖下它只是context.SaveChanges();
  • 非常感谢@QuintonSmith,您的评论为我节省了一天的时间,即需要加载子集合以进行替换。
猜你喜欢
  • 2011-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-21
相关资源
最近更新 更多