使用实体框架时的良好设计实践

这将主要应用于 asp.net 应用程序,其中数据不是通过 SOA 访问的。这意味着您可以访问从框架加载的对象,而不是传输对象,尽管有些建议仍然适用。

这是一个社区的职位,所以请添加到它,因为你认为适合。

应用于 : Visual Studio 2008 sp1附带的实体框架1.0。

一开始为什么选 EF?

考虑到它是一个有很多问题的年轻技术(见下文) ,为您的项目推销 EF 可能很困难。然而,这是微软正在推动的技术(以牺牲作为 EF 子集的 Linq2Sql 为代价)。此外,您可能不满意 NHibernate 或其他解决方案。不管是什么原因,有很多人(包括我)在 EF 工作,生活也不错。

EF 和遗产

第一个大课题是遗产。EF 确实支持以两种方式持久化的继承类的映射: 每个类的表和层次结构的表。建模很容易,而且这部分没有编程问题。

(下面的内容适用于每个类模型的表,因为我没有使用每个层次结构的表的经验,这是有限的。)当您试图运行包含继承树中的一个或多个对象的查询时,真正的问题出现了: 生成的 sql 非常糟糕,需要很长时间才能被 EF 解析,并且执行也需要很长时间。这真是一场精彩绝伦的表演。足以使 EF 不应该与继承或尽可能少地一起使用。

这里有一个例子说明情况有多糟糕。我的 EF 模型有约30个类,其中约10个是继承树的一部分。在运行查询以从 Base 类获取一个项时,类似 Base 这样简单的操作。获取(id)时,生成的 SQL 超过50,000个字符。然后,当您试图返回一些关联时,它会退化得更厉害,甚至会抛出 SQL 异常,表示无法同时查询超过256个表。

好吧,这是不好的,EF 的概念是允许你创建你的对象结构没有(或尽可能少)考虑到实际的数据库实现你的表。在这方面完全失败了。

有什么建议吗?如果可以的话,避免继承,性能会更好。在必要的时候尽量少用。在我看来,这使 EF 成为一个用于查询的美化的 sql 生成工具,但是使用它仍然有优势。以及实现与继承类似的机制的方法。

用接口绕过继承

在使用 EF 时,首先要知道的是,您不能将一个非 EF 建模的类分配给一个基类。甚至不要尝试它,它将被建模器覆盖。那怎么办?

您可以使用接口来强制类实现某些功能。例如,这里有一个 IEntity 接口,它允许您定义 EF 实体之间的关联,在设计时您不知道实体的类型。

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; } }
}

使用这个 IEntity,您就可以在其他类中处理未定义的关联

// 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);
}
}

它利用了一些扩展功能:

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");
}
}
}

不像普通继承那样简单,特别是考虑到必须将 PetType 存储在额外的数据库字段中,但是考虑到性能的提高,我不会回头看。

它也不能建模一对多,多对多的关系,但与“联盟”的创造性使用,它可以使工作。最后,它创建了在对象的属性/函数中加载数据的副作用,您需要注意这一点。在这方面,使用像 GetxYZ ()这样的清晰变数命名原则会有所帮助。

已编译的查询

实体框架的性能不如使用 ADO (显然)或 Linq2SQL 直接访问数据库。然而,有一些方法可以改进它,其中之一就是编译查询。编译查询的性能类似于 Linq2Sql。

什么是已编译查询?它只是一个查询,您告诉框架将解析后的树保存在内存中,这样在下次运行它时就不需要重新生成它。因此,下一次运行时,您将节省解析树所需的时间。不要忽视这一点,因为这是一个非常昂贵的操作,对于更复杂的查询,操作会变得更糟。

编译查询有两种方法: 使用 EntitySQL 创建 ObjectQuery 和使用 CompiledQuery。编译()函数。(注意,通过在页面中使用 EntityDataSource,实际上您将对 EntitySQL 使用 ObjectQuery,因此可以编译并缓存它)。

如果您不知道 EntitySQL 是什么,这里有一个小插曲。它是一种基于字符串的针对 EF 编写查询的方法。下面是一个例子: “从实体中选择值狗。设置为狗,其中 dog.ID =@ID”。语法与 SQL 语法非常相似。您还可以进行相当复杂的对象操作,这一点已经在[这里][1]中得到了很好的解释。

好的,这里介绍了如何使用 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();

第一次运行此查询时,框架将生成表达式树并将其保存在内存中。所以下次执行它时,您将节省这个昂贵的步骤。在该示例中,EnablePlanCaching= true 是不必要的,因为这是默认选项。

编译查询以供以后使用的另一种方法是 CompiledQuery.Compile 方法:

    static readonly Func<Entities, int, Dog> query_GetDog =
CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
ctx.DogSet.FirstOrDefault(it => it.ID == id));

或者使用 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());

调用查询:

query_GetDog.Invoke( YourContext, id );

CompiledQuery 的优点是在编译时检查查询的语法,而 EntitySQL 不检查语法。然而,还有其他的考虑..。

包括

假设您希望查询返回狗主人的数据,以避免对数据库发出2个调用。很简单,对吧?

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());

现在,如果希望将 Include 参数化,该怎么办?我的意思是,您希望有一个单独的 Get ()函数,该函数从不同的页面调用,这些页面关心狗的不同关系。一个关心主人,另一个关心他最喜欢的食物,另一个关心他最喜欢的玩具等等。基本上,您想告诉查询要加载哪些关联。

使用 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();
}

Include 只是使用传递的字符串。很简单。注意,可以使用 IncludeMany (string)来改进 Include (string)函数(它只接受单个路径) ,该函数将允许您传递要加载的以逗号分隔的关联字符串。在扩展部分进一步查看这个函数。

然而,如果我们尝试使用 CompiledQuery 进行此操作,就会遇到许多问题:

显而易见

    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());

当被叫到时会窒息:

query_GetDog.Invoke( YourContext, id, "Owner,FavoriteFood" );

因为,正如上面提到的,Include ()只想看到字符串中的一个路径,这里我们给它2: “ Owner”和“ FavoriteFood”(不要与“ Owner”混淆)。最喜欢的食物!).

然后,让我们使用 IncludeMany () ,它是一个扩展函数

    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());

又错了,这次是因为 EF 不能解析 IncludeMany,因为它不是识别函数的一部分: 它是一个扩展。

好的,所以你想要传递一个任意数量的路径到你的函数,而 include ()只需要一个路径。怎么办?您可以决定永远不需要超过20个包含,并将结构中每个分隔的字符串传递给 CompiledQuery。但现在的问题是这样的:

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

这也很糟糕。好吧,等一下。我们不能用 CompiledQuery 返回 ObjectQuery < > 吗?然后设置包括在内?我也是这么想的:

    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;
}

这应该可以工作,但是当您调用 IncludeMany (或 Include,Where,OrderBy...)时,您会使缓存的编译查询失效,因为它现在是一个全新的查询!因此,需要对表达式树进行重新解析,这样就可以再次提高性能。

那么解决办法是什么?只是不能对参数化包含使用 CompiledQuery。改为使用 EntitySQL。这并不意味着 CompiledQuery 没有用途。对于总是在相同上下文中调用的本地化查询来说,它非常有用。理想情况下应该始终使用 CompiledQuery,因为语法是在编译时检查的,但由于限制,这是不可能的。

一个使用的例子是: 你可能希望有一个页面,查询哪两只狗有相同的喜欢的食物,这是一个有点狭窄的 BusinessLayer 功能,所以你把它放在你的页面,并确切地知道什么类型的包括是必需的。

向 CompiledQuery 传递3个以上参数

Func 被限制为5个参数,其中最后一个是返回类型,第一个是模型中的 Entity 对象。所以只剩下3个参数。很可怜,但是很容易改进。

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();
}

返回类型(这不适用于 EntitySQL 查询,因为它们不与 CompiledQuery 方法在执行期间同时编译)

使用 Linq 时,通常直到最后一刻才强制执行查询,以防下游的其他函数想要以某种方式更改查询:

    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 );


}

这里会发生什么?通过仍然使用原始 ObjectQuery (即实现 IEnumable 的 Linq 语句的实际返回类型) ,它将使已编译的查询失效并强制重新解析。因此,经验法则是返回对象的 List < > 。

    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 );


}

当您调用 ToList ()时,按照编译后的查询执行查询,然后针对内存中的对象执行 OrderBy。可能会慢一点,但我不确定。可以肯定的一点是,您不必担心错误处理 ObjectQuery 和使已编译的查询计划失效。

再说一次,这不是一个笼统的说法。ToList ()是一个防御性编程技巧,但是如果你有正当理由不使用 ToList () ,请继续。在许多情况下,您希望在执行查询之前对其进行细化。

表演

编译查询对性能的影响是什么?它实际上可以相当大。根据经验,编译和缓存查询以便重用所花费的时间至少是不进行缓存的简单执行时间的两倍。对于复杂的查询(阅读继承) ,我已经看到了10秒以上。

因此,第一次调用预编译查询时,性能会受到影响。在第一次命中之后,性能明显优于同样的非预编译查询。实际上与 Linq2Sql 相同

当您第一次加载带有预编译查询的页面时,您将得到一个命中。它可能会在5-15秒内加载(显然不止一个预编译查询会被调用) ,而后续的加载只需要不到300毫秒。戏剧性的差异,这是由您来决定是否是确定您的第一个用户采取的命中或您想要一个脚本调用您的网页,以强制编译的查询。

可以缓存此查询吗?

{
Dog dog = from dog in YourContext.DogSet where dog.ID == id select dog;
}

不,临时 Linq 查询不会被缓存,而且每次调用它时都会产生生成树的成本。

参数化查询

大多数搜索功能涉及大量参数化查询。甚至还有一些库可以让您用 lamba 表达式构建参数化查询。问题在于,不能将预编译查询与这些。解决这个问题的一种方法是,在查询中绘制出所有可能的条件,并标记出您想要使用的条件:

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();
}

这里的优点是您可以获得预编译查询的所有好处。缺点是最终很可能会得到一个很难维护的 where 子句,预编译查询会带来更大的损失,并且运行的每个查询都没有它可以达到的效率(特别是在加入连接的情况下)。

另一种方法是逐片构建 EntitySQL 查询,就像我们对 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();
}

问题在于: - 在编译期间没有语法检查 - 每个不同的参数组合都会产生一个不同的查询,在首次运行时需要预先编译。在这种情况下,只有4种不同的可能的查询(没有 params,只有年龄,只有名称和两个 params) ,但是您可以看到,在普通的世界搜索中可以有更多的查询。 - 没人喜欢串联字符串!

另一种选择是查询数据的一个大的子集,然后在内存中缩小范围。如果您使用的是一个明确的数据子集,就像城市中的所有狗一样,那么这种方法特别有用。你知道有很多,但你也知道没有那么多... 所以你的 CityDog 搜索页面可以载入所有的城市在内存中的狗,这是一个单一的预编译查询,然后完善的结果

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;
}

当您开始显示所有数据并允许进行筛选时,它尤其有用。

问题: - 如果你不注意你的子集,可能会导致严重的数据传输。 - 只能对返回的数据进行筛选。意思是如果你不把狗还回去。业主协会,你将无法过滤狗。老板。姓名 那么最好的解决方案是什么呢?根本没有。你需要选择最适合你和你的问题的解决方案: - 使用基于 lambda 的查询构建,当您不关心预编译您的查询时。 - 当对象结构不太复杂时,使用完全定义的预编译 Linq 查询。 - 当结构可能比较复杂,而且可能产生的不同查询数量较少(这意味着预编译命中较少)时,使用 EntitySQL/string 连接。 - 当你处理一小部分数据时,或者当你必须首先获取数据上的所有数据时,使用内存中的过滤(如果所有数据的性能良好,那么内存中的过滤不会导致任何时间花费在数据库中)。

单人通道

处理所有页面上下文和实体的最好方法是使用单例模式:

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();
}
}

不追踪,值得吗?

在执行查询时,您可以告诉框架跟踪它将返回或不返回的对象。这是什么意思?如果启用了跟踪(默认选项) ,框架将跟踪对象正在发生的事情(它已经被修改了吗?)?创造?删除?)当从数据库进行进一步查询时,还会将对象链接在一起,这就是本文感兴趣的地方。

例如,让我们假设 ID = = 2的 Dog 拥有一个 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;

如果我们在没有跟踪的情况下做同样的事情,结果将会是不同的。

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;

跟踪非常有用,在一个没有性能问题的完美世界中,它将始终处于打开状态。但在这个世界上,这是要付出代价的,就性能而言。那么,是否应该使用“无跟踪”来加快速度呢?这取决于您计划将数据用于什么目的。

有没有可能使用 NoTrace 查询的数据在数据库中进行更新/插入/删除?如果是这样,就不要使用 NoTrace,因为关联不会被跟踪,而且会引发异常。

在数据库绝对没有更新的页面中,可以使用 NoTrace。

混合跟踪和无跟踪是可能的,但它要求您在更新/插入/删除时格外小心。问题是,如果混合使用,那么框架可能会尝试将一个 NoTrace 对象附加到上下文中,而这个上下文中存在同一对象的另一个副本,并且还在跟踪。基本上,我想说的是

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和 dog2是两个不同的对象,一个被跟踪,一个没有。在更新/插入中使用分离对象将强制执行 Attach ()命令,该命令将说“等一下,我在这里已经有一个具有相同数据库键的对象。失败”。并且当您附加()一个对象时,它的所有层次结构也会被附加,从而导致到处都出现问题。要格外小心。

使用“无跟踪”能快多少

这取决于查询。有些人比其他人更容易被跟踪。我没有一个简单快捷的规则,但它有帮助。

那么我应该在任何地方都使用“无跟踪”吗?

不完全是。跟踪目标有一些好处。第一个问题是对象被缓存,因此对该对象的后续调用不会到达数据库。该缓存仅对 YourEntities 对象的生存期有效,如果使用上面的单例代码,则该对象的生存期与页面的生存期相同。一个页面请求 = = 一个 YourEntity 对象。因此,对于同一对象的多个调用,每个页面请求只加载一次。(其他缓存机制可以扩展这一功能)。

当您使用 NoTrace 并尝试多次加载同一个对象时会发生什么?每次都会查询数据库,因此会产生影响。在单个页面请求中,多久调用一次相同的对象?当然,尽可能少,但它确实发生了。

还记得上面关于为你自动连接关联的文章吗?NoTrace 没有这个功能,所以如果你在多个批处理中加载数据,你就不会在它们之间建立链接:

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();

在这种情况下,不会设置狗的.Owner 属性。

当您试图优化性能时,需要记住一些事情。

没有懒惰装载,我该怎么办?

这可以看作是塞翁失马焉知非福。当然,手动加载所有东西是很烦人的。但是,它减少了对数据库的调用次数,并迫使您考虑何时应该加载数据。在一个数据库中加载的数据越多越好。这一直都是正确的,但是现在通过 EF 的这个“特性”强制执行。

当然,你可以打电话 如果(! ObjectReference.IsLoade) ObjectReference. Load () ; 但是更好的做法是强制框架加载您知道一次性需要的对象。这就是关于参数化包含的讨论开始有意义的地方。

让我们假设你有你的狗对象

public class Dog
{
public Dog Get(int id)
{
return YourContext.DogSet.FirstOrDefault(it => it.ID == id );
}
}

这是您一直使用的函数类型。它从各个地方被调用,一旦你有了 Dog 对象,你将用不同的函数对它做不同的事情。首先,它应该是预编译的,因为您将经常调用它。其次,每个不同的页面都希望能够访问 Dog 数据的不同子集。有些人想要主人,有些人想要最喜欢的玩具,等等。

当然,您可以在任何需要引用的时候为每个需要的引用调用 Load ()。但是这将每次生成对数据库的调用。坏主意。因此,每个页面将在第一次请求 Dog 对象时请求它想要查看的数据:

    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 " +
12585 次浏览

虽然信息量很大,但我认为分享如何将所有这些内容融入到完整的解决方案体系结构中可能更有帮助。示例-得到了一个解决方案,显示了如何同时使用 EF 继承和备选方案,以便显示它们的性能差异。

请不要使用上述所有信息,如“单点访问”。您绝对不应该存储此上下文以供重用,因为它不是线程安全的。