我正在尝试为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_Id
列映射到STATE
表。因此,当我尝试将State_Id属性设置为上面时,似乎将第一个文件设置为ok,但是根据它在FILE
poco类中命中的断点来判断它看起来像是在为db中的每个文件执行检索一旦它从一开始就设置了State_Id
。
FILE.cs类也粘贴在下面以供参考。它基本上是在一个60,000长的循环中击中很多getter和setter,如果我在任何给定的时间在其中一个上设置断点并检查调试器中的File_Id,那么每次都有一个新的File_Id,这就是我知道它正在遍历所有这些。
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
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<System.DateTime> 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> File_DateTime_Attribute
{
get
{
if (_file_DateTime_Attribute == null)
{
var newCollection = new FixupCollection<File_DateTime_Attribute>();
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<File_DateTime_Attribute>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupFile_DateTime_Attribute;
}
_file_DateTime_Attribute = value;
var newValue = value as FixupCollection<File_DateTime_Attribute>;
if (newValue != null)
{
newValue.CollectionChanged += FixupFile_DateTime_Attribute;
}
}
}
}
private ICollection<File_DateTime_Attribute> _file_DateTime_Attribute;
public virtual ICollection<File_Int_Attribute> File_Int_Attribute
{
get
{
if (_file_Int_Attribute == null)
{
var newCollection = new FixupCollection<File_Int_Attribute>();
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<File_Int_Attribute>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupFile_Int_Attribute;
}
_file_Int_Attribute = value;
var newValue = value as FixupCollection<File_Int_Attribute>;
if (newValue != null)
{
newValue.CollectionChanged += FixupFile_Int_Attribute;
}
}
}
}
private ICollection<File_Int_Attribute> _file_Int_Attribute;
public virtual ICollection<File_String_Attribute> File_String_Attribute
{
get
{
if (_file_String_Attribute == null)
{
var newCollection = new FixupCollection<File_String_Attribute>();
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<File_String_Attribute>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupFile_String_Attribute;
}
_file_String_Attribute = value;
var newValue = value as FixupCollection<File_String_Attribute>;
if (newValue != null)
{
newValue.CollectionChanged += FixupFile_String_Attribute;
}
}
}
}
private ICollection<File_String_Attribute> _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
}
}
这种行为有合理的原因吗?感谢。
答案 0 :(得分:1)
是的,这种行为有合理的原因。但在我看来,这不是你的错,而是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
中的数据库加载STATE != null
,因为数据库中的STATE
不能null
因为您的关系不可为空(State_Id
是int
)。如果STATE.State_Id
不等于value
,则_state_Id
不能等于value
,它会违反数据库中的FK约束。因此,STATE = null
被执行。换句话说,导航属性STATE
的setter被称为:
if (!ReferenceEquals(_sTATE, value))
{
var previousValue = _sTATE;
_sTATE = value;
FixupSTATE(previousValue);
}
if
条件再次为true
,因为_STATE
不是null
(它刚刚从数据库中加载过)和value
是null
。因此,FixupSTATE
将使用不是previousValue
的参数null
进行调用:
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&gt; = 4.1和DbContext
,因为DbContext
的POCO模板没有此附加代码。生成的POCO类比以前简单得多。