更新属性时,entity framework会遍历所有记录(60,000!)

我试图为POCO对象设置单个属性的值,它看起来像Entity正在检索最初从中检索POCO对象的数据库表中的每个记录。 这是代码:

public void DeleteFile(int fileid) { var context = GetContext(myTrans, false); FILE pocofile = (from f in context.FILES.All() where f.File_Id == fileId select f).FirstOrDefault(); // the following line causes a retrieve of 60,000 records pocofile.State_Id = Global.DeletedStateId // global constant // additional code that is eventually reached after about 10 minutes } 

我们有一个FILES表,它有一个映射到STATE表的State_Id列。 因此,当我尝试在上面设置State_Id属性时,似乎将第一个文件设置为ok,但是通过它在FILE poco类中命中的断点来判断它看起来像是在为db中的每个文件设置了State_Id从一开始。

FILE.cs类也粘贴在下面以供参考。 它基本上在60,000长循环中击中了很多getter和setter,如果我在任何给定时间在其中一个上设置断点并检查调试器中的File_Id,那么每次都有一个新的File_Id,这就是我的方法。知道它正在循环遍历所有这些。

 //------------------------------------------------------------------------------ //  // This code was generated from a template. // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. //  //------------------------------------------------------------------------------ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; namespace FORTRESSPOCO { public partial class FILE { #region Primitive Properties public virtual int File_Id { get; set; } public virtual string Name { get; set; } public virtual string Description { get; set; } public virtual Nullable Expiration_Date { get; set; } public virtual bool Is_Directory { get; set; } public virtual string Path { get; set; } public virtual int Data_Asset_Id { get { return _data_Asset_Id; } set { if (_data_Asset_Id != value) { if (Data_Asset != null && Data_Asset.Data_Asset_ID != value) { Data_Asset = null; } _data_Asset_Id = value; } } } private int _data_Asset_Id; public virtual int State_Id { get { return _state_Id; } set { if (_state_Id != value) { if (STATE != null && STATE.State_Id != value) { STATE = null; } _state_Id = value; } } } private int _state_Id; #endregion #region Navigation Properties public virtual Data_Asset Data_Asset { get { return _data_Asset; } set { if (!ReferenceEquals(_data_Asset, value)) { var previousValue = _data_Asset; _data_Asset = value; FixupData_Asset(previousValue); } } } private Data_Asset _data_Asset; public virtual ICollection File_DateTime_Attribute { get { if (_file_DateTime_Attribute == null) { var newCollection = new FixupCollection(); newCollection.CollectionChanged += FixupFile_DateTime_Attribute; _file_DateTime_Attribute = newCollection; } return _file_DateTime_Attribute; } set { if (!ReferenceEquals(_file_DateTime_Attribute, value)) { var previousValue = _file_DateTime_Attribute as FixupCollection; if (previousValue != null) { previousValue.CollectionChanged -= FixupFile_DateTime_Attribute; } _file_DateTime_Attribute = value; var newValue = value as FixupCollection; if (newValue != null) { newValue.CollectionChanged += FixupFile_DateTime_Attribute; } } } } private ICollection _file_DateTime_Attribute; public virtual ICollection File_Int_Attribute { get { if (_file_Int_Attribute == null) { var newCollection = new FixupCollection(); newCollection.CollectionChanged += FixupFile_Int_Attribute; _file_Int_Attribute = newCollection; } return _file_Int_Attribute; } set { if (!ReferenceEquals(_file_Int_Attribute, value)) { var previousValue = _file_Int_Attribute as FixupCollection; if (previousValue != null) { previousValue.CollectionChanged -= FixupFile_Int_Attribute; } _file_Int_Attribute = value; var newValue = value as FixupCollection; if (newValue != null) { newValue.CollectionChanged += FixupFile_Int_Attribute; } } } } private ICollection _file_Int_Attribute; public virtual ICollection File_String_Attribute { get { if (_file_String_Attribute == null) { var newCollection = new FixupCollection(); newCollection.CollectionChanged += FixupFile_String_Attribute; _file_String_Attribute = newCollection; } return _file_String_Attribute; } set { if (!ReferenceEquals(_file_String_Attribute, value)) { var previousValue = _file_String_Attribute as FixupCollection; if (previousValue != null) { previousValue.CollectionChanged -= FixupFile_String_Attribute; } _file_String_Attribute = value; var newValue = value as FixupCollection; if (newValue != null) { newValue.CollectionChanged += FixupFile_String_Attribute; } } } } private ICollection _file_String_Attribute; public virtual STATE STATE { get { return _sTATE; } set { if (!ReferenceEquals(_sTATE, value)) { var previousValue = _sTATE; _sTATE = value; FixupSTATE(previousValue); } } } private STATE _sTATE; #endregion #region Association Fixup private void FixupData_Asset(Data_Asset previousValue) { if (previousValue != null && previousValue.FILES.Contains(this)) { previousValue.FILES.Remove(this); } if (Data_Asset != null) { if (!Data_Asset.FILES.Contains(this)) { Data_Asset.FILES.Add(this); } if (Data_Asset_Id != Data_Asset.Data_Asset_ID) { Data_Asset_Id = Data_Asset.Data_Asset_ID; } } } private void FixupSTATE(STATE previousValue) { if (previousValue != null && previousValue.FILES.Contains(this)) { previousValue.FILES.Remove(this); } if (STATE != null) { if (!STATE.FILES.Contains(this)) { STATE.FILES.Add(this); } if (State_Id != STATE.State_Id) { State_Id = STATE.State_Id; } } } private void FixupFile_DateTime_Attribute(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (File_DateTime_Attribute item in e.NewItems) { item.FILE = this; } } if (e.OldItems != null) { foreach (File_DateTime_Attribute item in e.OldItems) { if (ReferenceEquals(item.FILE, this)) { item.FILE = null; } } } } private void FixupFile_Int_Attribute(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (File_Int_Attribute item in e.NewItems) { item.FILE = this; } } if (e.OldItems != null) { foreach (File_Int_Attribute item in e.OldItems) { if (ReferenceEquals(item.FILE, this)) { item.FILE = null; } } } } private void FixupFile_String_Attribute(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (File_String_Attribute item in e.NewItems) { item.FILE = this; } } if (e.OldItems != null) { foreach (File_String_Attribute item in e.OldItems) { if (ReferenceEquals(item.FILE, this)) { item.FILE = null; } } } } #endregion } } 

这种行为有合理的原因吗? 谢谢。

是的,这种行为有合理的原因。 但是我认为这不是你的错,而是EF 4.0 POCO T4模板的错误。 问题是这些自动生成的Fixup...方法与延迟加载的组合。

发生以下情况:

  • 您设置FK属性:

     pocofile.State_Id = Global.DeletedStateId; 
  • 它调用State_Id setter:

     if (_state_Id != value) { if (STATE != null && STATE.State_Id != value) { STATE = null; } _state_Id = value; } 
  • 第一个if条件为true除非您设置了与已加载实体相同的FK值(不更改FK)。 第二个if条件将为true因为延迟加载将从表达式STATE != null的数据库加载STATE ,因为数据库中的STATE != null不能为null因为您的关系不可为空( State_Idint )。 如果_state_Id不等于value ,则_state_Id不能等于value ,它将违反DB中的FK约束。 因此,执行STATE = null 。 换句话说,导航属性STATE的setter被调用:

     if (!ReferenceEquals(_sTATE, value)) { var previousValue = _sTATE; _sTATE = value; FixupSTATE(previousValue); } 
  • if条件再次为true ,因为_STATE不为null (它刚刚从DB加载)并且valuenull 。 因此,将使用非null的参数previousValue调用FixupSTATE

     if (previousValue != null && previousValue.FILES.Contains(this)) //... 
  • 现在,如果启用延迟加载(并且默认情况下是这样),访问previousValue.FILES集合将导致延迟加载启动并发出数据库查询,从数据库加载整个previousValue.FILES集合 – 它似乎包含60000个实体在你的情况下。

此问题的解决方案是禁用延迟加载:

 var context = GetContext(myTrans, false); context.ContextOptions.LazyLoadingEnabled = false; //... 

或者,修改T4模板,使其不再创建Fixup代码(可能很难正确)。 或者,也许已经有一个修改过的T4模板用于EF 4.0,你可以使用它。 或者,升级到EF> = 4.1和DbContext因为DbContext的POCO模板没有此附加代码。 生成的POCO类更简单。