LINQ to Entities 是 LINQ 中最吸引人的部分。它让你可以使用标准的 C# 对象与数据库的结构和数据打交道。使用 LINQ to Entities 时,LINQ 查询在后台转换为 SQL 查询并在需要数据的时候执行,即开始枚举结果的时候执行。LINQ to Entities 还为你获取的所有数据提供变化追踪,也就是说,可以修改查询获得的对象,然后整批同时把更新提交到数据库。

LINQ to Entities 是 Entity Framework 的一部分并且取代 LINQ to SQL 作为在数据库上使用 LINQ 的标准机制。Entity Framework 是行业领先的对象-关系映射(ORM)系统。可以和多种数据库一起使用,并支持各种灵活、复杂的数据模型。

注:

微软把开发的重点从 LINQ to SQL 转移到了 LINQ to Entities,并且宣布 LINQ to SQL 不再提供更新,LINQ to SQL 现在仍被支持单不推荐。

LINQ to Entities 是一项令人印象深刻的技术,但对大多数开发人员而言只是一个小的进步。和 DataSet 一样,ASP.NET 开发人员使用 LINQ 的查询特新远多于它的批量更新特性。这是因为通常 Web 应用程序的更新是单次的而不是批量的。他们更愿意在页面回发时立刻执行更新,同时可以获得原始值和新(更新)值,这使得通过 ADO.NET 命令提交更新更加方便。

简而言之,LINQ to Entities 没有提供任何不能用 ADO.NET代码、自定义对象、LINQ to Objects 实现的特性,但是有时出于某些原因而需要考虑使用 LINQ to Entities:

  • 更少的代码。不必编写查询数据库的 ADO.NET 代码,可以通过一个工具生成需要的数据类。
  • 灵活的查询能力。不必拼凑 SQL 语句,而是使用 LINQ 查询模型。一致的查询模型可访问众多不同的数据源(从数据库到 XML)。
  • 变更追踪以及批量更新。可以对查询的数据进行多项修改并提交批量更新,这不需要编写任何 ADO.NET 代码。

生成数据模型

Entity Framework 依赖于一个数据模型来使用 LINQ to Entities 进行查询。表中的行被转换为 C# 对象的实例,表的列是这些对象的属性。数据库架构和数据模型对象的映射是 Entity Framework 的核心.

为了生成模型,右击 App_Code 目录,单击“添加新项”,“ADO.NET 实体数据模型”,设置创建的文件名称后(这里是 NorthwindModel.edmx),单击“确定”。

从一个已经存在的数据库生成模型,即微软的 Northwind 示例数据库。配置数据库连接,并可以选择表、视图、和存储过程。还可以选择使用复数还是单数形式的对象名(例如,Products 表的行被命名为 Product )、是否包含外键关系等。这里选择全部表并选中“所生成对象的单复数形式”。

Visual Studio 会为你选择的数据库元素创建模型图,它显示了已经创建的映射对象、对象拥有的字段以及对象之间的关系。

项目中新增了下面这两个文件:

  • NorthwindModel.edmx:这个XML文件定义数据库模型的架构。
  • NorthwindModel.Designer.cs:这个C#代码文件包含数据模型的映射对象。

数据模型类

我们将把大部分时间花在 NorthwindModel.Designer.cs 这个文件上。因为它包含了我们要用于 LINQ to Entities 查询的数据类型。(这个文件会被数据模型重新生成,因此不应该也不必要手工去修改这个文件,你的修改会丢失。

打开该文件,可以看到有两段代码区域:Contexts 和 Entities 。

1. 派生的对象上下文

NorthwindModel.Designer.cs 文件中定义的第一个类从 ObjectContext 派生,其名称是 NorthwindEntities 。这个类的构造函数连接到所生成模型的数据库,或者你也可以指定连接字符串连接到其他数据库(必须具有相同的架构,否则模型无法工作)。

下面是一个简单的示例:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    GridView1.DataSource = db.Products;

    GridView1.DataBind();

}

2. 实体类

实体类用于把数据库表的记录映射到C#对象。如果选中了“确定所生成对象的单复数形式”选项,那么像 Products 这样的表创建的实体对象名称是 Product。

每个实体对象包含如下的内容:

  • 工厂方法:可以通过默认的构造函数或工厂方法创建实体对象新实例。工厂方法的参数是需要的字段,它是试图保存数据元素时防止架构错误的好办法。
  • 字段属性:实体对象为它们派生的数据库表的每个列包含一个字段属性。
  • 导航属性:如果数据模型中包含了外键关系,实体对象就会包含帮助访问关联数据的导航属性。

提示:

实体类被声明为分部类,因此可以创建扩展功能,在重新生成数据模型时它不会被覆盖。

示例:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    var result = from p in db.Products

                 where p.Discontinued == false

                 select new

                 {

                     ID = p.ProductID,

                     Name = p.ProductName

                 };

    GridView1.DataSource = result;

    GridView1.DataBind();

}

  ID=5 的产品不符合条件被过滤掉了

实体关系

实体类包含导航属性。通过导航属性可以在数据模型间移动而不需要考虑外键关系。看下面的示例:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    var result = from c in db.Customers

                 let o = from q in c.Orders

                         where q.Employee.LastName != "King"

                         select q

                 where c.City == "London" && o.Count() > 5

                 select new

                 {

                     Name = c.CompanyName,

                     Contact = c.ContactName,

                     OrderCount = o.Count()

                 };

    GridView1.DataSource = result;

    GridView1.DataBind();

}

这个查询使用 Orders 导航属性查询每个与 Customer 关联的所有 Orders 。我们使用 Order 实体类型的 Employee 导航属性检查下了订单的员工的姓并过滤掉了姓等于“King”的数据。

使用导航属性,不必为每个实体类创建单独的查询就可以在数据模型间导航。

1. 一对多关系

一对多关系的导航属性通过强类型的 EntityCollection 来处理。针对某个关系选择合适记录的问题你不需要关心,它已经由外键关系处理了。因此选择某个用户的订单时,仅仅得到了那些 CustomerID 值和 Customer的CustomerID 属性值相同的 Order 实例。

使用 SelectMany 扩展方法进行 LINQ to Entities 查询,可以直接把 EntityCollection 类作为查询结果,它会在结果集合里包含所有匹配的结果。

示例如下:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    IEnumerable<Order> orders = db.Customers

        // 定位 LAZYK 客户

        .Where(c => c.CustomerID == "LAZYK") 

        // 以上一个客户结果集为准,导航客户的 Orders 属性获得所有 Order

        .SelectMany(c => c.Orders); 

    GridView1.DataSource = orders;

    GridView1.DataBind();

}

也可以改写成隐式的 LINQ 表达式得到相同的结果:

var result = from o in db.Orders

             where o.CustomerID == "LAZYK"

             select o;

2. 一对一关系

对于一对一关系,有两个导航属性。

  • TReference:它返回的结果是 EntityReference<T> ,其中,T 是关联关系引用的实体类型。例如,Order 实体类型有一个名为 EmployeeReference 的导航属性,它返回 EntityReference<Employee> 。
  • T这个属性更有用。T 是它引用的实体类型。例如,Order 实体类型有一个名为 Employee 的方便的导航属性。

查询存储过程

在解决方案资源管理器双击 NorthwindModel.edmx 文件,打开数据模型图,按右键选择“从数据库更新”可导入存储过程。打开“实体数据模型浏览器”(在“视图”->“其他”菜单里可以找到),展开 NorthwindModel.Store 节点,打开存储过程目录,就会看到导入模型里的存储过程列表。

选中存储过程,右键添加函数导入,导入界面还可以选择“创建新的复杂类型”,然后可以如下使用它:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    IEnumerable<ESBC_Result> results

        = from c in db.ESBC(DateTime.Now.AddYears(-20), DateTime.Now)

          select c;

    GridView1.DataSource = results;

    GridView1.DataBind();

}

此时返回的类型的名称就是先前选择自定义复杂类型的名称。

LINQ to Entities 查询揭秘

先前演示的 LINQ to Entities 的用法几乎和 LINQ to Objects 的用法完全一样。确实是这样(至少表面上是这样)。LINQ 最棒的一件事就是它对各种数据源保持高度的一致性。如果你知道如何使用基本的 LINQ 查询,就可以用它来查询对象、数据库、XML 等。

缺点是这种相似性来自对很多复杂性的隐藏。如果不小心,就会给数据库产生很大的负载。你应该花时间好好检查为了服务你的 LINQ to Entities 查询,究竟生成了什么样的 SQL 查询。通过 Entity Framework 查看 SQL 查询并不容易,需要把 LINQ to Entities 查询的结果转换为 System.Data.Objects.ObjectQuery 的实例并调用 ToTraceString()方法才行。

示例:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    var result = from c in db.Customers

                 let o = from q in c.Orders

                         where q.Employee.LastName != "King"

                         select q

                 where c.City == "London" && o.Count() > 5

                 select new

                 {

                     Name = c.CompanyName,

                     Contact = c.ContactName,

                     OrderCount = o.Count()

                 };

    Label1.Text = (result as System.Data.Objects.ObjectQuery).ToTraceString();

}

很多时候像这样打印 SQL 查询并不现实。如果使用的是非 Express 版本的 SQL Server,可以使用 SQL Server Profile 工具。如果是 Express 版本的,那我们推荐使用 Anjlab 开发的开源免费的 SQL Profile,它很不错。

1. 迟到的过滤

一个导致不必要的数据库查询的常见原因是过滤查询中的数据太晚了,这是一个查询示例:

NorthwindEntities db = new NorthwindEntities();

IEnumerable<NorthwindModel.Customer> custs

    = from c in db.Customers

      where c.Country == "UK"

      select c;

IEnumerable<NorthwindModel.Customer> results

    = from c in custs

      where c.City == "London"

      select c;

GridView1.DataSource = results;

GridView1.DataBind();

这里的问题是,第一个查询从数据库检索 Country=UK 的所有记录。第二个查询应用于第一个查询的结果,但是它使用的是 LINQ to Objects,也就是说我们丢弃了从数据库请求的绝大部分数据(第一个查询就显得非常的浪费资源了)。

这个示例只会产生一条 SQL 查询,类似于于下面:

SELECT * FROM Customers WHERE Country='UK'

解决方案是把过滤器融合到同一个查询里。

2. 使用延迟和贪婪数据加载

为了让导航属性无缝地工作,LINQ to Entities 使用了一项称作延迟加载的技术,只在需要的时候才从数据库加载数据。通过导航属性从某个实体类型转移到另一个实体类型时,第二个实体类型的实例仅在需要的时候才加载。

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

 

    IEnumerable<NorthwindModel.Customer> custs

        = from c in db.Customers

          where c.Country == "UK" && c.City == "London"

          select c;

 

    List<string> names = new List<string>();

    foreach (NorthwindModel.Customer c in custs)

    {

        if (c.Orders.Count > 2)

        {

            names.Add(c.CompanyName);

        }

    }

 

    GridView1.DataSource = names;

    GridView1.DataBind();

}

这个查询中,我们过滤出一组 Customers,然后对其结果进行迭代,并导航到相关的 Order 实例,最终,我们得到了位于英国伦敦且订单多余两笔的公司名称。

由于延迟加载,Orders 表的数据只在需要时加载,也就是说为了在循环中得到每个客户关联的订单,我们都生成了一条 SQL 查询。这产生了太多的查询。对于这个简单的示例,我们可以把所有这一切整合到一个 LINQ 查询里。

但其实我们要演示的是贪婪加载功能。它可以在查询中加载其他表的关联数据。示例如下:

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

 

    IEnumerable<NorthwindModel.Customer> custs

        = from c in db.Customers.Include("Orders")

          where c.Country == "UK" && c.City == "London"

          select c;

 

    List<string> names = new List<string>();

    foreach (NorthwindModel.Customer c in custs)

    {

        if (c.Orders.Count > 2)

        {

            names.Add(c.CompanyName);

        }

    }

 

    GridView1.DataSource = names;

    GridView1.DataBind();

}

使用 Include()扩展方法包含关联的数据,它告诉 LINQ to Entities 引擎关联到我们查询的 Customer 的 Order 实例应该被加载,即便这个查询并没有直接关联到 Orders 表。最终,Entity Framework 捕获了结果,也就是说当我们迭代 Customer 实例并检查关联的 Orders 时,它们都已经被加载了,不需要再生成额外的数据库查询。

3. 使用显式加载

如果要完全控制加载的数据,可以使用显式加载。可以使用派生 ObejectContext 类禁用延迟加载,然后使用 EntityCollection.Load()方法按需加载数据,可以通过 IsLoaded 方法检查所需的数据是否已经加载。

protected void Page_Load(object sender, EventArgs e)

{

    NorthwindEntities db = new NorthwindEntities();

    db.ContextOptions.LazyLoadingEnabled = false;

 

    IEnumerable<NorthwindModel.Customer> custs

        = from c in db.Customers

          where c.Country == "UK"

          select c;

    

    foreach (NorthwindModel.Customer c in custs)

    {

        if (c.City == "London")

        {

            c.Orders.Load();

        }

    }

 

    List<Order> orders = new List<Order>();

    foreach (NorthwindModel.Customer c in custs)

    {

        if (c.Orders.IsLoaded)

        {

            orders.Add(c.Orders.First());

        }

    }

 

    GridView1.DataSource = orders;

    GridView1.DataBind();

}

  1. 禁用了延迟加载,即导航属性要引用的数据不会被自动加载。
  2. 第一次迭代使用 Load()显式加载那些符合条件的 Orders 数据,此时数据会从数据库加载到 Entity Framework 缓存里。
  3. 第二次迭代检查所有的 Customers 对象,用 IsLoaded 属性判断哪些 Customers 加载了 Orders 数据
  4. First()方法可以把第一条 Order 添加到集合中

这个示例并不怎么自然,但它足以让你看出项目中实际使用显式加载所需的知识。

4. 编译查询

LINQ to Entities 另一个隐藏的功能是能够创建已编译的查询。已编译的查询是一个强类型的 Func 委托,它有一个用于查询的参数。编译查询时,执行翻译到 SQL 语句的动作,以后每次调用已编译的查询时都会对其重用。

这并不像使用存储过程那样高效,因为数据库还是要创建查询计划来执行 SQL ,但它确实避免了 LINQ to Entities 重复解析 LINQ 查询。

示例:

using System.Data.Objects;

 

public partial class Chapter13_DerivedObjectContext : System.Web.UI.Page

{

    // 封装一个具有两个参数并返回 TResult 参数指定的类型值的方法。

    Func<NorthwindEntities, string, IQueryable<NorthwindModel.Customer>> MyCompiledQuery;

    NorthwindEntities db;

 

    protected void Page_Load(object sender, EventArgs e)

    {

        // CompiledQuery 表示一个缓存的 LINQ to Entities 查询

        // Compile() 创建一个表示已编译的 LINQ to Entities 查询的新委托

        MyCompiledQuery = CompiledQuery.Compile<NorthwindEntities, string,

            IQueryable<NorthwindModel.Customer>>((context, city) =>

                from c in context.Customers

                where c.City == city

                select c);

 

        db = new NorthwindEntities();

 

        GridView1.DataSource = MyCompiledQuery(db, "London");

        GridView1.DataBind();

    }

}

C#_LINQ(LINQ to Entities)的更多相关文章

  1. LINQ(LINQ to Entities)

    LINQ to Entities 是 LINQ 中最吸引人的部分.它让你可以使用标准的 C# 对象与数据库的结构和数据打交道.使用 LINQ to Entities 时,LINQ 查询在后台转换为 S ...

  2. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  3. Linq之旅:Linq入门详解(Linq to Objects)【转】

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html Linq之旅:Linq入门详解(Linq to Objects) 示例代码下载:Linq之 ...

  4. Linq之旅:Linq入门详解(Linq to Objects)(转)

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html 示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细 ...

  5. LINQ(LINQ to DataSet)

    http://www.cnblogs.com/SkySoot/archive/2012/08/21/2649471.html DataTable.Select()方法使用和 SQL 相似的过滤语法从 ...

  6. MVC 知识点学习3(linq to sql)

    1.通过DbContext对象的Database.SqlQuery执行sql语句 string query = "SELECT EnrollmentDate, COUNT(*) AS Stu ...

  7. LINQ以及LINQ to Object 和LINQ to Entities

    LINQ的全称是Language Integrated Query,中文译成“语言集成查询”,是一种查询技术. LINQ查询通过提供一种跨各种数据源和数据格式使用数据的一致模型,简化了查询过程.LIN ...

  8. EntityFramework 7 更名为EntityFramework Core(预发布状态)

    前言 最近很少去学习和探索新的东西,尤其是之前一直比较关注的EF领域,本身不太懒,但是苦于环境比较影响自身的心情,所以迟迟没有下笔,但是不去学习感觉在精神层面缺少点什么,同时也有园友说EF又更新了,要 ...

  9. C#基础知识汇总(不断更新中)

    ------------------------------目录---------------------------- 1.隐式类型2.匿名类型3.自动属性4.初始化器5.委托6.泛型7.泛型委托8 ...

随机推荐

  1. apache开源项目--TIKA

    Tika是一个内容抽取的工具集合(a toolkit for text extracting).它集成了POI, Pdfbox 并且为文本抽取工作提供了一个统一的界面.其次,Tika也提供了便利的扩展 ...

  2. liunx环境下安装mysql数据库

    一:如果你的机器上之前安装有mysql数据库,先进行卸载 (1)需要先将它的文件删除 (2)同时注意删除老板本的etc/my.cnf文件和/etc/mysql目录,这两个文件控制的是mysql的一些配 ...

  3. Entity Framework 增删改查和事务操作

    1.增加对象 DbEntity db = new DbEntity(); //创建对象实体,注意,这里需要对所有属性进行赋值(除了自动增长主键外),如果不赋值,则会数据库中会被设置为NULL(注意是否 ...

  4. Timus 1746 Hyperrook

    题意:在一个n维坐标系中,坐标的范围是0到m - 1,如果两个点坐标只有一个维度的坐标不同则可以相互移动,给出p个点,问任意两个点之间路径为d的个数是多少,答案与p取模. 解法:只需要考虑两个点之间不 ...

  5. mycat分布式mysql中间件(数据库切分概述)[转]

    mysql数据库切分 前言 通 过MySQLReplication功能所实现的扩展总是会受到数据库大小的限制,一旦数据库过于庞大,尤其是当写入过于频繁,很难由一台主机支撑的时 候,我们还是会面临到扩展 ...

  6. UVA 11488-Hyper Prefix Sets(Trie)

    题意: 给一个01串的集合,一个集合的幸运值是串的个数*集合中串的最大公共前缀 ,求所有子集中最大幸运值 分析: val[N]表示经过每个节点串的个数求幸运值 求就是每个节点值*该节点的深度 搜一遍树 ...

  7. 发送一个简单的HTTP GET请求并且取回响应。

    string uri="http//www.baidu.com"; WebClient wc = new WebClient(); Console.WriteLine(" ...

  8. 安装zabbix server

    本文安装的zabbix版本为2.2 步骤 1.安装php 5.3.3 rpm -e `rpm -qa | grep php` rpm -ivh http://mirrors.163.com/cento ...

  9. activemq 的小实验

    package ch02.chat; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms ...

  10. bzoj 3932 [CQOI2015]任务查询系统(主席树)

    Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分. 超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si ...