使用Entity Framework时有哪些好的设计实践

asp.net entity-framework visual-studio-2008

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

这是一个社区帖子,所以请在您认为合适时添加。

适用于 :Visual Studio 2008 sp1附带的Entity Framework 1.0。

为什么首先选择EF?

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

EF和继承

第一个重要的主题是继承。 EF确实支持以两种方式持久化的继承类的映射:每个类的表和表的层次结构。建模很简单,该部分没有编程问题。

(以下适用于每个类模型的表,因为我没有每个层次结构的表的经验,无论如何,这是有限的。)当您尝试运行包含一个或多个对象的查询时,真正的问题就出现了。继承树:生成的sql非常糟糕,需要很长时间才能被EF解析并且需要很长时间才能执行。这是一个真正的表演塞子。足够的EF应该不能用于继承或尽可能少。

这是一个多么糟糕的例子。我的EF模型有~30个类,其中约10个是继承树的一部分。在运行查询以从Base类获取一个项目时,就像Base.Get(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.Compile()函数。 (请注意,通过在页面中使用EntityDataSource,您实际上将使用带有EntitySQL的ObjectQuery,以便进行编译和缓存)。

在这里,如果您不知道EntitySQL是什么。它是一种基于字符串的方法,可以针对EF编写查询。下面是一个示例:“从Entities.DogSet中选择值dog作为狗,其中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());

现在,如果您想要包含参数化怎么办?我的意思是你想拥有一个从不同页面调用的Get()函数,它关心狗的不同关系。一个关心业主,另一个关心他的最喜欢的食物,另一个关于他的FavotireToy等等。基本上,您想要告诉查询要加载哪些关联。

使用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(字符串)改进Include(字符串)函数(仅接受单个路径),该函数将允许您将一串逗号分隔的关联传递给load。在此功能的扩展部分中进一步查看。

但是,如果我们尝试使用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.FavoriteFood”混淆!)。

然后,让我们使用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,因为它不是可识别的函数的一部分:它是一个扩展。

好的,所以你想要向你的函数传递任意数量的路径,而Includes()只需要一个路径。该怎么办?您可以决定永远不会需要超过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 ...)时,你使缓存的编译查询失效,因为它现在是一个全新的!因此,需要重新表达表达式树,并再次获得性能。

那么解决方案是什么?您根本无法将CompiledQueries与参数化包含一起使用。请改用EntitySQL。这并不意味着CompiledQueries没有用途。对于始终在同一上下文中调用的本地化查询非常有用。理想情况下应始终使用CompiledQuery,因为在编译时检查语法,但由于限制,这是不可能的。

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

将超过3个参数传递给CompiledQuery

Func限制为5个参数,其中最后一个是返回类型,第一个是模型中的Entities对象。所以这给你留下3个参数。一个pitance,但它可以非常容易地改进。

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(即实现IEnumerable的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(),请继续。在许多情况下,您希望在执行查询之前优化查询。

性能

编译查询对性能有何影响?它实际上可能相当大。根据经验,编译和缓存查询以便重用至少需要两倍的时间来简单地执行它而不进行缓存。对于复杂的查询(read inherirante),我已经看到了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();
}

这里的优点是你获得了预编译的quert的所有好处。缺点是你最有可能最终得到一个很难维护的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种不同的可能查询(没有参数,仅年龄,仅名称和两种参数),但是您可以看到通过正常的世界搜索可以有更多的查询。 - 没有人喜欢连接字符串!

另一种选择是查询数据的大部分,然后在内存中缩小数据。如果您正在使用一定数量的数据,例如城市中的所有狗,这将特别有用。你知道有很多,但你也知道没有那么多......所以你的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;
}

当您开始显示所有数据然后允许过滤时,它尤其有用。

问题: - 如果您不小心您的子集,可能会导致严重的数据传输。 - 您只能过滤返回的数据。这意味着如果你不返回Dog.Owner协会,你将无法过滤Dog.Owner.Name那么什么是最好的解决方案?没有。您需要选择最适合您和您的问题的解决方案: - 当您不关心预编译查询时,使用基于lambda的查询构建。 - 当对象结构不太复杂时,使用完全定义的预编译Linq查询。 - 当结构可能很复杂并且可能的不同结果查询数量很小时(这意味着更少的预编译命中),使用EntitySQL /字符串连接。 - 当您使用一小部分数据时或者必须首先获取数据上的所有数据时使用内存中过滤(如果所有数据的性能都很好,那么内存中的过滤将不会导致任何时间花在数据库中)。

单身人士访问

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

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,值得吗?

执行查询时,您可以告诉框架跟踪它将返回的对象。这是什么意思?启用跟踪(默认选项)后,框架将跟踪对象的进行情况(已修改?已创建?已删除?)并且还会在将对象链接到一起时进行进一步查询,这是什么这里很感兴趣。

例如,假设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;

跟踪是非常有用的,在没有性能问题的完美世界中,它总是会开启。但在这个世界上,就性能而言,它有代价。那么,你应该使用NoTracking加快速度吗?这取决于您计划使用的数据。

您的查询使用NoTracking的数据是否有可能用于在数据库中进行更新/插入/删除?如果是这样,请不要使用NoTracking,因为不跟踪关联并且会导致抛出异常。

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

混合跟踪和NoTracking是可能的,但它要求您在更新/插入/删除时要格外小心。问题是,如果你混合,那么你可能会冒险让框架尝试将NoTracking对象附加到上下文中,其中存在跟踪的同一对象的另一个副本。基本上,我所说的是

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

NoTracking的速度有多快

这取决于查询。有些人比其他人更容易追踪。我没有快速简单的规则,但它有所帮助。

所以到处都应该使用NoTracking?

不完全是。跟踪对象有一些优点。第一个是对象被缓存,因此对该对象的后续调用将不会访问数据库。该缓存仅在YourEntities对象的生命周期内有效,如果使用上面的单例代码,则该对象与页面生存期相同。一页请求==一个YourEntity对象。因此,对于同一对象的多次调用,每个页面请求只会加载一次。 (其他缓存机制可以扩展)。

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

还要记住上面有关于自动连接关联的部分吗?你没有使用NoTracking,所以如果你多次加载你的数据,你就不会有它们之间的链接:

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的这个“功能”强制执行。

当然,你可以调用if(!ObjectReference.IsLoaded)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 " +

热门答案

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



许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因