EntityFramework 4.1笔记


//全部条件
customerList = db.ShopOfCus.Where(o => o.CusLevel.ProviderID == LoginProCon.ProviderID)
.Distinct().Select(c => c.Customer).Where(c => c.UserName.Contains(multiCondition) 
|| c.QQ.Contains(multiCondition) || c.WeiBo.Contains(multiCondition) 
|| System.Data.Linq.SqlClient.SqlMethods.DateDiffDay(c.LogDate, logDate) == 0).Skip(SkipIndex).Take(PageSize);

Linq之ToDictionary<TSource, TKey, TElement>的写法

Dictionary<short, string> dic = db.CusLevel.Where(c => c.ProviderID == 23).ToDictionary(new Func<CusLevel, short>(c => c.CusLevelID), new Func<CusLevel, string>(c => c.CusLevelName));
//如果觉得上面的写法太复杂,还可以简化为
Dictionary<short, string> dic = db.CusLevel.Where(c => c.ProviderID == 23).ToDictionary(c => c.CusLevelID, c => c.CusLevelName);  

DbContext API中使用SqlQuery和ExecuteSqlCommand获取存储过程的输入输出参数

在 Entity Framework 4.1中引入的DbContext API暴露了一些新的方法用于提供透传原生SQL给数据库执行查询和命令,比如 Database.SqlQuery<T>, 以及 Database.ExecuteSqlCommand

这些方法很重要不仅仅因为它们允许你执行自己的原生SQL查询,而且因为它们是当前你可以使用DbContext访问存储过程的主要方法,特别当你使用代码优先的开发模式。

就实现而言,它们是在EF 4.0中加入的ObjectContext.ExecuteStoreQuery<T>ObjectContext.ExecuteStoreCommand 的更简单变种。不管怎么说,关于这些方法可以做什么看上去还是会有一些困惑,尤其是它们支持的查询语法。

简而言之我觉得以下几点可以说明这些方法是怎么工作的:
1、传递给方法的查询文本会被设置给来自底层ADO.NET提供程序的DbCommand对象。
2、DbCommand对象是通过设置CommandType属性值为CommandType.Text来执行的。
3、此外,如果方法会返回你之前传递的对象类型的结果(例如SqlQuery<T>),这个结果是基于DbDataReader返回的值所提取的。

你可以使用类似下面的语法,从一个存储过程返回的必要列中提取一个个人实体对象:

 var idParam = new SqlParameter { 
      ParameterName = "id",  
      Value = 1}; 
 var person = context.Database.SqlQuery<Person>( 
      "GetPerson @id",  
      idParam);

为了方便这些方法同时也接收普通的原始类型参数。你可以使用类似“{0}”的语法在查询语句中使用这些参数。

var person = context.Database.SqlQuery<Person>(
     "SELECT * FROM dbo.People WHERE Id = {0}", id);

可是这些语法适用性有局限,而且可能你要做的一些事情需要更好的控制,像调用一个带输入输出参数的存储过程,或者参数不是基本类型,你必须使用数据源所支持的完整的SQL语法。

我想分享一个关于使用输入输出参数的简单例子,这样可以更好的举例说明。

在你的SQL Server数据库中创建一个(完整但没什么用)存储过程定义:

CREATE PROCEDURE [dbo].[GetPersonAndVoteCount] 
( 
 @id int, 
 @voteCount int OUTPUT ) 
AS 
BEGIN 
SELECT @voteCount = COUNT(*) 
FROM dbo.Votes 
WHERE PersonId = @id; 
SELECT * 
FROM dbo.People 
WHERE Id = @id; 
END

你可以写一段如下所示的代码执行它:

var idParam = new SqlParameter {ParameterName = "id",Value = 1};
var votesParam = new SqlParameter {ParameterName = "voteCount",Value = 0,Direction = ParameterDirection.Output };
var results = context.Database.SqlQuery<Person>("GetPersonAndVoteCount @id, @voteCount out",idParam,votesParam);
var person = results.Single();
var votes = (int)votesParam.Value;

在上面这段代码中有几个注意事项:
1、SqlQuery和ExecuteSqlCommand方法所支持的主要语法是被底层ADO.NET提供程序所支持的原生SQL语法。(有人在评论中提到SQL Server 2005必须在存储过程名前面加上EXEC关键字)。
2、DbCommand是使用CommandType.Text(相对于CommandType.StoredProcedure)来执行的,这意味着它不会自动为存储过程绑定参数,尽管如此你还是可以使用普通的SQL语法来执行存储过程。
3、你必须使用正确的语法来为存储过程传递一个输入输出参数,比如你需要在SQL语句中参数名称的后面增加一个“OUT”关键字。
4、输入和输出参数仅仅在使用实际的DbParameters类型的时候起作用(在这个例子中我们使用SqlParameters因为我们使用SQL Server),SqlQuery和ExecuteSqlCommand所支持的普通类型对象是不支持输入输出参数的。(译注:SqlQuery和ExecuteSqlCommand的参数要么全部是DbParameter参数,要么全部是普通类型对象)
5、在你访问输入输出参数你必须读整个返回结果(这个例子我们通过Single方法获取),但这是存储过程工作原理不是特定于EF的特性。

一旦你学会了,你可以使用提供程序特定参数和底层数据源的原生SQL语法,你能够获取更多和你使用ADO.NET相同的灵活性,但是使用ADO.NET无法获得重用同一个EF所维护的数据库连接的便利性和直接从查询结果中获取对象的能力。


Entity Framework 小技巧一 —— 如何从DbContext得到其内部封装的ObjectContext:

DbContext及其相关的API是EF4.1中一大新特性。简单说,DbContext就是之前EF的核心类ObjectContext的抽象封装。如果用.NET Reflector看一下DbContext的内部实现,不难发现其内部有一私有成员_internalContext,它属于InternalContext这一Internal的类。DbContext内部的许多函数就是通过InternalContext.ObjectContext属性来得到其内部封装的ObjectContext类并调用其方法的。由于_internalContext是私有成员,要得到它的值需动用反射,好像太劳师动众了。今天为大家介绍十分方便的一个小技巧来得到DbContext内部封装的ObjectContext

DbContext实现两个接口:IDisposableIObjectContextAdapter。前者的博大精深在本文就不提及了。IObjectContextAdapter按照MSDN文档,就是为了让我们方便得到DbContext内部封装的ObjectContext而设计的。所以很简单:

var context = ((IObjectContextAdapter)dbContext).ObjectContext;

转换一下DbContext的接口类型就行了!


Entity Framework 4.1 DbContext使用记之二——如何玩转本地实体?
DbSet.Local属性的使用与实现:
DbSet.Local属性的使用与实现。和上次介绍的Find函数首先查找context中缓存的实体类似,DbSet的Local属性也是返回context中缓存并且被跟踪的实体。
不同点在于,Local属性不会返回状态为EntityState.Deleted的实体,且即使缓存中什么实体都没有,也不会对数据库进行访问。这样的设计也正符合Local(本地)之意。

using (var db = new MyDbContext())
{
     // 此处调用EF4.1的新扩展方法DbSet<>.Load()从数据库导入对应的实体到缓存中
     db.People.Load();
     db.People.Add(new Person { Name = "Michael" });
     db.People.Remove(db.People.Find(1));
     foreach (var p in db.People.Local)
     {
         // 这里调用了EF4.1的新方法Entry来得到实体的DbEntryEntry
         Console.WriteLine("Found {0}: {1} with state {2}", p.PersonID, p.Name, db.Entry(p).State);
     }
}  

过去我们可以用以下两种方法来实现这一目的:

1) 利用匿名类,并且将过滤条件嵌入到返回的匿名类中:

using (var context = new MyObjectContext))
{
    var query = (from b in context.Blogs
                 where b.Tags.Contains("EF")
                 select new 
                 {
                     Blog = b,
                     Posts = b.Posts.Where(p => p.CreationTime >= new DateTime(2011, 1, 1))
                 }).ToList();
} 

不过,此方法的缺点显而易见, 我们必须使用匿名类来包装实体对象,在数据绑定时会有挺多问题。

2) 使用多个query,让EF的跟踪机制帮助我们建立对象之间的联系。(EF4和EF4.1中,我们需要关闭Lazy Loading)

using(var context = new MyObjectContext))
{
    // For EF4
    // context.ContextOptions.LazyLoadingEnabled = false;
    // For EF4.1
    // context.Configuration.LazyLoadingEnabled = false;
    var blogs = (from b in context.Blogs
                 where b.Tags.Contains("EF")
                 select b).ToList();

    var posts = (from p in context.Posts
                 where p.Blog.Tags.Contains("EF")
                 && p.CreationTime >= new DateTime(2011, 1, 1)
                 select p).ToList();
}

在两个query执行之后,EF的ObjectStateManager会自动建立Blog和Post之间的关系,如果我们访问blogs.First().Posts属性,将得到的都是今年1月1日之后创建的博文。

当然我想这两个方法在EF4.1中同样能成功。幸运的是,EF4.1提供了新的功能使我们可以在使用Explicitly Loading时添加过滤条件。

using (var context = new MyDbContext())
{
   var blog = context.Blogs.Where(b => b.Tags.Contains("EF")).First();
   context.Entry(blog).Collection(b => b.Posts)
          .Query()
          .Where(p => p.CreationTime >= new DateTime(2011, 1, 1))
          .Load();
   // 或者使用String类型来标记Blog与Post的关系
   context.Entry(blog).Collection("Posts")
          .Query().Cast<Post>()
          .Where(p => p.CreationTime >= new DateTime(2011, 1, 1))
          .Load();
}

Collection()方法在这里返回DbCollectionEntry,Query()方法返回对应的IQueryable<T>对象。熟悉LINQ的同志们一定知道,得到IQueryable<T>了的话,一切好说,哈哈。EF4.1中,System.Data.Entity命名空间下的DbExtensions类中定义了不少LINQ的扩展方法,其中一个就是Load方法。

值得注意的是:在EF4.1中使用这一功能最好也关闭Lazy Loading,否则一旦对应的集合类型的Navigation Property被访问的话,所有对应的实体都将被导入。
当然这里如果我们不想导入这些相关的实体对象,也可以使用Count()方法得到符合条件对象的数量。这样我们得到的是实体的数量,而没有一个对应实体会被导入。

using (var context = new MyDbContext())
{
   var blog = context.Blogs.Where(b => b.Tags.Contains("EF")).First();

   context.Entry(blog).Collection(b => b.Posts)
          .Query()
          .Count();
}

Entity Framework 小技巧四 —— 如何使用NoTracking查询得到Detached状态的实体?

有时我们的实体只需要显示,无需更新,所以为了提高性能,我们不需要实体被EF context追踪。此时可以使用NoTracking的查询来得到实体,这样实体的状态会是Detached状态。

在EF3.5 SP1和EF 4中,我们可以这样来进行NoTracking查询:

using (var context = new MyObjectContext())
{
    context.People.MergeOption = System.Data.Objects.MergeOption.NoTracking;
    var list = context.People.Where(p => PersonID > 100).ToList();
}

或使用ExecuteStoreQuery API来直接调用SQL命令来得到实体:

using (var context = new MyObjectContext())
{
    var query = context.ExecuteStoreQuery<Parent>("SELECT * FROM Parent WHERE Parent.ParentID > @ID", "TestDBEntities.Parents", System.Data.Objects.MergeOption.NoTracking, new SqlParameter("@ID", 100));
}

或使用Load方法得到相关实体:

using (var context = new MyObjectContext())
{
    context.ContextOptions.LazyLoadingEnabled = false;
    var person = context.People.First();
    person.Courses.Load(System.Data.Objects.MergeOption.NoTracking);
}

在EF 4.1中,我们可以使用新增的AsNoTracking方法:

using (var context = new MyDbContext())
{
     var people = context.People.Where(p => p.PersonID > 100).AsNoTracking().ToList();
}

这里的AsNoTracking方法内部调用了过去的ObjectQuery.MergeOption来实现NoTracking查询。此方法(DbHelpers.CreateNoTrackingQuery)是AsNoTracking方法的核心:

public static IQueryable CreateNoTrackingQuery(ObjectQuery query)
{
    IQueryable queryable = query;
    ObjectQuery query2 = (ObjectQuery) queryable.Provider.CreateQuery(queryable.Expression);
    query2.MergeOption = MergeOption.NoTracking;
    return query2;
}

NoTracking得到的Detached实体的一个小问题。由NoTracking查询得到的实体和我们直接调用Detach方法得到的实体不同。前者内部会仍然保留一个对当前context的引用,以便在调用Load方法可以explicitly load相关的实体。而后者则完全脱离了相应的context,所以属于真正的Detached状态。细心的读者可能会觉得NoTracking得到的Detached实体会导致context一直被引用,从而不能及时被垃圾处理(GC)。确实,这个被产品组证实是by design的,如果context不被引用,则explicit load则无法被支持。


Entity Framework 小技巧五 —— 如何得到EF查询生成的SQL?

在EF 4和EF 3.5 SP1中,我们可以使用ToTraceString()方法得到EF查询所生成的SQL。

using (var context = new TestDBEntities())
{
    var query = from p in context.Parents
                where p.Name == "Lingzhi"
                select p;

    ObjectQuery<Parent> parents = query as ObjectQuery<Parent>;
    if (parents != null)
    {
        string sql = parents.ToTraceString();
    }
}

在EF 4.1中,我们可以直接调用DbQuery<>的ToString()方法得到所生成的SQL。

using (var context = new MyDbContext())
{
    var people = from p in context.People
                 where p.PersonID > 100
                 select p;

    string sql = people.ToString();
}

How to: Display Generated SQL (LINQ to SQL)?
例子:

db.Log = Console.Out;
IQueryable<Customer> custQuery =
    from cust in db.Customers
    where cust.City == "London"
    select cust;

foreach(Customer custObj in custQuery)
{
    Console.WriteLine(custObj.CustomerID);
}

新的API,DbSet<>.Find()

过去我们常常用Where或First(FirstOrDefault)方法来查找对应的实体,比如:

var people = from p in context.People
             where p.Name.StartsWith("M")
             select u;
var people = context.People.FirstOrDefault(p => p.Name == "Michael");  

这样查找的缺点是:即使我们相应的实体已经被ObjectContext缓存,EF还是会去执行数据库访问,而数据库访问是被普遍认为比较耗费性能的。

EF4.1为我们提供了一个新的API: DbSet<>.Find()。它可以帮助我们通过主键来查找对应的实体。并且如果相应的实体已经被ObjectContext缓存,EF会在缓存中直接返回对应的实体,而不会执行数据库访问。
比如我们查找主键PersonID为1的Person实体:

var person = context.People.Find(1);

也可用于联合主键(比如查找主键PersonID=1, PersonName=”Michael”的实体):

var person = context.People.Find(1, "Michael");

注意:此处输入联合主键的次序需要按照我们定义改实体类时声明主键的次序。
和之前直接用Where或First的调用不同,Find函数对于刚刚新增的实体也能找到:

using (var context = new MyContext())
{
    context.People.Add(new People { PersonID = 100 });
    var newPerson = context.People.Find(100);
}

Find函数首先调用了EF的内部实现:

public TEntity Find(params object[] keyValues)
{
    this.InternalContext.DetectChanges(false);
    WrappedEntityKey key = new WrappedEntityKey(this.EntitySet, this.EntitySetName, keyValues, "keyValues");
    object obj1 = this.FindInStateManager(key);
    if (obj1 != null)
    {
        goto Label_003E;
    }
    object obj2 = this.FindInStore(key, "keyValues");
    if ((obj2 != null) && !(obj2 is TEntity))
    {
        throw Error.DbSet_WrongEntityTypeFound(obj2.GetType().Name, typeof(TEntity).Name);
    }
    return (TEntity) obj2;
}

这里,EF首先调用了DetectChanges()来同步内部ObjectContext跟踪的对象的状态。WrappedEntityKey则对过去的EntityKey做了一个包装,每个主键对应于一个KeyValuePair。将Find函数的输入值一一包装成KeyValuePair,并最终生成EntityKey对象。
之后,Find函数调用了FindInStateManager函数。顾名思义,EF首先在内部缓存中查找对应实体。对于状态是Unchanged、Modified以及Deleted的实体,FindInStateManager直接使用了过去ObjectContext.ObjectStateManager.TryGetObjectStateEntry方法,并且直接将EntityKey传给该方法。
对于状态是Added的新增实体,这里需要做的处理稍微复杂一些。EF先调用了ObjectStateManager.GetObjectStateEntries(EntityState.Added)方法得到所有状态是Added的Entry。
然后依次比较主键的值(之前得到的KeyValuePair)以判定是否为所要的实体。在得到Added状态的实体的值时,EF也使用了ObjectStateEntry.CurrentValues
如果在内部缓存中找不到相应的实体,Find函数会调用FindInStore函数来对数据库进行查找。FindInStore函数内部使用了Entity SQL的查询,以这样一个Entity SQL命令为基础:SELECT VALUE X FROM {0} AS X WHERE
再将对应的主键名字拼接到这个命令中。主键值则以ObjectParameter的形式传递。最后FindInStore调用ObjectContext.CreateQuery<>(...).SingleOrDefault()函数来执行Entity SQL查询。
由于最后使用了SingleOrDefault,如果数据库中也没有对应的实体存在,Find函数会返回NULL。


而在EF4.1中,由于我们可以使用DbContext.Entry()DbContext.Entry<T>()来得到DbEntityEntryDbEntityEntry<T>对象。通过DbEntityEntry (DbEntityEntry<T>)OriginalValuesCurrentValues属性,我们可以方便地得到相应的属性集合(DbPropertyValues)。
通过DbEntityEntry (DbEntityEntry<T>)Property(Property<T>)方法得到DbPropertyEntry(DbPropertyEntry<T>)之后,我们也能通过相应的OriginalValueCurrentValue属性得到单个属性的原始值和当前值。

using (var context = new MyDbContext())
{
    // 有关Find方法,请看EF4.1系列博文一
    var person = context.People.Find(1);

    // 得到Person.Name属性的当前值
    string currentName = context.Entry(person).Property(p => p.Name).CurrentValue;

    // 设置Person.Name属性的当前值
    context.Entry(person).Property(p => p.Name).CurrentValue = "Michael";

    // 通过string值"Name"来获得DbPropertyEntry,而非通过Lambda Expression
    object currentName2 = context.Entry(person).Property("Name").CurrentValue;
}

再来看看如何得到实体的所有属性的OriginalValue(原始值),CurrentValue(当前值)和DatabaseValue(数据库值)吧:

using (var context = new MyDbContext())
{
    var person = context.People.Find(1);

    // 改变对应的实体的Name属性
    person.Name = "Michael";

    // 改变对应属性的数据库值
    context.Database.ExecuteSqlCommand("update Person set Name = 'Lingzhi' where PersonID = 1");

    // 输出对应实体所有属性的当前值,原始值和数据库值
    Console.WriteLine("Current values:");
    PrintValues(context.Entry(person).CurrentValues);

    Console.WriteLine("\nOriginal values:");
    PrintValues(context.Entry(person).OriginalValues);

    Console.WriteLine("\nDatabase values:");
    PrintValues(context.Entry(person).GetDatabaseValues());
}

这里用到的PrintValues函数实现如下:

public void PrintValues(DbPropertyValues values)
{
    foreach (var propertyName in values.PropertyNames)
    {
        Console.WriteLine("Property {0} has value {1}",
                          propertyName, values[propertyName]);
    }
}

1) 设置多层的复杂类型的属性。

例如,Person实体含有Address复杂类型属性(Complex - type),Address又含有City这一string类型属性,那么我们可以这样来获得City这一属性的当前值:

// 使用Lambda Expression
string city = context.Entry(person).Property(p => p.Address.City).CurrentValue;

// 使用string类型的属性表达
object city = context.Entry(person).Property("Address.City").CurrentValue;

这里层次的次数其实没有限制,可以一直点下去的。:) EF的内部实现也是使用递归调用的方式,之后会有详细的介绍。

2) 克隆含有实体属性当期值,原始值或数据库值的对象。

我们可以使用DbPropertyValues.ToObject()方法来克隆将DbPropertyValues中相应的属性和值变成对象。

using (var context = new MyDbContext())
{
    var person = context.People.Find(1);
    var clonedPerson = context.Entry(person).GetDatabaseValues().ToObject();
} 

这里克隆到的对象不是实体类,也不被DbContext所跟踪。但这样的对象在处理数据库并发等问题时会很有用。


1.分页的时候,尽量在数据库里面去分页.在我实际中的项目,我就发现我同事由于他不了解EF属性,它的分页都是做在内存中分页.下面请看他的代码.

    queryToList().Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize);

像上面的语句,他会先把数据从数据库中查出来,然后才分页.

正确的写法应如下:

query.Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize).ToList();

这样才会在数据库中分页.

2.尽量禁用延迟加载,尽量使用预加载和显式加载查询.

Vs 默认生成的代码,是启用了延迟加载的.这样往往是有些开发人员在不了解的情况下,进行了多次往数据库查询.如下的代码.

using (SchoolContainer db = new SchoolContainer())
        {

           var list = db.People.Where(ent => ent.PersonID < 30).ToList();

            foreach (var people in list)

            {

                foreach (var ent in people.StudentGrades)

                {

                    Console.WriteLine(ent.Grade);

                }

              }

        }

如果启用延迟加载,这样会造成多次往返数据库查询的.势必造成性能低下.

因此我们在这里应该使用预加载.代码如下:

var list = db.People.Include("StudentGrades").Where(ent => ent.PersonID < 30).ToList();

当然使用了预加载,延迟加载也不会查询多次.但怕在程序员写代码时,忘了要预加载,如果启用了延迟加载,那么就会多次查询数据库.如果不启用,那么程序员就获取不了数据,这样他就马上明白,要进行预加载了.

当然任何事情不是绝对的.如果被查询的对象,过于复杂,就要慎用预加载

3.注意事务的简短性.

在使用事务时,我们要尽量把查询语句或者其他响事务外的语句移在事务外执行.不然让一个事务的时间太长了,就容易引起资源死锁的问题.我上次就优化我一个同事的代码,他代码里就把所所有不相关的东西都放在事务里执行,这样就造成事务的时间太长,当用压力测时,马上就出现资源被锁的错误.

4.查询出来的实体,如果不考虑删除和修改,请用NoTracking

关于Notracking 的使用方法,请看. http://www.cnblogs.com/LingzhiSun/archive/2011/04/27/EF_Trick4.html

5.批量删除和修改,不要用先把实体查询出来,然后再逐个删除和修改.这样会产生大量的语句,效率肯定会低.

对于这个解决方式一是直接用sql语句执行,二是可以用自己写个扩展方法来操作.虽然有不少人反对这方法.但我个人是倾向于自己写个扩展方法.这样,有2个好处.一是给开发人员使用简单.二是方便管理. 虽然也有网友提出用ObjectStateManager.ChangeObjectState 来实现批量删除,但我是没有找到相关的批量删除方法.而且这个也经常会出异常.

6.使用已编译的查询,虽然到EF5.0, LINQ 查询是自动缓存的.但使用编译查询会比自动缓存的效率高.


LINQ查询时又两种语法可供选择:方法语法(Fluent Syntax)和查询语法(Query Expression)。


EntitySet<TEntity>
为 LINQ to SQL 应用程序中的一对多关系和一对一关系的集合方提供延迟加载和关系维护。
命名空间: System.Data.Linq
程序集: System.Data.Linq(在 System.Data.Linq.dll 中)


导航属性

实体框架 中的导航属性提供了一种在两个实体类型之间导航关联的方法。 导航属性在概念模型中由 NavigationProperty 元素 (CSDL) 定义。 针对对象参与到其中的每个关系,各对象均可以具有导航属性。 使用导航属性,您可以在两个方向上导航和管理关系,如果重数为一或者零或一,则返回 EntityReference,或者如果重数为多个,则返回 EntityCollection。 也可以选择单向导航,这种情况下可以删除导航属性。

修改关系
可以使用导航属性更改关系。
下面的示例通过使用 Order 对象的 Customer 导航属性访问与该订单关联的客户引用,从而将其他客户分配给一个订单:
order.Customer = customer

下面的示例通过使用 Orders 导航属性访问属于 customer 的 orders 集合,从而将订单添加到现有客户订单:
customer.Orders.Add(order).

在外键关联中,通过设置依赖对象的外键属性,可以形成或更改关系:
order.CustomerID = CustomerID.

可以使用导航属性加载通过定义的关联与实体相关的对象。

导航关系

以下示例采用基于方法的查询语法,使用 SelectMany 方法以获取其姓氏为“Zhou”的联系人的所有订单。 Contact.SalesOrderHeader 导航属性用于获取每个联系人的 SalesOrderHeader 对象的集合。

string lastName = "Zhou"; 
using (AdventureWorksEntities context = new AdventureWorksEntities()) 
{ 
    IQueryable<SalesOrderHeader> ordersQuery = context.Contacts 
        .Where(c => c.LastName == lastName).SelectMany(c => c.SalesOrderHeaders); 

    foreach (var order in ordersQuery) 
    { 
        Console.WriteLine("Order ID: {0}, Order date: {1}, Total Due: {2}",order.SalesOrderID, order.OrderDate, order.TotalDue); 
    } 
} 

string lastName = "Zhou"; 
using (AdventureWorksEntities context = new AdventureWorksEntities()) 
{  
    ObjectQuery<DbDataRecord> query = context.Contacts.Select("it.FirstName, " + "it.LastName, it.SalesOrderHeaders") 
        .Where("it.LastName = @ln", new ObjectParameter("ln", lastName)); 

    foreach (DbDataRecord rec in query.Execute(MergeOption.AppendOnly)) 
    { 
        // Display contact's first name. 
        Console.WriteLine("First Name {0}: ", rec[0]); 
        List<SalesOrderHeader> list = rec[2] as List<SalesOrderHeader>; 
        // Display SalesOrderHeader information  
        // associated with the contact. 
        foreach (SalesOrderHeader soh in list) 
        { 
            Console.WriteLine("Order ID: {0},"+"Order date: {1}, Total Due: {2}", soh.SalesOrderID, soh.OrderDate, soh.TotalDue); 
        } 
    } 
} 

二、延迟加载

不要使用延迟加载,要一次把需要的数据读取出来。特别是在循环中使用延迟加载,每次都读取一次数据库,对性能影响非常大。

有主外键关系的使用.Include,没有的使用.Join

五、太复杂的查询使用存储过程

如果涉及多个表的关联,最好使用存储过程。EF虽然也能实现,但其生成的sql恐怕和预想的要差很远。

六、主外键

能使用主外键的,尽可能的使用主外键,这样无论是EF还是数据库,性能都会有所提高。当然太复杂的还是应该使用存储过程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值