问题描述
这将主要适用于其中数据不通过SOA访问的asp.net应用程序。这意味着你可以访问从框架加载,不传输对象的对象,虽然一些建议仍然适用。
这是一个社区的帖子,所以请在您认为合适添加到它。
适用于:实体框架1.0附带的Visual Studio 2008 SP1
为什么选择EF摆在首位?
考虑到这是一个年轻的技术,很多问题(见下文),它可能是一个强买强卖得到的EF花车为您的项目。然而,这是微软力推(在LINQ2SQL为代价,这是EF的子集)的技术。此外,你可能不会满意与NHibernate或其他解决方案在那里。不管是什么原因,还有人在那里(包括我)与EF工作和生活不bad.make你的想法。
EF和继承
第一大主题是继承。 EF确实对于在2方面坚持继承类支持映射:每个类和表等级表。造型很容易,有与没有部分的编程问题。
(以下适用于每类模型表,因为我没有与每个层级,这一点,表的经验反正有限的。)当您试图运行查询包含一个或多个对象真正的问题来了是继承树的一部分:生成的sql是令人难以置信的可怕,需要一段时间来的EF分析时间长,需要很长的时间来执行为好。这是一个真正的表演塞。以至于EF可能不应该有继承或使用尽可能少的。
下面是它是多么糟糕的例子。我的EF模型具有约30类,〜其中10人继承树的一部分。在运行一个查询来获取从基类一个项目,作为东西Base.Get(ID)一样简单,生成的SQL是超过50000个字符。然后,当你试图返回一些协会,它退化甚至更多,尽量去投掷有关无法立刻查询超过256个表的SQL例外。
好吧,这是不好的,EF理念是为了让您能够在表的实际数据库实现无(或尽可能少)考虑创建对象结构。它彻底失败这个。
所以,建议?避免继承,如果你能,性能会好得多。在这里你必须使用它谨慎。在我看来,这使得EF一个荣耀的SQL生成工具进行查询,但仍有使用它的优势。以及如何落实的机制类似于继承。
的与接口绕过继承的
要知道,试图获得某种继承与EF去的第一件事是,你不能指定一个非EF-模拟类的基类。甚至不要尝试它,它会得到由建模者覆盖。那么怎么办?
您可以使用接口来强制类实现的一些功能。例如这里是一个IEntity界面允许您定义EF实体之间的关联,你不知道在设计时的实体的类型是什么。
公共枚举EntityTypes {未知= -1,狗= 0,猫}
公共接口IEntity
{
INT ENTITYID {搞定; }
字符串名称{; }
键入的EntityType {搞定; }
}
公共部分类犬:IEntity
{
//实施ENTITYID和名称可能实际上是场
//从EF模型
键入的EntityType {{返回EntityTypes.Dog; }}
}
使用这个IEntity,您就可以在其他类不确定关联工作
//让我们您在模型中定义的类。
//这个类有一个映射到列:PetID,PetType
公共部分类人
{
公共IEntity GetPet()
{
返回IEntityController.Get(PetID,PetType);
}
}
这使得使用一些扩展函数:
公共类IEntityController
{
静态公共IEntity获取(INT ID,EntityTypes型)
{
开关(类型)
{
案例EntityTypes.Dog:返回Dog.Get(ID);
案例EntityTypes.Cat:返回Cat.Get(ID);
默认:抛出新的异常(无效的EntityType);
}
}
}
具有普通继承,特别是考虑到你要的PetType存储在一个额外的数据库字段,但考虑到性能提升不整洁,我就不会回头。
也不能模拟一到多,多到多的关系,但与'联盟'创造性地利用它可以进行工作。最后,它会在对象的属性/函数加载数据,您需要小心的侧面effet。使用类似GetXYZ()明确的命名约定有助于在问候。
编译查询
实体框架的表现并不像ADO(显然)或LINQ2SQL直接访问数据库一样好。有办法但对其进行改进,其中之一是编译查询。一个编译的查询的性能类似于LINQ2SQL
什么是编译的查询?它只是为你告诉框架保持解析的树在内存中,因此不需要你运行它下一次重新生成一个查询。所以,下次来看,你会节省花费解析树的时间。不打折的,因为它是一个非常昂贵的操作,会变得更加复杂的查询变得更糟。
有2种方式来编译查询:创建具有EntitySQL一个的ObjectQuery和使用CompiledQuery.Compile()函数。 (请注意,在页面中使用EntityDataSource,你其实会使用的ObjectQuery与EntitySQL,使被编译和缓存)。
这是一边在这里如果你不知道EntitySQL是什么。这是编写查询对EF的基于字符串的方式。这里有一个例子:从Entities.DogSet选择价值狗随狗,其中dog.ID = @ID。语法类似于SQL的语法pretty。你也可以做pretty复杂对象的操作,这是很好的解释[这里] [1]。
好了,所以这里是如何使用的ObjectQuery&LT做到这一点;>
查询字符串=选择值狗+
从Entities.DogSet为狗+
其中dog.ID = @ID; 的ObjectQuery<狗和GT; oQuery =新的ObjectQuery<狗和GT(查询,EntityContext.Instance));
oQuery.Parameters.Add(新ObjectParameter(ID,ID));
oQuery.EnablePlanCaching = TRUE;
返回oQuery.FirstOrDefault();
在运行此查询的第一次,该框架将产生前pression树,并保持它在内存中。因此,它被执行接下来的时间,你会节省代价高昂的一步。在这个例子中EnablePlanCaching = true,这是不必要的,因为这是默认选项。
其它方式编译供以后使用的查询是CompiledQuery.Compile方法。它使用一个委托:
静态只读Func键<实体,INT,狗> query_GetDog =
CompiledQuery.Compile<实体,INT,狗>((CTX,ID)=>
ctx.DogSet.FirstOrDefault(IT = GT; it.ID == ID));
或使用LINQ
静态只读Func键<实体,INT,狗> query_GetDog =
CompiledQuery.Compile<实体,INT,狗>((CTX,ID)=>
(从狗那里ctx.DogSet == dog.ID ID选择狗).FirstOrDefault());
致电查询:
query_GetDog.Invoke(YourContext,身份证);
CompiledQuery的好处是,你的查询语法在编译的时候,那里的EntitySQL不检查。但是,也有其他的考虑...
的包括的
比方说,你想拥有的数据的狗主人要由查询返回的避免使2调用数据库。容易做到,不是吗?
EntitySQL
查询字符串=选择值狗+
从Entities.DogSet为狗+
其中dog.ID = @ID;
的ObjectQuery<狗和GT; oQuery =新的ObjectQuery<犬方式>(查询,EntityContext.Instance))包括(所有者);
oQuery.Parameters.Add(新ObjectParameter(ID,ID));
oQuery.EnablePlanCaching = TRUE;
返回oQuery.FirstOrDefault();
CompiledQuery
静态只读Func键<实体,INT,狗> query_GetDog =
CompiledQuery.Compile<实体,INT,狗>((CTX,ID)=>
(从狗ctx.DogSet.Include(所有者),其中dog.ID == ID选择狗).FirstOrDefault());
现在,如果你想要有包括参数化?我的意思是,你想有一个是从关心狗不同的关系,不同的页面称为单的get()函数。人在乎的所有者,另一个关于他的FavoriteFood,另一个关于他的FavotireToy等。基本上,你要告诉查询协会要加载的。
这是很容易与EntitySQL做
公共犬获取(INT ID,字符串包括)
{
查询字符串=选择值狗+
从Entities.DogSet为狗+
其中dog.ID = @ID; 的ObjectQuery<狗和GT; oQuery =新的ObjectQuery<狗和GT(查询,EntityContext.Instance))
.IncludeMany(含);
oQuery.Parameters.Add(新ObjectParameter(ID,ID));
oQuery.EnablePlanCaching = TRUE;
返回oQuery.FirstOrDefault();
}
该包括仅仅使用所传递的字符串。很容易的。注意,有可能提高对包含(串)函数(仅接受单一路径)与IncludeMany(字符串),这将让你通过逗号分隔的关联的字符串加载。在此功能的扩展部分看得更远。
如果我们试图然而,随着CompiledQuery做到这一点,我们遇到了许多问题:
显而易见
静态只读Func键<实体,整型,字符串,狗> query_GetDog =
CompiledQuery.Compile<实体,整型,字符串,狗>((CTX,身份证,包含)=>
(从狗ctx.DogSet.Include(含),其中dog.ID == ID选择狗).FirstOrDefault());
将窒息时名为:
query_GetDog.Invoke(YourContext,ID,老板,FavoriteFood);
由于,如上mentionned,包括()只希望看到字符串中的单一路径,在这里我们给它2:所有者和FavoriteFood(这是不是与Owner.FavoriteFood混淆!)。
然后,让我们使用IncludeMany(),这是一个扩展功能
静态只读Func键<实体,整型,字符串,狗> query_GetDog =
CompiledQuery.Compile<实体,整型,字符串,狗>((CTX,身份证,包含)=>
(从狗ctx.DogSet.IncludeMany(含),其中dog.ID == ID选择狗).FirstOrDefault());
错,这一次是因为EF无法解析IncludeMany,因为它不是的,它是功能部分识别:它是一个扩展
。好了,你要的路径任意数量传递给你的函数,包括()只需要一个。该怎么办?你可以决定你将永远不会需要超过,说20包括,并通过各分隔的字符串的结构来CompiledQuery。但现在的查询看起来是这样的:
从狗ctx.DogSet.Include(include1).INCLUDE(措施包括1).INCLUDE(include3)
.INCLUDE(include4).INCLUDE(include5).INCLUDE(include6)
[...]。包括(include19).INCLUDE(include20)其中dog.ID == ID选择狗
这是可怕的为好。好吧,那么,而是等待一分钟。我们不能返回的ObjectQuery<>与CompiledQuery?然后设置包括上?那么,什么我还以为左右为好:
静态只读Func键<实体,INT的ObjectQuery<狗和GT;> query_GetDog =
CompiledQuery.Compile<实体,INT,字符串的ObjectQuery<狗和GT;>((CTX,ID)=>
(&的ObjectQuery LT,犬>)(从狗那里ctx.DogSet == dog.ID ID选择狗));
公众狗GetDog(INT ID,字符串包括)
{
的ObjectQuery<狗和GT; oQuery = query_GetDog(ID);
oQuery = oQuery.IncludeMany(含);
返回oQuery.FirstOrDefault;
}
这应该有工作,只是当你调用IncludeMany(或包括在哪里,排序依据...),因为它是一个完全新的,现在你无效缓存编译的查询!因此,前pression树需要重新分析,你会得到的表现再次袭来。
那么该如何解决?你与参数化包括根本无法使用CompiledQueries。使用EntitySQL代替。这并不意味着,有没有对CompiledQueries用途。这是伟大的本地化查询,将始终在同一范围内调用。理想情况下CompiledQuery应始终使用,因为这个语法是在编译期进行检查,但由于限制,这是不可能的。
使用的一个例子是:你可能需要有一个网页,其中两只狗具有相同的最喜爱的食物,这是一个有点窄了BusinessLayer功能,让你把它放在你的页面,到底是什么类型的知道查询包括是必需的。
的传递超过3个参数为CompiledQuery 的
函数功能被限制为5个参数,其中最后一个是返回类型和第一个是你的实体从模型对象。让你留下3个参数。一个pitance,但它可以在很容易提高。
公共结构MyParams
{
公共字符串参数1;
公众诠释参数2;
公众的DateTime参数3;
} 静态只读Func键<实体,MyParams,IEnumerable的<狗和GT;> query_GetDog =
CompiledQuery.Compile<实体,MyParams,IEnumerable的<狗和GT;>((CTX,myParams)=>
从狗那里ctx.DogSet == dog.Age&myParams.param2功放;&安培; dog.Name == myParams.param1和dog.BirthDate> myParams.param3选择狗);公开名单<狗和GT; GetSomeDogs(INT年龄,字符串名称,日期生日)
{
MyParams myParams =新MyParams();
myParams.param1 =名称;
myParams.param2 =年龄;
myParams.param3 =生日;
返回query_GetDog(YourContext,myParams).ToList();
}
返回类型(这不适用于EntitySQL查询,因为它们不执行作为CompiledQuery方法期间同时编译)的
使用LINQ工作,你平时不强制查询的执行,直到最后一刻,以防其他一些功能下游要改变以某种方式查询:
静态只读Func键<实体,INT,字符串的IEnumerable<狗和GT;> query_GetDog =
CompiledQuery.Compile<实体,INT,字符串的IEnumerable<狗和GT;>((CTX,年龄,姓名)=>
从狗那里ctx.DogSet == dog.Age年龄和放大器;&安培; dog.Name ==名称选择狗);公共IEnumerable的<狗和GT; GetSomeDogs(INT年龄,字符串名称)
{
返回query_GetDog(YourContext,年龄,姓名);
}
公共无效DataBindStuff()
{
IEnumerable的<狗和GT;狗= GetSomeDogs(4,巴德);
//但是我想通过出生日期排序狗
gridView.DataSource = dogs.OrderBy(IT = GT; it.BirthDate);}
这是怎么发生在这里?通过与原来的ObjectQuery仍在播放(即LINQ的语句,它实现了IEnumerable的实际返回类型),它将失效编译的查询,并力重新解析。因此,经验法则是返回一个List<对象>来代替。
静态只读Func键<实体,INT,字符串的IEnumerable<狗和GT;> query_GetDog =
CompiledQuery.Compile<实体,INT,字符串的IEnumerable<狗和GT;>((CTX,年龄,姓名)=>
从狗那里ctx.DogSet == dog.Age年龄和放大器;&安培; dog.Name ==名称选择狗);公开名单<狗和GT; GetSomeDogs(INT年龄,字符串名称)
{
返回query_GetDog(YourContext,年龄,姓名).ToList(); //< ==这里的变化
}
公共无效DataBindStuff()
{
清单<狗和GT;狗= GetSomeDogs(4,巴德);
//但是我想通过出生日期排序狗
gridView.DataSource = dogs.OrderBy(IT = GT; it.BirthDate);}
当调用了ToList(),该查询被作为每个已编译的查询执行,然后,后,排序依据是针对在存储器中的对象执行。它可能是一个稍微慢一点,但我甚至不能确定。一个肯定的事情是,你有没有对错误处理的ObjectQuery和无效的编译的查询计划的担忧。
再一次,这不是一个毯子声明。了ToList()是一个防御性编程的技巧,但是如果你有一个有效的理由不使用了ToList(),继续前进。有很多情况下,你会想在执行前细化查询。
的性能的
什么是编译查询的性能影响?它其实是可以相当大。一条经验法则是,编译和缓存重用查询至少需要简单地执行它没有缓存的两倍时间。对于复杂的查询(读取inherirante),我已经向上看到10秒。
所以,第一次pre-编译的查询被调用,你会得到一个性能命中。首先命中后,性能明显高于同非pre-编译的查询更好。几乎相同LINQ2SQL
在加载页面pre-编译查询第一次,你会得到一击。它将也许5-15秒(显然不止一个pre-编译查询最终会被调用)加载,而随后的负载只需要不到300毫秒。显着的差异,这是由你来决定,如果它是确定你的第一个用户采取一击,或者您想要一个脚本来调用你的网页来强制查询的汇编。
的可这个查询被缓存?的
{
狗狗=从狗那里YourContext.DogSet == dog.ID ID选择狗;
}
没有,即席LINQ查询不缓存,你会招致生成树你怎么称呼它每一次的成本。
的参数化查询的
大多数搜索功能涉及到大量参数化查询。有可用的库甚至可以让你建立一个参数化查询出兰巴前pressions的。问题是,你不能使用那些pre-编译查询。周围的一个方法是绘制出在查询和标志要使用哪一个所有可能的标准:
公共结构MyParams
{
公共字符串名称;
公共BOOL检查名;
公众诠释年龄;
公共BOOL checkAge;
} 静态只读Func键<实体,MyParams,IEnumerable的<狗和GT;> query_GetDog =
CompiledQuery.Compile<实体,MyParams,IEnumerable的<狗和GT;>((CTX,myParams)=>
从狗ctx.DogSet
其中(myParams.checkAge ==真&放大器;&安培; dog.Age == myParams.age)
&功放;&安培; (myParams.checkName ==真&放大器;&安培; dog.Name == myParams.name)
选择狗);保护名单,LT,犬> GetSomeDogs()
{
MyParams myParams =新MyParams();
myParams.name =芽;
myParams.checkName =真;
myParams.age = 0;
myParams.checkAge = FALSE;
返回query_GetDog(YourContext,myParams).ToList();
}
这里的好处是,你得到一个pre-编译quert的所有优惠。缺点是你最有可能将结束与where子句是pretty难以维持,你将承担为pre-编译查询,并且每次运行查询效率不高,更大的惩罚因为它可能是(尤其是联接抛出)。
另一种方法是一块建立一个EntitySQL查询一块,就像我们所有的SQL一样。
保护名单,LT;多德> GetSomeDogs(字符串名称,诠释岁)
{
查询字符串=从Entities.DogSet选择值的狗,其中1 = 1;
如果(!String.IsNullOrEmpty(名))
查询=查询+和dog.Name == @Name
如果(年龄大于0)
查询=查询+和dog.Age == @Age 的ObjectQuery<狗和GT; oQuery =新的ObjectQuery<狗和GT(查询,YourContext);
如果(!String.IsNullOrEmpty(名))
oQuery.Parameters.Add(新ObjectParameter(名,名));
如果(年龄大于0)
oQuery.Parameters.Add(新ObjectParameter(时代,年龄));返回oQuery.ToList();
}
下面的问题是:
- 有没有语法编译期间检查
- 参数的每个不同的组合产生不同的查询这将需要pre-编译的时候才第一次运行。在这种情况下,只有4个不同的可能的查询(无参数,可以与年龄只,名称只和未参数),但你可以看到,可以有更多的方式与正常世界的搜索。
- !没有人喜欢来连接字符串。
另一种选择是查询数据的一个大的子集,然后缩小它在存储器中。如果与数据的一个确定的子集的工作,如在一个城市的所有的狗,这是特别有用的。你知道有很多,但你也知道有没有那么多......所以你CityDog搜索页面可以加载所有的狗为城市在内存中,这是一个单一pre-编译查询,然后细化结果
保护名单,LT;多德> GetSomeDogs(字符串名称,诠释年龄,串市)
{
查询字符串=从Entities.DogSet那里dog.Owner.Address.City == @city选择值狗;
的ObjectQuery<狗和GT; oQuery =新的ObjectQuery<狗和GT(查询,YourContext);
oQuery.Parameters.Add(新ObjectParameter(城市,市));清单<狗和GT;狗= oQuery.ToList();如果(!String.IsNullOrEmpty(名))
狗= dogs.Where(IT = GT; it.Name ==名);
如果(年龄大于0)
狗= dogs.Where(IT = GT; it.Age ==岁);返回的狗;
}
当你开始显示,那么所有的数据允许过滤这是非常有用的。
问题:
- 如果你不小心你的子集,可能会导致严重的数据传输。
- 你只能在你返回的数据进行过滤。这意味着,如果你不返回Dog.Owner关联,您将无法在Dog.Owner.Name过滤
那么,什么是最好的解决办法?没有任何。你需要选择最适合你和你的问题的解决方案:
- 使用氯氟基于查询的建筑,当你不关心pre-编译查询。
- 当你的对象结构不太复杂的使用完全定义pre-编译Linq查询。
- 使用EntitySQL /字符串连接当该结构可能是复杂的并且当不同所得的查询的可能的数目是小的(这意味着更少$ P $对汇编命中)。
- 当你的数据的一个短小的子集工作或使用内存筛选时,你不得不反正获取所有的在第一数据中的数据(如果性能优良的所有数据,然后在内存中过滤,并且不会安排在分贝花费任何时间)。
辛格尔顿访问
处理您的上下文和实体翻过所有网页的最佳方式是使用Singleton模式:
公共密封类YourContext
{
私人常量字符串instanceKey =On3GoModelKey; YourContext(){} 公共静态YourEntities实例
{
得到
{
HttpContext的背景下= HttpContext.Current;
如果(上下文== NULL)
返回Nested.instance; 如果(context.Items [instanceKey] == NULL)
{
On3GoEntities实体=新On3GoEntities();
context.Items [instanceKey] =实体;
}
回报(YourEntities)context.Items [instanceKey]
}
} 嵌套类
{
//显式静态构造函数来告诉C#编译器
//没有标记类型beforefieldinit
静态嵌套()
{
} 内部静态只读YourEntities实例=新YourEntities();
}
}
NoTracking,是否值得呢?
当执行一个查询,你可以告诉框架跟踪它会返回与否的对象。这是什么意思?与跟踪启用(默认选项),该框架将跟踪正在发生的事情与对象(已经是被修改?创建?删除?),也将连接物体放在一起,当进一步查询从数据库中,这是什么做是这里的利益。
例如,让我们假设有ID == 2这狗已经拥有者的ID == 10。
狗狗=(从狗那里YourContext.DogSet == dog.ID 2选择狗).FirstOrDefault();
//dog.OwnerReference.IsLoaded ==虚假;
人所有者=(邻在YourContext.PersonSet那里o.ID == 10选狗).FirstOrDefault();
//dog.OwnerReference.IsLoaded ==真;
如果我们做同样的,没有跟踪,其结果必然是不同的。
的ObjectQuery<狗和GT; oDogQuery =(的ObjectQuery<狗和GT;)
(从狗那里YourContext.DogSet == dog.ID 2选择狗);
oDogQuery.MergeOption = MergeOption.NoTracking;
狗狗= oDogQuery.FirstOrDefault();
//dog.OwnerReference.IsLoaded ==虚假;
的ObjectQuery<&人GT; oPersonQuery =(的ObjectQuery<&人GT;)
(邻在YourContext.PersonSet那里o.ID == 10选O);
oPersonQuery.MergeOption = MergeOption.NoTracking;
所有者所有者= oPersonQuery.FirstOrDefault();
//dog.OwnerReference.IsLoaded ==虚假;
跟踪是非常有用的,在没有性能问题一个完美的世界,这将永远是。但是,在这个世界上,有一个代价,在性能方面。所以,你应该用NoTracking加快速度?这取决于你打算使用的数据是什么。
是否有数据的带NoTracking查询可以被用来制造更新/插入/删除数据库中任何机会?如果是这样,因为协会是不被跟踪,并会导致引发异常不使用NoTracking。
在那里有是绝对的数据库没有更新页面,则可以使用NoTracking。
混合跟踪和NoTracking是可能的,但需要你格外小心更新/插入/删除。问题是,如果混合则有风险的框架试图连接()一个NoTracking对象,其中有跟踪上存在同一个对象的另一个副本的背景。基本上,我的意思是,
狗DOG1 =(从狗那里YourContext.DogSet == dog.ID 2).FirstOrDefault();的ObjectQuery<狗和GT; oDogQuery =(的ObjectQuery<狗和GT;)
(从狗那里YourContext.DogSet == dog.ID 2选择狗);
oDogQuery.MergeOption = MergeOption.NoTracking;
狗DOG2 = oDogQuery.FirstOrDefault();
DOG1和DOG2是2个不同的对象,一个跟踪和一个没有。在更新/插入使用分离对象将强制安装(),将说:等一下,我已经在这里有一个对象使用相同的数据库键失败。而当你连接()一个对象,它的所有层次得到连接,以及,造成的问题比比皆是。要格外小心。
的如何更快的是它NoTracking 的
这取决于查询。有些人更succeptible比其他跟踪。我没有为它快速的简单的规则,但它帮助。
的所以,我应该用NoTracking无处不在呢?的
不完全是。有一些优势的跟踪对象。第一个是对象被缓存,因此后续调用该对象将不能访问数据库。该缓存仅适用于该YourEntities对象,如果你使用Singleton code以上,是一样的页面生命周期的寿命。一个页面请求== 1 YourEntity对象。因此,对于多个呼叫为同一个对象,它会每页请求只加载一次。 (其他的缓存机制可以延长这一点)。
当你使用NoTracking,并尝试对同一个对象多次加载会发生什么?该数据库将每一次查询,所以有产生影响。你/你应该在一个页面请求期间呼吁多久相同的对象?尽可能少当然,但它确实会发生。
还记得这块上面有左右为您自动连接的关联?你不必与NoTracking,因此,如果您在多个批次的数据,你会不会有一个链接到他们之间的:
的ObjectQuery<狗和GT; oDogQuery =(的ObjectQuery<狗和GT;)(从狗YourContext.DogSet选择狗);
oDogQuery.MergeOption = MergeOption.NoTracking;
清单<狗和GT;狗= oDogQuery.ToList();的ObjectQuery<&人GT; oPersonQuery =(的ObjectQuery<&人GT;)(从o在YourContext.PersonSet选择o);
oPersonQuery.MergeOption = MergeOption.NoTracking;
清单<&人GT;所有者= oPersonQuery.ToList();
在此情况下,没有狗都会有.Owner属性设置。
有些事情要记住,当你试图优化性能。
无延迟加载,我该怎么办?
这可以被看作是因祸得福。当然,这是恼人的手动加载的一切。但是,它降低你想想,当你要加载数据调用db和部队人数。更可以在一个数据库中装载调用越好。这是总是正确的,但它与该EF的'功能'现在执行。
当然,你可以调用
如果ObjectReference.Load()(ObjectReference.IsLoaded!);
如果你想,但一个更好的做法是迫使框架加载的对象,你知道你需要在一杆。这是关于包括参数化的讨论开始道理。
可以说,你有你的狗对象
公共类犬
{
公众狗获取(INT ID)
{
返回YourContext.DogSet.FirstOrDefault(IT = GT; it.ID == ID);
}
}
这是你所有的工作时间函数的类型。它得到来自全国各地的地方叫,一旦你有一个Dog对象,你会在不同的功能做的非常不同的东西出来。首先,它应该是pre-编译,因为你会经常调用。第二,每个不同的网页将要访问的狗数据的不同子集。有些人会想业主,一些FavoriteToy等。
当然,你可以呼叫负载()为您随时需要你需要各一个参考。但是,这将产生每次到数据库的呼叫。馊主意。所以取而代之,每个页面都会要求它希望看到的数据时,狗对象第一个请求:
静态公共犬获取(INT ID){返回GetDog(实体,);}
静态公共犬获取(INT ID,字符串INCLUDEPATH)
{
查询字符串=选择值O+
从YourEntities.DogSet为○+
请不要使用上述所有信息,如单身访问。你绝对100%不应该被存储,因为它不是线程安全的这种背景下被重新使用。
This will apply mostly for an asp.net application where the data is not accessed via soa. Meaning that you get access to the objects loaded from the framework, not Transfer Objects, although some recommendation still apply.
This is a community post, so please add to it as you see fit.
Applies to: Entity Framework 1.0 shipped with Visual Studio 2008 sp1.
Why pick EF in the first place?
Considering it is a young technology with plenty of problems (see below), it may be a hard sell to get on the EF bandwagon for your project. However, it is the technology Microsoft is pushing (at the expense of Linq2Sql, which is a subset of EF). In addition, you may not be satisfied with NHibernate or other solutions out there. Whatever the reasons, there are people out there (including me) working with EF and life is not bad.make you think.
EF and inheritance
The first big subject is inheritance. EF does support mapping for inherited classes that are persisted in 2 ways: table per class and table the hierarchy. The modeling is easy and there are no programming issues with that part.
(The following applies to table per class model as I don't have experience with table per hierarchy, which is, anyway, limited.) The real problem comes when you are trying to run queries that include one or many objects that are part of an inheritance tree: the generated sql is incredibly awful, takes a long time to get parsed by the EF and takes a long time to execute as well. This is a real show stopper. Enough that EF should probably not be used with inheritance or as little as possible.
Here is an example of how bad it was. My EF model had ~30 classes, ~10 of which were part of an inheritance tree. On running a query to get one item from the Base class, something as simple as Base.Get(id), the generated SQL was over 50,000 characters. Then when you are trying to return some Associations, it degenerates even more, going as far as throwing SQL exceptions about not being able to query more than 256 tables at once.
Ok, this is bad, EF concept is to allow you to create your object structure without (or with as little as possible) consideration on the actual database implementation of your table. It completely fails at this.
So, recommendations? Avoid inheritance if you can, the performance will be so much better. Use it sparingly where you have to. In my opinion, this makes EF a glorified sql-generation tool for querying, but there are still advantages to using it. And ways to implement mechanism that are similar to inheritance.
Bypassing inheritance with Interfaces
First thing to know with trying to get some kind of inheritance going with EF is that you cannot assign a non-EF-modeled class a base class. Don't even try it, it will get overwritten by the modeler. So what to do?
You can use interfaces to enforce that classes implement some functionality. For example here is a IEntity interface that allow you to define Associations between EF entities where you don't know at design time what the type of the entity would be.
public enum EntityTypes{ Unknown = -1, Dog = 0, Cat }
public interface IEntity
{
int EntityID { get; }
string Name { get; }
Type EntityType { get; }
}
public partial class Dog : IEntity
{
// implement EntityID and Name which could actually be fields
// from your EF model
Type EntityType{ get{ return EntityTypes.Dog; } }
}
Using this IEntity, you can then work with undefined associations in other classes
// lets take a class that you defined in your model.
// that class has a mapping to the columns: PetID, PetType
public partial class Person
{
public IEntity GetPet()
{
return IEntityController.Get(PetID,PetType);
}
}
which makes use of some extension functions:
public class IEntityController
{
static public IEntity Get(int id, EntityTypes type)
{
switch (type)
{
case EntityTypes.Dog: return Dog.Get(id);
case EntityTypes.Cat: return Cat.Get(id);
default: throw new Exception("Invalid EntityType");
}
}
}
Not as neat as having plain inheritance, particularly considering you have to store the PetType in an extra database field, but considering the performance gains, I would not look back.
It also cannot model one-to-many, many-to-many relationship, but with creative uses of 'Union' it could be made to work. Finally, it creates the side effet of loading data in a property/function of the object, which you need to be careful about. Using a clear naming convention like GetXYZ() helps in that regards.
Compiled Queries
Entity Framework performance is not as good as direct database access with ADO (obviously) or Linq2SQL. There are ways to improve it however, one of which is compiling your queries. The performance of a compiled query is similar to Linq2Sql.
What is a compiled query? It is simply a query for which you tell the framework to keep the parsed tree in memory so it doesn't need to be regenerated the next time you run it. So the next run, you will save the time it takes to parse the tree. Do not discount that as it is a very costly operation that gets even worse with more complex queries.
There are 2 ways to compile a query: creating an ObjectQuery with EntitySQL and using CompiledQuery.Compile() function. (Note that by using an EntityDataSource in your page, you will in fact be using ObjectQuery with EntitySQL, so that gets compiled and cached).
An aside here in case you don't know what EntitySQL is. It is a string-based way of writing queries against the EF. Here is an example: "select value dog from Entities.DogSet as dog where dog.ID = @ID". The syntax is pretty similar to SQL syntax. You can also do pretty complex object manipulation, which is well explained [here][1].
Ok, so here is how to do it using ObjectQuery<>
string query = "select value dog " +
"from Entities.DogSet as dog " +
"where dog.ID = @ID";
ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance));
oQuery.Parameters.Add(new ObjectParameter("ID", id));
oQuery.EnablePlanCaching = true;
return oQuery.FirstOrDefault();
The first time you run this query, the framework will generate the expression tree and keep it in memory. So the next time it gets executed, you will save on that costly step. In that example EnablePlanCaching = true, which is unnecessary since that is the default option.
The other way to compile a query for later use is the CompiledQuery.Compile method. This uses a delegate:
static readonly Func<Entities, int, Dog> query_GetDog =
CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
ctx.DogSet.FirstOrDefault(it => it.ID == id));
or using linq
static readonly Func<Entities, int, Dog> query_GetDog =
CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
(from dog in ctx.DogSet where dog.ID == id select dog).FirstOrDefault());
to call the query:
query_GetDog.Invoke( YourContext, id );
The advantage of CompiledQuery is that the syntax of your query is checked at compile time, where as EntitySQL is not. However, there are other consideration...
Includes
Lets say you want to have the data for the dog owner to be returned by the query to avoid making 2 calls to the database. Easy to do, right?
EntitySQL
string query = "select value dog " +
"from Entities.DogSet as dog " +
"where dog.ID = @ID";
ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance)).Include("Owner");
oQuery.Parameters.Add(new ObjectParameter("ID", id));
oQuery.EnablePlanCaching = true;
return oQuery.FirstOrDefault();
CompiledQuery
static readonly Func<Entities, int, Dog> query_GetDog =
CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
(from dog in ctx.DogSet.Include("Owner") where dog.ID == id select dog).FirstOrDefault());
Now, what if you want to have the Include parametrized? What I mean is that you want to have a single Get() function that is called from different pages that care about different relationships for the dog. One cares about the Owner, another about his FavoriteFood, another about his FavotireToy and so on. Basicly, you want to tell the query which associations to load.
It is easy to do with EntitySQL
public Dog Get(int id, string include)
{
string query = "select value dog " +
"from Entities.DogSet as dog " +
"where dog.ID = @ID";
ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance))
.IncludeMany(include);
oQuery.Parameters.Add(new ObjectParameter("ID", id));
oQuery.EnablePlanCaching = true;
return oQuery.FirstOrDefault();
}
The include simply uses the passed string. Easy enough. Note that it is possible to improve on the Include(string) function (that accepts only a single path) with an IncludeMany(string) that will let you pass a string of comma-separated associations to load. Look further in the extension section for this function.
If we try to do it with CompiledQuery however, we run into numerous problems:
The obvious
static readonly Func<Entities, int, string, Dog> query_GetDog =
CompiledQuery.Compile<Entities, int, string, Dog>((ctx, id, include) =>
(from dog in ctx.DogSet.Include(include) where dog.ID == id select dog).FirstOrDefault());
will choke when called with:
query_GetDog.Invoke( YourContext, id, "Owner,FavoriteFood" );
Because, as mentionned above, Include() only wants to see a single path in the string and here we are giving it 2: "Owner" and "FavoriteFood" (which is not to be confused with "Owner.FavoriteFood"!).
Then, let's use IncludeMany(), which is an extension function
static readonly Func<Entities, int, string, Dog> query_GetDog =
CompiledQuery.Compile<Entities, int, string, Dog>((ctx, id, include) =>
(from dog in ctx.DogSet.IncludeMany(include) where dog.ID == id select dog).FirstOrDefault());
Wrong again, this time it is because the EF cannot parse IncludeMany because it is not part of the functions that is recognizes: it is an extension.
Ok, so you want to pass an arbitrary number of paths to your function and Includes() only takes a single one. What to do? You could decide that you will never ever need more than, say 20 Includes, and pass each separated strings in a struct to CompiledQuery. But now the query looks like this:
from dog in ctx.DogSet.Include(include1).Include(include2).Include(include3)
.Include(include4).Include(include5).Include(include6)
.[...].Include(include19).Include(include20) where dog.ID == id select dog
which is awful as well. Ok, then, but wait a minute. Can't we return an ObjectQuery<> with CompiledQuery? Then set the includes on that? Well, that what I would have thought so as well:
static readonly Func<Entities, int, ObjectQuery<Dog>> query_GetDog =
CompiledQuery.Compile<Entities, int, string, ObjectQuery<Dog>>((ctx, id) =>
(ObjectQuery<Dog>)(from dog in ctx.DogSet where dog.ID == id select dog));
public Dog GetDog( int id, string include )
{
ObjectQuery<Dog> oQuery = query_GetDog(id);
oQuery = oQuery.IncludeMany(include);
return oQuery.FirstOrDefault;
}
That should have worked, except that when you call IncludeMany (or Include, Where, OrderBy...) you invalidate the cached compiled query because it is an entirely new one now! So, the expression tree needs to be reparsed and you get that performance hit again.
So what is the solution? You simply cannot use CompiledQueries with parametrized Includes. Use EntitySQL instead. This doesn't mean that there aren't uses for CompiledQueries. It is great for localized queries that will always be called in the same context. Ideally CompiledQuery should always be used because the syntax is checked at compile time, but due to limitation, that's not possible.
An example of use would be: you may want to have a page that queries which two dogs have the same favorite food, which is a bit narrow for a BusinessLayer function, so you put it in your page and know exactly what type of includes are required.
Passing more than 3 parameters to a CompiledQuery
Func is limited to 5 parameters, of which the last one is the return type and the first one is your Entities object from the model. So that leaves you with 3 parameters. A pitance, but it can be improved on very easily.
public struct MyParams
{
public string param1;
public int param2;
public DateTime param3;
}
static readonly Func<Entities, MyParams, IEnumerable<Dog>> query_GetDog =
CompiledQuery.Compile<Entities, MyParams, IEnumerable<Dog>>((ctx, myParams) =>
from dog in ctx.DogSet where dog.Age == myParams.param2 && dog.Name == myParams.param1 and dog.BirthDate > myParams.param3 select dog);
public List<Dog> GetSomeDogs( int age, string Name, DateTime birthDate )
{
MyParams myParams = new MyParams();
myParams.param1 = name;
myParams.param2 = age;
myParams.param3 = birthDate;
return query_GetDog(YourContext,myParams).ToList();
}
Return Types (this does not apply to EntitySQL queries as they aren't compiled at the same time during execution as the CompiledQuery method)
Working with Linq, you usually don't force the execution of the query until the very last moment, in case some other functions downstream wants to change the query in some way:
static readonly Func<Entities, int, string, IEnumerable<Dog>> query_GetDog =
CompiledQuery.Compile<Entities, int, string, IEnumerable<Dog>>((ctx, age, name) =>
from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog);
public IEnumerable<Dog> GetSomeDogs( int age, string name )
{
return query_GetDog(YourContext,age,name);
}
public void DataBindStuff()
{
IEnumerable<Dog> dogs = GetSomeDogs(4,"Bud");
// but I want the dogs ordered by BirthDate
gridView.DataSource = dogs.OrderBy( it => it.BirthDate );
}
What is going to happen here? By still playing with the original ObjectQuery (that is the actual return type of the Linq statement, which implements IEnumerable), it will invalidate the compiled query and be force to re-parse. So, the rule of thumb is to return a List<> of objects instead.
static readonly Func<Entities, int, string, IEnumerable<Dog>> query_GetDog =
CompiledQuery.Compile<Entities, int, string, IEnumerable<Dog>>((ctx, age, name) =>
from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog);
public List<Dog> GetSomeDogs( int age, string name )
{
return query_GetDog(YourContext,age,name).ToList(); //<== change here
}
public void DataBindStuff()
{
List<Dog> dogs = GetSomeDogs(4,"Bud");
// but I want the dogs ordered by BirthDate
gridView.DataSource = dogs.OrderBy( it => it.BirthDate );
}
When you call ToList(), the query gets executed as per the compiled query and then, later, the OrderBy is executed against the objects in memory. It may be a little bit slower, but I'm not even sure. One sure thing is that you have no worries about mis-handling the ObjectQuery and invalidating the compiled query plan.
Once again, that is not a blanket statement. ToList() is a defensive programming trick, but if you have a valid reason not to use ToList(), go ahead. There are many cases in which you would want to refine the query before executing it.
Performance
What is the performance impact of compiling a query? It can actually be fairly large. A rule of thumb is that compiling and caching the query for reuse takes at least double the time of simply executing it without caching. For complex queries (read inherirante), I have seen upwards to 10 seconds.
So, the first time a pre-compiled query gets called, you get a performance hit. After that first hit, performance is noticeably better than the same non-pre-compiled query. Practically the same as Linq2Sql
When you load a page with pre-compiled queries the first time you will get a hit. It will load in maybe 5-15 seconds (obviously more than one pre-compiled queries will end up being called), while subsequent loads will take less than 300ms. Dramatic difference, and it is up to you to decide if it is ok for your first user to take a hit or you want a script to call your pages to force a compilation of the queries.
Can this query be cached?
{
Dog dog = from dog in YourContext.DogSet where dog.ID == id select dog;
}
No, ad-hoc Linq queries are not cached and you will incur the cost of generating the tree every single time you call it.
Parametrized Queries
Most search capabilities involve heavily parametrized queries. There are even libraries available that will let you build a parametrized query out of lamba expressions. The problem is that you cannot use pre-compiled queries with those. One way around that is to map out all the possible criteria in the query and flag which one you want to use:
public struct MyParams
{
public string name;
public bool checkName;
public int age;
public bool checkAge;
}
static readonly Func<Entities, MyParams, IEnumerable<Dog>> query_GetDog =
CompiledQuery.Compile<Entities, MyParams, IEnumerable<Dog>>((ctx, myParams) =>
from dog in ctx.DogSet
where (myParams.checkAge == true && dog.Age == myParams.age)
&& (myParams.checkName == true && dog.Name == myParams.name )
select dog);
protected List<Dog> GetSomeDogs()
{
MyParams myParams = new MyParams();
myParams.name = "Bud";
myParams.checkName = true;
myParams.age = 0;
myParams.checkAge = false;
return query_GetDog(YourContext,myParams).ToList();
}
The advantage here is that you get all the benifits of a pre-compiled quert. The disadvantages are that you most likely will end up with a where clause that is pretty difficult to maintain, that you will incur a bigger penalty for pre-compiling the query and that each query you run is not as efficient as it could be (particularly with joins thrown in).
Another way is to build an EntitySQL query piece by piece, like we all did with SQL.
protected List<Dod> GetSomeDogs( string name, int age)
{
string query = "select value dog from Entities.DogSet where 1 = 1 ";
if( !String.IsNullOrEmpty(name) )
query = query + " and dog.Name == @Name ";
if( age > 0 )
query = query + " and dog.Age == @Age ";
ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>( query, YourContext );
if( !String.IsNullOrEmpty(name) )
oQuery.Parameters.Add( new ObjectParameter( "Name", name ) );
if( age > 0 )
oQuery.Parameters.Add( new ObjectParameter( "Age", age ) );
return oQuery.ToList();
}
Here the problems are: - there is no syntax checking during compilation - each different combination of parameters generate a different query which will need to be pre-compiled when it is first run. In this case, there are only 4 different possible queries (no params, age-only, name-only and both params), but you can see that there can be way more with a normal world search. - Noone likes to concatenate strings!
Another option is to query a large subset of the data and then narrow it down in memory. This is particularly useful if you are working with a definite subset of the data, like all the dogs in a city. You know there are a lot but you also know there aren't that many... so your CityDog search page can load all the dogs for the city in memory, which is a single pre-compiled query and then refine the results
protected List<Dod> GetSomeDogs( string name, int age, string city)
{
string query = "select value dog from Entities.DogSet where dog.Owner.Address.City == @City ";
ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>( query, YourContext );
oQuery.Parameters.Add( new ObjectParameter( "City", city ) );
List<Dog> dogs = oQuery.ToList();
if( !String.IsNullOrEmpty(name) )
dogs = dogs.Where( it => it.Name == name );
if( age > 0 )
dogs = dogs.Where( it => it.Age == age );
return dogs;
}
It is particularly useful when you start displaying all the data then allow for filtering.
Problems: - Could lead to serious data transfer if you are not careful about your subset. - You can only filter on the data that you returned. It means that if you don't return the Dog.Owner association, you will not be able to filter on the Dog.Owner.NameSo what is the best solution? There isn't any. You need to pick the solution that works best for you and your problem:- Use lambda-based query building when you don't care about pre-compiling your queries.- Use fully-defined pre-compiled Linq query when your object structure is not too complex.- Use EntitySQL/string concatenation when the structure could be complex and when the possible number of different resulting queries are small (which means fewer pre-compilation hits).- Use in-memory filtering when you are working with a smallish subset of the data or when you had to fetch all of the data on the data at first anyway (if the performance is fine with all the data, then filtering in memory will not cause any time to be spent in the db).
Singleton access
The best way to deal with your context and entities accross all your pages is to use the singleton pattern:
public sealed class YourContext
{
private const string instanceKey = "On3GoModelKey";
YourContext(){}
public static YourEntities Instance
{
get
{
HttpContext context = HttpContext.Current;
if( context == null )
return Nested.instance;
if (context.Items[instanceKey] == null)
{
On3GoEntities entity = new On3GoEntities();
context.Items[instanceKey] = entity;
}
return (YourEntities)context.Items[instanceKey];
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly YourEntities instance = new YourEntities();
}
}
NoTracking, is it worth it?
When executing a query, you can tell the framework to track the objects it will return or not. What does it mean? With tracking enabled (the default option), the framework will track what is going on with the object (has it been modified? Created? Deleted?) and will also link objects together, when further queries are made from the database, which is what is of interest here.
For example, lets assume that Dog with ID == 2 has an owner which ID == 10.
Dog dog = (from dog in YourContext.DogSet where dog.ID == 2 select dog).FirstOrDefault();
//dog.OwnerReference.IsLoaded == false;
Person owner = (from o in YourContext.PersonSet where o.ID == 10 select dog).FirstOrDefault();
//dog.OwnerReference.IsLoaded == true;
If we were to do the same with no tracking, the result would be different.
ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)
(from dog in YourContext.DogSet where dog.ID == 2 select dog);
oDogQuery.MergeOption = MergeOption.NoTracking;
Dog dog = oDogQuery.FirstOrDefault();
//dog.OwnerReference.IsLoaded == false;
ObjectQuery<Person> oPersonQuery = (ObjectQuery<Person>)
(from o in YourContext.PersonSet where o.ID == 10 select o);
oPersonQuery.MergeOption = MergeOption.NoTracking;
Owner owner = oPersonQuery.FirstOrDefault();
//dog.OwnerReference.IsLoaded == false;
Tracking is very useful and in a perfect world without performance issue, it would always be on. But in this world, there is a price for it, in terms of performance. So, should you use NoTracking to speed things up? It depends on what you are planning to use the data for.
Is there any chance that the data your query with NoTracking can be used to make update/insert/delete in the database? If so, don't use NoTracking because associations are not tracked and will causes exceptions to be thrown.
In a page where there are absolutly no updates to the database, you can use NoTracking.
Mixing tracking and NoTracking is possible, but it requires you to be extra careful with updates/inserts/deletes. The problem is that if you mix then you risk having the framework trying to Attach() a NoTracking object to the context where another copy of the same object exist with tracking on. Basicly, what I am saying is that
Dog dog1 = (from dog in YourContext.DogSet where dog.ID == 2).FirstOrDefault();
ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)
(from dog in YourContext.DogSet where dog.ID == 2 select dog);
oDogQuery.MergeOption = MergeOption.NoTracking;
Dog dog2 = oDogQuery.FirstOrDefault();
dog1 and dog2 are 2 different objects, one tracked and one not. Using the detached object in an update/insert will force an Attach() that will say "Wait a minute, I do already have an object here with the same database key. Fail". And when you Attach() one object, all of its hierarchy gets attached as well, causing problems everywhere. Be extra careful.
How much faster is it with NoTracking
It depends on the queries. Some are much more succeptible to tracking than other. I don't have a fast an easy rule for it, but it helps.
So I should use NoTracking everywhere then?
Not exactly. There are some advantages to tracking object. The first one is that the object is cached, so subsequent call for that object will not hit the database. That cache is only valid for the lifetime of the YourEntities object, which, if you use the singleton code above, is the same as the page lifetime. One page request == one YourEntity object. So for multiple calls for the same object, it will load only once per page request. (Other caching mechanism could extend that).
What happens when you are using NoTracking and try to load the same object multiple times? The database will be queried each time, so there is an impact there. How often do/should you call for the same object during a single page request? As little as possible of course, but it does happens.
Also remember the piece above about having the associations connected automatically for your? You don't have that with NoTracking, so if you load your data in multiple batches, you will not have a link to between them:
ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)(from dog in YourContext.DogSet select dog);
oDogQuery.MergeOption = MergeOption.NoTracking;
List<Dog> dogs = oDogQuery.ToList();
ObjectQuery<Person> oPersonQuery = (ObjectQuery<Person>)(from o in YourContext.PersonSet select o);
oPersonQuery.MergeOption = MergeOption.NoTracking;
List<Person> owners = oPersonQuery.ToList();
In this case, no dog will have its .Owner property set.
Some things to keep in mind when you are trying to optimize the performance.
No lazy loading, what am I to do?
This can be seen as a blessing in disguise. Of course it is annoying to load everything manually. However, it decreases the number of calls to the db and forces you to think about when you should load data. The more you can load in one database call the better. That was always true, but it is enforced now with this 'feature' of EF.
Of course, you can call if( !ObjectReference.IsLoaded ) ObjectReference.Load();if you want to, but a better practice is to force the framework to load the objects you know you will need in one shot. This is where the discussion about parametrized Includes begins to make sense.
Lets say you have you Dog object
public class Dog
{
public Dog Get(int id)
{
return YourContext.DogSet.FirstOrDefault(it => it.ID == id );
}
}
This is the type of function you work with all the time. It gets called from all over the place and once you have that Dog object, you will do very different things to it in different functions. First, it should be pre-compiled, because you will call that very often. Second, each different pages will want to have access to a different subset of the Dog data. Some will want the Owner, some the FavoriteToy, etc.
Of course, you could call Load() for each reference you need anytime you need one. But that will generate a call to the database each time. Bad idea. So instead, each page will ask for the data it wants to see when it first request for the Dog object:
static public Dog Get(int id) { return GetDog(entity,"");}
static public Dog Get(int id, string includePath)
{
string query = "select value o " +
" from YourEntities.DogSet as o " +
Please do not use all of the above info such as "Singleton access". You absolutely 100% should not be storing this context to be reused as it is not thread safe.
这篇关于什么是实体框架时良好的设计实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!