冈本正品验证:ADO.NET Entity Framework 学习 1 - VS2010学习 - IC窗口

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 07:43:07

使用实体框架灵活地为数据建模

ADO.NET 实体框架概念最初是在 2006 年作为 ADO.NET vNext引入的,现在它已准备好要在即将发布的 Visual Studio® 2008 SP1中大显身手。在历经多年对类似产品的多次失败尝试后,Microsoft 随 Visual Studio 2008 发布了部分适合于对象关系映射(ORM) 空间的下列两种技术:LINQ to SQL 和 ADO.NET EntityFramework。随着市场逐渐开始采用这些技术,开发人员希望了解我们目前的状况以及 Microsoft未来的发展方向。他们还希望了解这些技术开发背后的原理、实体框架不同于市场上其他 ORM 技术的原因以及 Microsoft对这些技术的投资趋向。

在 Visual Studio 2008 发布伊始,就出现了大量有关 LINQ to SQL 的文章以及介绍应采用哪种技术的文章(请参阅 msdn.microsoft.com/data)。在此我将着重介绍实体框架,并深入探讨在开发过程中应该如何进行选择以及为何如此选择。

Microsoft®实体数据模型 (EDM) 基于 Peter Chen 博士的实体关系 (ER) 模型,实际上它是 ADO.NET EntityFramework 背后的驱动力。EDM 也是区分实体框架与市场上其他 ORM 型技术的最明显特征。EDM 构建在 ER模型之上,它将模型的抽象级别提升到高于逻辑模型的级别,但同时仍保留实体和关系的概念。

为什么需要建立另外一种数据模型?

那么为什么需要建立另外一种模型呢?随着公司数据处理量的增加,理顺数据关系并基于这些数据来开发应用程序变得非常困难。数据库架构的设计需要考虑存储问题(如数据完整性、性能和管理),有时候这不是很容易理解。这些架构还经常与应用程序的结构有冲突,使开发和维护工作变得更加复杂。

我们经常会遇到数据结构与所构建的应用程序被分割开的自定义解决方案。遗憾地是,对每个应用程序而言,自定义解决方案的数量、各种各样的方法以及建模数据所需的步骤都各不相同,导致问题不断产生。整个行业都希望能有一种方法来针对应用程序级的域模型进行定义和开发,以便能够与逻辑模型的存储清晰地分隔开。因此引入了实体框架。

EDM(请参阅图 1 中描述的示例)允许以组织看待和使用数据的方式(不是数据的存储方式)来定义域模型。开发 EDM 还有一个主要目标,那就是在 Microsoft 内成为用于开发人员和服务器技术套件的核心数据模型。

图 1 某个博客数据库的示例实体数据模型

通过使用一个核心数据模型,应用程序的维护得以简化。实现此目标后,EDM 即可发挥作用,不但可以为基于 ADO.NET EntityFramework 构建的自定义应用程序定义模型,还可以作为报告和可视化应用程序、Intranet 门户应用程序或工作流应用程序的输入内容。

与ER 模型类似,EDM使用以下两个主要概念:实体(事物)以及这些实体之间的关系(或关联)。在考虑实体和关联实例(或这些实例集操作的封闭包)的存储时,还需要用到“集”的概念。因此,更清楚地说,实体存在于 EntitySet 中而关联存在于 AssociationSet 中。

在 EDM中定义的最后一个结构概念是 EntityContainer概念,它可以在之前介绍的实例和关系集周围定义一个封闭包。这些简单概念在联合使用后可允许开发人员定义一个域模型,此模型可被映射回持久性层和在应用程序自身中使用的类。(需要注意的是,EDM 的持久性层不需要是相关的,尽管它现在是相关的。)

EDM 中定义的每个实体类型可包含两个不同类型的成员,一个是用来定义实体的属性(类似于数据库中的列),另一个是导航属性,它支持实体类型参与的导航关系(通常表示为数据库中的外键)。此外,每个实体类型都必须有一个唯一的标识或关键字。图 2 中的 XML 片断描述了一个 "Blog Post" 实体类型。

图 2 BlogPost 实体类型定义

   
      
   
   
   
             Unicode="true" FixedLength="false" />
   
   
             Unicode="true" FixedLength="false" />
   
   
             Unicode="true" FixedLength="false" />
   
             FixedLength="false" />
   
 
   
                       "MyTravelPostModel.FK_BlogPosts_Blogs"
                       FromRole="BlogPosts" ToRole="Blogs" />
   
                       "MyTravelPostModel.FK_Pictures_BlogPosts"
                       FromRole="BlogPosts" ToRole="Pictures" />
   
                       "MyTravelPostModel.BlogComments"
                       FromRole="BlogPosts" ToRole="Comments" />

实体类型的属性可以是基元类型或复杂类型(请参阅图 3),但在 Entity Framework 1.0 中,它们不能是基元或复杂类型的其他实体类型或集合。实体类型的关键字由这些属性的子集组成。导航属性允许导航从一个实体到另一实体的关系。

图 3 解决复杂类型定义

    
              Type="String" MaxLength="50" />
    
              Type="String" MaxLength="50" />
    
              Type="String" MaxLength="50" />
    
              Type="String" MaxLength="50" />
    
              Type="String" MaxLength="50" />
    
              Type="String" MaxLength="50" />

如之前所介绍的,关系可以作为某个关系参与方的各实体类型的导航属性。关系本身是 EDM 中的一等公民并且在 EDM 中明确定义为关联:

   
   
   
      
         
      
      
         
      
   

因此简言之,我们为什么要首先创建一种新的数据建模技术?尽管在 EDM 之前就存在许多数据建模技术或语言,但没有一个能满足Microsoft想要实现的主要目标,也没有一个能使经常使用的实体关系模型变为可执行模型。团队曾研究了大量现有的数据建模技术,但最后发现它们都专门针对或强调特定的问题领域,因此不得不重新开发 EDM 来创建一种新模型,以满足这些目标并能够真正用作开发人员和服务器技术套件的核心数据模型。

为什么使用 XML 来描述 EDM?

经过认真考虑后,XML 被选作 EDM 的第一个序列表示。通过生成 XML 文件(或资源)或从动态生成的 XML表示中加载,开发人员和第三方可以利用经过良好定义的 XML 格式来进行此类格式的转换以及将其加载到实体框架的元数据运行时中。但是,创建 EDM的其他表示也是完全可能的,而且很可能随着产品的发展在未来版本中就会出现其他替代表示。

当前的 EDM 语法是使用产品随附的 XML 架构定义语言 (XSD) 定义的。但是,不应期望大多数人都会采用手动开发 XML的方式,而应认为人们会使用 Visual Studio 中提供的工具。也就是说,团队已知晓大家对特定于域的语言 (DSL) 以及对 EDM模型备用持久性机制(通常针对数据库而言)的关注,并且正在对这些选项做评估以便在即将发行的版本中进行扩展。

谁需要另一种新的查询语言?

有关 EDM 开发的最后一个问题是为什么要创建一种新的查询语言?为什么不使用现有的语言?在稍微深入地研究一个 EDM 后,答案就会变得清晰起来。

到目前为止,我已经介绍了为什么要创建 EDM 以及 EDM中使用的各种构造,还介绍了这样一个事实,即该模型是实体关系模型的后代。为了使创建的模型不但能够清楚地映射到底层数据存储,而且还可以表现开发人员编程时所依据的应用程序级的域模型,EDM需要能够对各种概念(如继承和多态性)进行建模。由于当前的关系查询语言并不支持基于继承、关系导航或多态结果的返回值进行查询,因此需要开发一种新的查询语言来满足此需求。

这就催生了实体 SQL (ESQL),它是一种新的 SQL 语言,其中加入了之前的 SQL 语言并不支持的基于概念的查询功能。ESQL扩展现有 SQL 语言的方式与 EDM 扩展数据库中所使用的关系模型的方式十分类似。此外,ESQL未绑定到任何特定于后台数据库的语法,因此可一次性编写查询(和/或应用程序),无论针对的是哪个后台数据库都无影响。下面我将以一个简单的 ESQL查询为例,说明如何从全部博客中检索至少具有一篇帖子和关联人员(在我的模型中是指博客所有者)的博客:

select c, c.Person 
  from travelEntitiesGeneral.Blogs as c 
  where c.BlogPosts.Count > 0

实现 EDM

ADO.NET Entity Framework 是由 ADO.NET 演变而来的,是 EDM的首个具体实现,可在开发关系数据库时提供较高级别的抽象。在版本 1.0 中,团队一直侧重于构建平台基础,而不仅仅是一个简单的ORM,这将允许开发人员使用非常灵活的映射来处理概念模型或对象模型,并能够适应与底层存储之间存在的巨大分别。

这一高度的灵活性以及与底层存储的巨大分别是允许数据库和应用程序分别发展的关键所在。当数据库架构发生改变时,应用程序由于采用实体框架而不必进行改动,并且您通常不需要重新编写应用程序的内容,只是在必要时更新映射文件以适应此变化即可。

为了开始发展 ADO.NET 平台,需要以现有 ADO.NET 2.0提供程序模型为基础构建实体框架,并对现有提供程序进行适当更新以支持新的实体框架和 ADO.NET 3.5 功能。我们之所以选择在现有ADO.NET 提供程序模型的基础上来实现是为了确保开发社区对提供程序模型不会感到陌生。

体系结构如图 4 所示。您会注意到可接受的架构包括概念架构定义语言 (CSDL)、映射架构语言以及存储架构定义语言 (SSDL)。您还会注意到,实体框架包括了更新后的支持规范命令树 (CCT) 的 SqlClient 数据提供程序。

图 4 ADO.NET Entity Framework 体系结构

EntityClient

然后,实体框架在这些 ADO.NET 3.5 提供程序的基础上引入新的 ADO.NET 提供程序EntityClient。Entity­Client 看上去与之前使用的 ADO.NET提供程序非常类似,它将提供第一个抽象,可允许开发人员使用标准的 Connection、Command 和 DataReader 对象依照 EDM执行查询。它还会将映射域模型所需的客户端视图引擎(根据 EDM 定义的)添加到底层关系数据库架构。必要时,EntityClient 可借助ESQL 查询字符串让开发人员以行和列的形式处理实体,而不必生成类来表示概念架构。

如果看一看图 5 中的 EntityClient,您会注意到我创建了一个包含 ESQL 查询字符串的 EntityCommand,该命令随后会在我的 EDM 中被执行。作为 EntityCommand 的一部分而提供的查询文本被解析并会创建一个 CCT。

图 5 使用 ESQL 执行针对 EntityClient 的查询

using (EntityConnection conn = new 
         EntityConnection("name=travelEntitiesGeneral"))
{
      conn.Open();
      EntityCommand cmd = conn.CreateCommand();
      cmd.CommandText = @"select c.BlogID 
         from travelEntitiesGeneral.Blogs as c 
         where c.BlogPosts.Count > 0";
      EntityDataReader reader = 
         cmd.ExecuteReader(CommandBehavior.SequentialAccess);
      while (reader.Read())
      {
         Console.WriteLine("BlogID = {0}", reader["BlogID"]);
      }
     conn.Close();
}

在这第一个阶段中,命令树仍针对 EDM来表示。客户端视图引擎借鉴数据库系统中的具体化视图理论并将这些理论应用到数据访问层,它在树中应用了一个映射转换,可生成一个在底层逻辑存储模型方面表示相同操作的树,并删除任何非关系概念(如关系、继承和多态性)。这一新转换的树被传给 ADO.NET 3.5提供程序服务,而此服务会返回封装底层存储的本机 SQL 的 DbCommand 命令,然后此命令被执行,其结果通过堆栈向上回传。

在定义客户端视图引擎中所使用的映射以便在 EDM 和逻辑数据库架构之间进行转换时,可采用多种不同的方法。此映射可使用映射规范语言(MSL) 来指定,这是一种声明性的 XML 语法,可通过手动编写 XML 或使用 Visual Studio中包括的实体映射工具进行创建和编辑(请参阅图 6)。

图 6 MSL—EntitySetMapping 示例

   
      
         
         
         
         
         
         
         
                         ColumnName="CountryVisited" />
         
      
   

编译时,MSL 允许实体框架生成必要的查询并更新视图,这些视图随后会在客户端视图引擎中被用来完成从查询(利用 EDM 定义的)到逻辑存储架构的转换。

另一种用于表达映射或部分映射的方法是使用 ESQL 查询。在这种情况下,当开发人员使用 ESQL来表达查询视图时,基础结构会要求他们在映射规范中同时定义伴随的 Create、Update 和 Delete映射。此操作是必需的,因为如果能够在“查询视图”中利用 ESQL功能,从而可以为没有单个有效更新视图的查询定义视图,则映射基础结构将无法为查询生成对应的更新视图。

对象服务

在 EntityClient 提供程序的基础上,实体框架添加了另一组抽象,以便允许针对对象而非 EntityClient返回的非类型化数据记录进行开发。这就是通常被认为是 ORM 的层,它可以生成在数据模型中所定义类型的 CLR 实例并允许开发人员使用 LINQ 或ESQL 查询这些对象。它也恰好是当初众多开发人员在市场中寻找可用的 ORM 技术时最能吸引他们眼球的实体框架层。

正如图 1 所示,对象服务层的高级功能是接受来自应用程序的 ESQL 或 LINQ查询,然后将查询表达式传递给下面的 EntityClient 并返回IEnumerable。但是,经过深入的分析后您会发现,对象服务层的中心是代表应用程序与底层数据存储之间的交互会话的ObjectContext。

ObjectContext 是开发人员在查询、添加和删除其实体实例以及将新状态保存回数据库时用到的主要构造。图 7 显示的是实体的创建以及使用 ObjectContext 对实体执行查询、操作以及 SaveChanges 等动作。此示例使用 ESQL 作为查询语言。

图 7 使用 ObjectContext

using (ObjectContext context = new ObjectContext("name=travelEntities"))
    {
        //--- create a query for customers
        ObjectQuery personQuery = context.CreateQuery(
                     @"select value c from travelEntitiesGeneral.People 
                     as c where c.PersonID == 1");
        //--- by enumerating the query will be implicitly executed
        //--- against the store and you can now work with an
        //--- IEnumerable
        foreach (Person c in personQuery)
        {
            //--- dereference anything you like from Customer
            Console.WriteLine(c.PersonID + ": " + c.Name);
            c.Name = "New Name";
        }
        try
        {
            context.SaveChanges();
        }
        catch (OptimisticConcurrencyException opt)
        {
            // catching this exception allows you to 
            // refresh travelEntities with either store/client wins
            // project the travelEntities into this failed travelEntities.
            var failedEntities = from e3 in opt.StateEntries
                                 select new { e3.Entity };
 
            // Note: in future you should be able to just pass 
            // the opt.StateEntities  
            // in to refresh.
            context.Refresh(RefreshMode.ClientWins, failedEntities.ToList());
            context.SaveChanges();
        }
    } 

如果使用对象服务,则对开发人员而言,跟踪内存中对象所发生的更改的流程以及将这些更改保存回数据库的流程都会得到简化。对象服务使用ObjectStateManager不但会跟踪内存中实例的当前状态,还会跟踪每个实例从存储库中检索出来时的初始状态,从而使实体框架可以在将数据推送回数据库时应用最优的并发操作。通过对 ObjectContext 调用 SaveChanges 方法,可以轻松地保存所跟踪的更改并将其推送回数据存储库。

到目前为止,我一直在讲述有关 ObjectContext 的基本内容并通过一些示例介绍 Object­Context的基本用法,它通常用于有动态工具或应用程序需要使用 EDM 模型的情形。但是,在使用 Visual Studio作为开发环境时,开发人员发现强类型化 ObjectContext 还有一个优点,即可以向可能特定于目标 EDM 的表面功能添加属性和方法。

图 8 显示了使用强类型化 Object­Context 构建的查询。此示例演示了如何使用 LINQ 作为查询语言。使用强类型化ObjectContext 可公开每个 EntitySet 的属性,从而使其更容易被发现;例如,使用travel­Entities.BlogPosts 而非travelEntities.CreateQuery("travelEntitiesGeneral.BlogPosts")。

图 8 使用强类型化 ObjectContext 构建的查询

using (MyTravelPostEntities travelEntities = new MyTravelPostEntities())
{
    // get the latest blog post, with the comments and the people
    // I'm querying for all the blog posts that are related to this blog.
    // I want to include the comments and the people who wrote the
    // comments.
    // I also want only the most recent posting.
    // Note: Since we use the EntityKey that is put on the EntityReference
    // we can either do a tracking query or use span.
    BlogPost post = (from bp in 
        travelEntities.BlogPosts
                              .Include("Comments.Person")
                              .Include("Blog")
                     where bp.Blog.BlogID == requestedBlog.BlogID
                     orderby bp.BlogDate descending
                     select bp).First();
    return post;
} 

LINQ to Entities 是作为对象服务上一个非常薄的层出现的,它在编程语言中提供直接的查询功能(请参阅图 8)而非使用基于字符串的查询。在这种情况下,ObjectQuery 类将实现 Iqueryable,允许它接受 LINQ表达式树,并采用对象服务在将 ESQL 查询传递到 Entity­Client 提供程序时所使用的同样方式在实例框架中推动查询(作为 CCT查询表达式)。

使用实体框架进行 N 层开发

尽管本文的主要目标并非是全面介绍 n 层开发,但作为实体框架开发过程中一个比较有趣的环节,对此还是应该有所提及。在版本 1.0中,实体框架在许多主要方案中都支持 n 层开发。其中包括使用 ADO.NET 数据服务或使用 Windows® CommunicationFoundation (WCF),它们都具有序列化实体以及在 ObjectContext 中附加/分离实体的功能。这些显然并非是实现 n层开发的唯一方法;但是,它们是团队在 V1 中重点关注的解决方案,而且在 V2 和更高的版本中还添加了更多其他方案(如更接近数据集的体验)。图 9 展示了我所讲述的内容。

图 9 N 层应用程序中的 ADO.NET 数据服务

static Uri baseService = new 
   Uri("http://localhost:17338/MyTravelPostService.svc");
MyPeople2Entities context = new MyPeople2Entities(baseService); 
    // get the comment that is being marked for deletion
    // and get the view state blog post.
BlogPost post = (BlogPost)ViewState["BlogPost"];
 
    // move the comment to the deleted comment selection. 
Comment deletedComment = post.Comments[e.RowIndex];
 
    // call the DeleteComment service
context.AttachTo("Comments", deletedComment);
context.DeleteObject(deletedComment);
DataServiceResponse r = context.SaveChanges();
 
    // reload page so that F5, refresh doesn't update all this data.
ReloadPage();

ADO.NET 数据服务是具像状态传输 (REST) 体系结构风格的一个具体实现(每种资源都代表系统中的一个名词 —即一个可以通过统一资源标识符 (URI) 唯一标识的事物),它允许在任意 IQueryable 实现上进行 n 层应用程序的开发。利用ADO.NET 数据服务,除了通过线路查询实例以外,您还可以实现更多功能。ADO.NET 数据服务支持 Create、Read、Update 和Delete 等各种 HTTP 动词,并提供客户端抽象来帮助开发人员实现其解决方案。

n 层方案的第二种方法是使用具有实体框架的 WCF,以便充分利用序列化实体以及在 ObjectContext 中附加/分离实体的能力。图 10 展示了在此方案中如何附加到 ObjectContext。

图 10 附加到 ObjectContext

 
// the creation of the travel MyTravelPostEntities opens the connection 
// and sets up all the metadata information automatically for you.
using (MyTravelPostEntities travelEntities = new MyTravelPostEntities())
{
    // attach the comment and delete.
    travelEntities.Attach(deleteComment);
 
    // call delete on the object
    travelEntities.DeleteObject(deleteComment);
 
    try
    {
       travelEntities.SaveChanges();
    }
    catch (OptimisticConcurrencyException opt)
    {
      // catching this exception allows you to 
      // refresh travelEntities with either store/client wins
      // project the travelEntities into this failed travelEntities.
      var failedEntities = from e3 in opt.StateEntries
                           select new { e3.Entity };
 
     travelEntities.Refresh(RefreshMode.ClientWins, failedEntities.ToList());
     travelEntities.SaveChanges();
    }
}

默认情况下,无论是从 Visual Studio 中的 EDM 生成的还是使用 edmgen.exe(实体框架随附的命令行工具)生成的CLR 类都是 XML 可序列化和二进制可序列化的,并且是导航属性默认设置为 DataMembers 的数据约定,因而可以创建 ASMX Web服务并在视图状态或 WCF 服务中使用实体实例。

与大多数 ORM 类似,实体框架目前并不支持使用数据操作语言 (DML) 进行创建、更新或删除等操作。必须将更改应用到内存中的对象,并且在构建要保持的整个图表时可能需要与数据库进行多次交互。

可用来避免这一过程的一种方法是使用 ObjectContext 提供的附加功能。您可以利用 Attach来通知基础结构,告知它实体已经存在,应该在内存中执行一组操作然后将更改向下推送。如需有关使用实体框架实现 n 层开发的更多信息,请搜索MSDN® 库,因为很快就会向其中添加更多内容。

只是另一个 ORM?

到目前为止,许多人都只是将实体框架看作是市场中的另一个ORM,如果只看本产品的第一个版本,那么有这种想法也不足为奇。但从该角度来看,产品此时所具有的功能已经可以实现 ORM刚刚开始着手解决的一组核心方案。不过,目前的许多分析也指出,实体框架并不能完全涵盖市场中其他一些 ORM 所提供的全部功能,这也的确是实情。

Microsoft 在此领域所做的投资旨在扩展传统 ORM 产品的功能,而实体框架(我将在稍后介绍)正是围绕 EDM所展开的更广泛策略的第一个步骤。正如我在本文开头所提到的,EDM 创建了一个较高级别的域模型,其适用范围远远超出了实体框架和传统的ORM。预计在未来几个版本的 Microsoft .NET Framework、Visual Studio、SQL Server® 以及其他Microsoft 技术中,EDM 的身影会越来越多地出现在其中。

此预期以及 EDM发展的整体愿景已成为最主要的推动力(正如本文所讨论的各种产品决策中所展示的那样)。各种决策在制定时都应尽可能确保能被各种技术(如Reporting Services 和 AnalysisServices)所采用。这将为客户带来极大的好处,因为各种服务都将可以在常见且一致的域模型中提供。

第一个成为现实的愿景是 ADO.NET 数据服务将在 Visual Studio 2008 SP1 中与实体框架一起发布。ADO.NET数据服务为基于 REST 的应用程序提供了值得关注的开发人员体验,它将成为使用 EDM 作为元数据交换格式构建的首个产品(在实体框架以外)。

为配合此次发布行动,Microsoft 在 MIX 2008 中展示了各种与众不同的 Windows Live™ 属性,它们都使用ADO.NET 数据服务协议和 EDM 提供其数据。同样,当我们开始规划下一版本的 SQL Server 和 Visual Studio时,团队将以 EDM 和实体框架为核心,努力提供更好的端对端开发体验。

Elisa Flasko 是 Microsoft 数据可编程性团队的一名项目经理,主要从事 ADO.NET、XML 和 SQL Server 连接性技术的研究。您可以通过她的博客 blogs.msdn.com/elisaj 与其联系。

 

 

 

C# 3.0推出一些新的特性,比如Extension Method, Lambda Expression, Anonymous Type,其实这些新特性都是用来为Linq服务。LinqToSQL将程序员从以往写大量的sql查询语句、取值、赋值中解放出来,在intellisense和编译上做了很多工作,使得这些查询能够在编译期进行检查。同时微软推出ADO.NET Entity Framework,即下一代的ADO.NET。它是比Linq ToSQL更加强大的ORM,让开发人员只需要着眼于领域对象模型的开发,而不需要考虑它们是如何与关系数据库交互。

本文由一个简单的例子进行介绍Entity Framework是如何使用的。在此之前,必须下载ADO.NET Entity Framework runtime 和tools,官方也提供了很多示例下载。下面正式开始ADO.NET Entity Framework之旅(开发工具VS 2008 beta,以Northiwind数据库为例)。

首先建立一个Console project(要选择.NET Framework 3.5),然后点击Add New Item,看见ADO.NET Entity Data Model选项:

然后会出现Wizard,选择数据库,选择表、视图、存储过程等,按照向导一步步来就可以了,在这里只选择Customers和Orders表。在Solution Explore视图里面点击Northwind.edmx,可以看到Entity的信息(看上去很像Linq ToSQL的dbml设计视图),然后将它们改名为Customer和Order。

现在我们就可以进行查询了,先引用命名空间:

using System.Data.Objects;

using NorthwindModel;

首先我们查询所有的Customer,代码和Linq To SQL中的十分相似。

using (NorthwindEntities ctx = new NorthwindEntities())

{

foreach (var customer in ctx.Customers)

{

Console.WriteLine(customer.CustomerID);

}

}

接着来查询某个Customer,

Customer alfki = ctx.Customers.Where("it.CustomerID = 'ALFKI'").First();

"it.CustomerID = 'ALFKI'"表示查询条件语句,该语句看着又像C#代码又像SQL,被成为Entity SQL Language,具体语法在帮助文档上有很详细的Reference。

这里也可以使用Linq进行查询(Linq To Entities),需要引用System.Linq命名空间,似乎Entity Framework不支持Single方法,会抛出异常。

Customer alfki = ctx.Customers.First(it => it.CustomerID == "ALFKI");

或者

Customer alfki = (from c in ctx.Customers

where c.CustomerID == "ALFKIA"

select c).First();

在Entity Framework中进行一对多的查询很简单,但是值得注意的是Entity Framework默认是Lazy Load,即关联数据在需要的时候才加载。

在本例子中,如果直接查询该Customer关联的orders,查询不到结果。

foreach (var order in alfki.Orders)

{

Console.WriteLine(order.OrderID);

}

需要在使用orders之前调用 alfki.Orders.Load(), 再次运行便可以得到正确的结果。

这篇文章主要介绍在ADO.NET实体框架中如何进行查询(以Northwind数据库为例)。

1. 使用EntityCommand进行查询

在实体框架中,我们可以通过EntityCommand进行查询,它的使用方法就像ADO.NET中的SqlCommand。不同的是SqlCommand使用标准SQL语句对数据库进行查询,而EntityCommand使用Entity SQL对EntityContainer进行查询,当然最终实体框架会将Entity SQL转换成标准SQL语句查询数据库。

EntityConnection con = new EntityConnection("Name=NorthwindEntities");

con.Open();

using (EntityCommand cmd =

new EntityCommand("select value c from NorthwindEntities.Customers as c", con))

{

EntityDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);

while (reader.Read())

{

Console.WriteLine("ID [{0}], ContactTitle [{1}]",

reader["CustomerID"], reader["ContactTitle"]);

}

}

首先是建立一个EntityConnection,它接受一个参数,表明使用的是在config文件中的哪个连接字符串。


  

可以看到这个连接字符串和以往ADO.NET中使用的连接字符串并不一样。metadata:指明.csdl/.ssdl/.msl三个文件的路径,这三个文件的作用以后再做说明。provider:表示使用的是SqlClient或者Oledb或者Odbc。provider connection string:这一项便是以往所用的连接字符串。providerName表示现在用的是EntityClient。

接着构造EntityCommand,最后通过EntityDataReader进行数据的读取,值得注意的是这里的EntityCommand接受的是Entity SQL语句,而不是标准SQL语句,具体的Entity SQL语法可以参考帮助文档。

2. 使用ObjectQuery进行查询

实体框架提供一个名为ObjectQuery的类,它让开发人员通过Entity SQL进行查询,查询结果以对象的形式供使用。

using (NorthwindEntities ctx = new NorthwindEntities())

{

ObjectQuery query

= ctx.CreateQuery("NorthwindEntities.Customers");

ObjectResult result = query.Execute(MergeOption.NoTracking);

foreach (Customer c in result)

{

Console.WriteLine("ID [{0}], ContactTitle [{1}]",

c.CustomerID, c.ContactTitle);

}

}

首先调用CreateQuery方法来创建ObjectQuery对象(当然这里也可以使用new,只是传进的参数不同而已),它接受EntitySQL语句作为参数。然后调用Execute方法进行查询,它接受MergeOption枚举型的参数,表示解决冲突的方法。查询结果以对象的形式(这里是Customer)保存在ObjectResult中。

下面是使用new的写法:

using (NorthwindEntities ctx = new NorthwindEntities())

{

ObjectQuery query = new ObjectQuery("Customers", ctx);

foreach (Customer c in query)

{

Console.WriteLine("ID [{0}], ContactTitle [{1}]",

c.CustomerID, c.ContactTitle);

}

}

3. ADO.NET Entity Framework Tool自动生成Entities和各个对象的代码,帮助开发人员减少了很多体力活。这样,我们可以简单写成:

using (NorthwindEntities ctx = new NorthwindEntities())

{

foreach (Customer c in ctx.Customers)

{

Console.WriteLine("ID [{0}], ContactTitle [{1}]",

c.CustomerID, c.ContactTitle);

}

}

其实这里,也是使用ObjectQuery来进行查询。当然,可以给查询加上更多的条件,在上一篇文章中也有说明这里就不写了。

值得关注的是:自动生成的实体类中有这样一个方法,比如Customer:

public static Customer CreateCustomer(string customerID, string companyName)

{

Customer customer = new Customer();

customer.CustomerID = customerID;

customer.CompanyName = companyName;

return customer;

}

并不像以前一样,提供带参数的构造函数,而是提供CreateCustomer的静态方法来构造Customer实例。这似乎和前一段时候贫血充血的问题有关了,实体对象该不该有行为,是贫血还是充血?虽然只是一个方法,不过相信这也表明了微软的态度吧。

ADO.NET Entity Framework 入门示例向导(附Demo程序下载)

ADO.NET Entity Framework 是.Net Framework 3.5 SP1引入的实体框架,它使开发人员可以通过对象模型(而不是逻辑/关系数据模型)专注于数据。实体框架EntityFramework使用概念层、映射层和逻辑层将逻辑数据库结构抽象化。

实体数据模型(Entity Data Model)

实体框架的核心位于其模型中。实体框架支持表示数据库中的关系架构的逻辑存储模型。关系数据库通常存储数据的方式与应用程序使用数据的方式不同。通常,这会迫使开发人员按照数据库包含数据的结构检索数据。因此,开发人员通常将数据加载到更适合处理业务规则的业务实体中。在本示例中,以逻辑模型表示关系数据库的构架,业务实体表示概念模型。实体框架使用映射层在模型之间搭建了桥梁。因此,实体框架的模型中有三个处于活动状态的层:

1.    概念层(Conceptual layer) - 表示数据的概念模型,包括实体和相互关系。

2.    逻辑层(Logical layer)- 描述数据在数据库的存储模型。

3.    映射层(Mapping layer) - 在概念层和逻辑层模型之间建立映射。

这三层允许将数据从关系数据库映射到更加面向对象的业务模型。实体框架提供了使用 XML 文件定义这些层的方法。它还基于概念模型的架构生成了一系列类。可以针对这些类进行编程以直接与数据交互。这提供了抽象级别,因此开发人员可以针对概念模型而不是关系模型进行编程。

下面演示如何使用Entity Framework 构建示例程序。

首先,使用Entity Data Model Wizard创建如下NorthwindDB.edml文件。

概念模型和逻辑模型视图:

本示例程序采用Northwind 示例数据库,下面开始编写代码对Customers表进行增、删、改、查等等操作。

1.新增Customers记录

using (NorthwindEntities myDb = new NorthwindEntities())

            {

Customers customer = new Customers();

Random rm = new Random();

                customer.CustomerID = "A" + rm.Next(9999).ToString();

                customer.CompanyName = "EntLib.com Forum";

                customer.Address = "http://www.EntLib.com";

                myDb.AddToCustomers(customer);

int count = myDb.SaveChanges();

                txtCustomerID.Text = customer.CustomerID;

            }

2.更新Customers记录

using (NorthwindEntities myDb = new NorthwindEntities())

            {

var query = from customer in myDb.Customers

where customer.CustomerID == txtCustomerID.Text.Trim()

select customer;

foreach (var row in query)

                {

                    row.CompanyName = "Updated Company Name";

                }

                myDb.SaveChanges();

            }

3.删除Customers记录

using (NorthwindEntities myDb = new NorthwindEntities())

            {

var query = from customer in myDb.Customers

where customer.CustomerID == txtCustomerID.Text.Trim()

select customer;

foreach (var row in query)

                {

                    myDb.DeleteObject(row);

                }

                myDb.SaveChanges();

            }

4.查询Customers,返回所有记录

using (NorthwindEntities myDb = new NorthwindEntities())

            {

                dataGridView1.DataSource = myDb.Customers;

            }