实体框架一个更新属性时,贯穿所有记录(60,000!) [英] Entity Framework runs through all records (60,000!) when updating property for one
问题描述
我试图设置一个POCO对象的一个属性的值,它看起来像实体正在做一个检索数据库中的表中的每个记录,从该POCO对象最初检索到。这里的code:
I am attempting to set the value of a single property for a POCO object, and it's looking like Entity is doing a retrieve of every record in the database table from which the POCO object was originally retrieved. Here's the code:
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
}
我们有一个文件
表中有一个 STATE_ID
列映射到一个州
表。所以,当我试图将STATE_ID属性设置上面,好像设置的第一个文件确定,但由断点它击中了文件
POCO类来看,它看起来像它的做了检索的数据库中每个文件一旦设置了 STATE_ID
从一开始就。
We have a FILES
table that has a State_Id
column that is mapped to a STATE
table. So when I attempt to set the State_Id property above, it seems to set the first file ok, but judging by the breakpoints it's hitting in the FILE
poco class, it looks like it's doing a retrieve for every file in the db once it sets the State_Id
from the outset.
该FILE.cs类也粘贴如下,以供参考。它基本上是打了很多60,000长循环的getter和setter,如果我对他们中的一个设置断点,在任何给定的时间,并检查FILE_ID在调试器,它的每一次得到了一个新的FILE_ID,这就是我知道它的通过所有这些循环。
The FILE.cs class is also pasted below for reference. It's basically hitting a lot of the getters and setters in a 60,000 long loop, and if I set a breakpoint on one of them at any given time and check the File_Id in the debugger, it's got a new File_Id every time, which is how I know it's looping through all of them.
//------------------------------------------------------------------------------
// <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
}
}
是否有此行为的任何合乎逻辑的理由吗?谢谢你。
Is there any logical reason for this behavior? Thanks.
推荐答案
是的,有这种行为的一个合乎逻辑的理由。但是,这不是你在我的意见,但在EF 4.0 POCO T4模板的错错。问题是,这些自动生成的修正...
方法与延迟加载一起的组合。
Yes, there is a logical reason for this behaviour. But it's not your fault in my opinion but the EF 4.0 POCO T4 template's fault. The problem is the combination of those autogenerated Fixup...
methods together with lazy loading.
会出现以下情况:
-
您设置FK属性:
You set the FK property:
pocofile.State_Id = Global.DeletedStateId;
它调用 STATE_ID
二传手:
It calls the State_Id
setter:
if (_state_Id != value)
{
if (STATE != null && STATE.State_Id != value)
{
STATE = null;
}
_state_Id = value;
}
第一个如果
条件真
,除非你设置相同的 FK
值装入的实体已经有(没有变化FK的)。第二个如果
的条件将真
,因为延迟加载会加载状态
从EX pression数据库状态!= NULL
键,因为状态
数据库不能空
,因为你们的关系是不可为空( STATE_ID
是 INT
)。 STATE.State_Id
不等于值
如果 _state_Id
是不等于值
,这将违反在DB的FK约束。因此, STATE = NULL
执行。换句话说导航setter方法状态
被称为:
The first if
condition is true
unless you set the same FK
value that the loaded entity has already (no change of FK). The second if
condition will be true
because lazy loading will load the STATE
from the database in the expression STATE != null
and because the STATE
in the database cannot be null
since your relationship is not nullable (State_Id
is an int
). STATE.State_Id
cannot be equal to value
if _state_Id
is not equal to value
, it would violate the FK constraint in the DB. So, STATE = null
is executed. In other words the setter of the navigation property STATE
is called:
if (!ReferenceEquals(_sTATE, value))
{
var previousValue = _sTATE;
_sTATE = value;
FixupSTATE(previousValue);
}
在如果
条件真
又因为 _STATE
不是空
(它只是已被加载从DB前)和值
是空
。因此, FixupSTATE
将会被调用,参数 previousValue
不是空
:
The if
condition is true
again because _STATE
is not null
(it just has been loaded from the DB before) and value
is null
. So, FixupSTATE
will be called with a parameter previousValue
that is not null
:
if (previousValue != null && previousValue.FILES.Contains(this))
//...
现在,如果延迟加载启用(这是默认设置)访问 previousValue.FILES
收集将导致延迟加载来踢,发出数据库查询负载整个 previousValue.FILES
从数据库集合 - 这似乎包含60000实体的情况下
Now, if lazy loading is enabled (and it is by default) accessing the previousValue.FILES
collection will cause lazy loading to kick in and issue a database query the loads the whole previousValue.FILES
collection from the database - which seem to contain 60000 entities in your case.
对于这个问题的解决办法是禁用延迟加载:
The solution for this problem is to disable lazy loading:
var context = GetContext(myTrans, false);
context.ContextOptions.LazyLoadingEnabled = false;
//...
或者修改T4模板,以便它不会再创建修正code(可能很难得到它的权利)。或者,也许已经有了关于EF 4.0在那里,你可以使用修改后的T4模板。或者,升级到EF> = 4.1和的DbContext
,因为POCO模板的DbContext
没有这种额外的code。生成的POCO类是非常简单的话。
Or, modify the T4 template so that it doesn't create the Fixup code anymore (might be difficult to get it right). Or, maybe there already is a modified T4 template for EF 4.0 out there you can use. Or, upgrade to EF >= 4.1 and DbContext
because the POCO template for DbContext
doesn't have this additional code. The generated POCO classes are much simpler then.
这篇关于实体框架一个更新属性时,贯穿所有记录(60,000!)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!