Entity Framework 5.0系列之EF概览

概述

在开发面向数据的软件时我们常常为了解决业务问题实体、关系和逻辑构建模型而费尽心机,ORM的产生为我们提供了一种优雅的解决方案。ADO.NET Entity Framework是.NET开发中一种由ADO.NET驱动的ORM框架,使用Entity Framework开发人员可以不必考虑数据的基础数据表和列,在处理数据时能够以更高的抽象级别进行工作,并能够以相对传统开发编写更少的代码来创建和维护应用程序。

我们知道面向对象的编程与数据存储系统的交换提出了一个难题:类结构通常同关系数据表组织结构相近但又不同。例如数据中可能使用一个外键表示一个实体与另一个实体的关系,但是对于类而言我们通知会在类中定义一个属性来描述这种关系。对于这个问题当前ORM框架一般选择通过将面向对象的类和属性映射到关系表和列来弥补这种不足。但是Entity Framework并没有采取这种方式而是将逻辑模型中的表、列和外键约束映射到概念模型中的实体和关系,实体数据模型工具再基于概念模型生成可扩展的数据类。这些类派生自基类,而基类提供服务以将实体具体化为对象并进行跟踪和保存。这样一来开发人员不仅可以很方便的对数据类进行扩展而且可以像处理关联对象一样处理实体和关系。

三种编程方式

Entity Framework4.1之前EF支持“Database First”和“Model First”编程方式,从EF4.1开始EF开始支持支持“Code First”编程方式,今天简单看一下EF三种编程方式。

开始介绍这三种EF操作方式之前,首先在Visual Studio 2013中建立一个数据库连接,这里我们以“AdventureWorks”数据库为例:

Database First

“Database First”模式我们称之为“数据库优先”,前提是你的应用已经有相应的数据库,你可以使用EF设计工具根据数据库生成数据数据类,你可以使用Visual Studio模型设计器修改这些模型之间对应关系。

首先创建一个控制台应用程序,然后右键添加新建项,选择“ADO.NET Entity Data Model”,名称输入AdventureWorksContext:

接着选择从数据库生成:

下一步,选择数据库连接“SQL2008.AdventureWorks.dbo”:

点击下一步,然后选择“Person”和“PersonPhone”表:

创建完模型之后,你会发现Visual Studio自动为你生成了“Perrson”、“PersonPhone”两个实体类和一个“AdventureWorksContext”数据库上下文操作类:

下面简单的看一下如何使用EF进行数据查询,通过下面的代码我们可以看到EF对于数据的操作入多么优雅:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DatabaseFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new AdventureWorksEntities())
            {
                IQueryable<Person> persons = from people in db.People
                                             where people.LastName == "Sánchez"
                                             select people;
                foreach (Person p in persons)
                {
                    Console.WriteLine("first name is :{0}", p.FirstName);
                    if (p.FirstName == "Angela")
                    {
                        p.AdditionalContactInfo = "other info";
                    }
                }

                var persons2 = from people in db.People
                               where people.LastName == "Sánchez" && people.FirstName == "Angela"
                               select people;
                if (persons2.Count() > 0)
                {
                    Console.WriteLine("additional contact information is :{0}", persons2.First().AdditionalContactInfo);
                }
                //上面虽然可以查出来AddtionalContactInfo,但是实际省并未保存到数据库,具体保存方法在此不再详细描述
            }
        }
    }
}

执行结果如下图:

注意:如果你的数据库表结构发生改变后,只需在模型设计视图空白处右键,选择“从数据库更新模型”接着按照向导操作即可。

Model First

Model First我们称之为“模型优先”,这里的模型指的是“ADO.NET Entity Framework Data Model”,此时你的应用并没有设计相关数据库,在Visual Studio中我们通过设计对于的数据模型来生成数据库和数据类。

首先创建一个控制台应用程序,右键添加新建项,选择“ADO.NET Entity Data Model”,名称输入AdventureWorksContext:

接着选择空模型:

在模型设计视图中,添加新实体:

输入实体对应的属性:

添加,两个Scalar属性:“Customer”和“OrderDate”;同样的方式添加第二个实体“OrderDetail”,并添加“Product”和“UnitPrice”属性:

接下来我们添加二者之间的关系,“Order”和“OrderDetail”是一对多的关系,“Order”可以通过“OrderDetails”属性访问“OrderDetail”实体,“OrderDetail”可以通过“Order”属性访问“Order”实体,并且添加了一个外键约束到“OrderDetail”中:

添加过关系后:



到目前为止Model First中的Model已经创建结束,下面就需要生成到数据库了,在模型设计视图空白处选择“从模型生成到数据库…”:

选择数据库连接,点击下一步,你将会看到生成的sql语句:

点击完成,不出意外的话将打开生成的脚本,当然你也可能会出现如下错误,请下载最新的SQL Server Data Tool(我本地VS2012,数据库SQLServer2008R2出现了下面的提示,下载更新即可,建议直接下载镜像文件):

此时生成了数据库上下文和实体类,并且打开了建表的脚本:

打开的数据库脚本:

然后右键选择执行即可。

然后编码查询一下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ModelFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            using(var dbContext=new AdventureWorksContextContainer())
            {
                var o = new Order();
                o.OrderDate = DateTime.Now;
                ctx.Orders.Add(o);
                ctx.SaveChanges();

                var orders = from od in dbContext.Orders
                            select od;

                foreach (Order order2 in orders)
                {
                    Console.WriteLine("OrderID:{0},OrderDate:{1}",order2.Id,order2.OrderDate);
                }

                Console.Read();
            }
        }
    }
}

查询结果:

注意:如果我们的模型发生改变,只需要在模型设计视图修改模型,让后保存此时实体类就会相应改变,然后选择“从模型生成到数据库”重新执行生成的脚本即可。

Code First

Code First模式我们称之为“代码优先”模式,是从EF4.1开始新建加入的功能。使用Code First模式进行EF开发时开发人员只需要编写对应的数据类(其实就是领域模型的实现过程),然后自动生成数据库。这样设计的好处在于我们可以针对概念模型进行所有数据操作而不必关系数据的存储关系,使我们可以更加自然的采用面向对象的方式进行面向数据的应用程序开发。

从某种角度来看,其实“Code First”和“Model First”区别并不是太明显,只是它不借助于实体数据模型设计器,而是直接通过编码(数据类)方式设计实体模型(这也是为什么最开始“Code First”被叫做“Code Only”的原因)。但是对于EF它的处理过程有所差别,例如我们使用Code First就不再需要EDM文件,所有的映射通过“数据注释”和“fluent API”进行映射和配置。另外需要注意的是“Code First”并不代表一定就必须通过数据类来定义模型,事实上也可以通过现有数据库生成数据类。

那么我们首先看一下传统的Code First如何使用。

首先创建一个控制台应用程序,接下来添加两个类“Order”和“OrderDetail”,我们可以看到这两个类只是简单的C#对象(POCO,Plain Old C# Object)这两个类基本和EF没有任何关系,需要注意的是这两个类有两个导航属性“Order.OrderDetails”和“OrderDetail.Order”:

using System;
using System.Collections.Generic;

namespace CodeFirst
{
    public class Order
    {
        public int Id { get; set; }
        public string Customer { get; set; }
        public System.DateTime OrderDate { get; set; }

        public virtual List<OrderDetail> OrderDetails { get; set; }
    }
}
using System;
using System.Collections.Generic;

namespace CodeFirst
{
    public partial class OrderDetail
    {
        public int Id { get; set; }
        public string Product { get; set; }
        public string UnitPrice { get; set; }
        public int OrderId { get; set; }

        public virtual Order Order { get; set; }
    }
}

 

有了这两个类之后让我们定义一个数据库上下文,有了它我们就可以对数据进行增删改查操作了,这个类必须继承于"System.Data.Entity.DbContext”类以赋予它数据操作能力。因此接下来我们需要给这个应用安装EntityFramework包,因为到目前为止我们并没有引入EF框架相关的任何内容,我们需要引入EF相关程序集。但是我们有更好的选择那就是NuGet。通过NuGet进行在线安装:项目中右键选择"Manage NuGet Packages…”;选择Online;再选择“EntityFramework”;然后点击安装即可。不了解NuGet的朋友到这里看一下使用 NuGet 管理项目库。数据库上下文操作类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;

namespace CodeFirst
{
    public class OrderContext:DbContext
    {

        public DbSet<Order> Orders
        {
            get;
            set;
        }

        public DbSet<OrderDetail> OrderDetails
        {
            get;
            set;
        }
    }
}

然后我们进行测试,在这个类中我们首先创建了一个Order实例,接着编码查询:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CodeFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new OrderContext())
            {

                var o = new Order();
                o.OrderDate = DateTime.Now;
                ctx.Orders.Add(o);
                ctx.SaveChanges();

                var query = from order in ctx.Orders
                            select order;
                foreach (var q in query)
                {
                    Console.WriteLine("OrderId:{0},OrderDate:{1}", q.Id, q.OrderDate);
                }

                Console.Read();
            }
        }
    }
}

查询结果如图:

如果是第一次使用EF Code First的朋友一定会有疑问,我们没有进行任何数据库配置,增加了一条数据通过查询确实保存上了,那么我们的数据到底在哪呢?事实上如果用户不进行数据库配置EF默认会使用“.\SQLEXPRESS”数据库实例,如果你没有安装“.\SQLEXPRESS”则默认使用LocalDb,关于LocalDb的具体细节请看:SQL Server 2012 Express LocalDB,例如我本机就存放在:“C:\Users\Kenshin”,可以看到创建了一个名为“CodeFirst.OrderContext”的数据库:

但是多数情况下我们是希望自己控制这个数据库的,例如我想让他保存在我机器上的”.\SQL2008”实例上,此时我们就需要在配置文件App.Config中配置一个数据库连接串,然后在我们的数据库上下文中指定这个连接名称。

配置文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="CodeFirstDb" connectionString="Data Source=.\SQL2008;Database=CodeFirstDb;UID=sa;PWD=123;" providerName="System.Data.SqlClient"></add>
  </connectionStrings>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
  </entityFramework>
</configuration>

OrderContext类,构造函数多了一个连接名参数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;

namespace CodeFirst
{
    public class OrderContext:DbContext
    {
        public OrderContext(string connectionName)
            : base(connectionName)
        {
        }

        public DbSet<Order> Orders
        {
            get;
            set;
        }

        public DbSet<OrderDetail> OrderDetails
        {
            get;
            set;
        }
    }
}

使用的时候,传入配置的数据库连接字符串名称:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CodeFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new OrderContext("CodeFirstDb"))
            {

                var o = new Order();
                o.OrderDate = DateTime.Now;
                ctx.Orders.Add(o);
                ctx.SaveChanges();

                var query = from order in ctx.Orders
                            select order;
                foreach (var q in query)
                {
                    Console.WriteLine("OrderId:{0},OrderDate:{1}", q.Id, q.OrderDate);
                }

                Console.Read();
            }
        }
    }
}

 

执行之后就会发现在”.\SQL2008”实例上多了一个“CodeFirstDb”数据库(注意图中除了我们创建的两个实体表还有一个系统表dbo._MigrationHistory它记录了模型的定义,在以后的文章中我会着重解释此表):

到了这里我们三种EF编程方式已经全部介绍完了,关于Code First模式如何更新模型、如何先建数据库再生成数据类以及更多EF问题在接下来的文章中再探讨。

时间: 2024-11-03 03:09:04

Entity Framework 5.0系列之EF概览的相关文章

Entity Framework 5.0系列之Code First数据库迁移

我们知道无论是"Database First"还是"Model First"当模型发生改变了都可以通过Visual Studio设计视图进行更新,那么对于Code First如何更新已有的模型呢?今天我们简单介绍一下Entity Framework的数据迁移功能. Entity Framework配置 在开始今天的话题之前先来看一下Entity Framework的配置,因为有很多朋友因为配置文件的问题造成"Migrations"命令执行失败.

Entity Framework 5.0系列之数据操作

Entity Framework将概念模型中定义的实体和关系映射到数据源,利用实体框架可以将数据源返回的数据具体化为对象:跟踪对象所做的更改:并发处理:将对象更改传播到数据源等.今天我们就一起讨论如何利用Entity Framework进行查询.插入.更新和删除数据. 查询 我们将使用AdventureWorks数据库来进行今天的所有演示,因此开始之前请准备好相应的数据库.在EF中进行查询应该说是相当简单,只需要定义一个类继承于"DbContext",然后定义对应的"DbSe

Entity Framework 5.0系列之自动生成Code First代码

在前面的文章中我们提到Entity Framework的"Code First"模式也同样可以基于现有数据库进行开发.今天就让我们一起看一下使用Entity Framework Power Tools如何基于现有数据库生成数据类和数据库上下等. Entity Framework Power Tools 基于现有数据库生成POCO数据类和数据库上下文需要借助Visual Studio一个扩展插件-- Entity Framework Power Tools(一个Code First反向工

Entity Framework 5.0系列之约定配置

Code First之所以能够让开发人员以一种更加高效.灵活的方式进行数据操作有一个重要的原因在于它的约定配置.现在软件开发越来复杂,大家也都试图将软件设计的越来越灵活,很多内容我们都希望是可配置的,但是过多的配置也会带来很大的工作量,解决这个问题的方法就是约定.对于一些简单的,不太可能经常变化的内容我们以一种约定的方式进行设计.使用过其他ORM框架的朋友可能知道一般ORM都有对应的映射配置文件(一般是一个Xml文件),但是EF并没有.在EF中是以一种约定的方式进行表.列同实体类进行映射的,与此

Entity Framework 5.0基础系列

原文:Entity Framework 5.0基础系列 1.Entity Framework简介 http://www.cnblogs.com/aehyok/p/3315991.html 2.Entity Framework DBFirst尝试http://www.cnblogs.com/aehyok/p/3318892.html 3.Entity Framework ModelFirst尝试http://www.cnblogs.com/aehyok/p/3323438.html 4.Entit

Entity Framework 4.0并发处理的步骤

新用到了实体框架,用到了并发,参考以下的图书资料 Entity Framework 4.0 Recipes A Problem-Solution Approach ISBN-13 (pbk): 978-1-4302-2703-8 ISBN-13 (electronic): 978-1-4302-2704-5 http://apress.com/book/view/1430227036 具体的步骤如下: 建立模型 添加Ado.net Entity Model后类似如下添加TimeStamp并发控制

Entity Framework学习中级篇1—EF支持复杂类型的实现

本节,将介绍如何手动构造复杂类型(ComplexType)以及复杂类型的简单操作. 通常,复杂类型是指那些由几个简单的类型组合而成的类型.比如:一张Customer表,其中有FristName和LastName字段,那么对应的Customer实体类将会有FristName和LastName这两个属性.当我们想把FirstName和LastName合成一个名为CustomerName属性时,此时,如果要在EF中实现这个目的,那么我们就需要用到复杂类型. 目前,由于EF不能显示支持复杂类型,所以我们

Entity Framework 4.0新增对T-SQL的支持

EF4.0中新增了ExecuteStoreQuery,ExecuteStoreCommand,ExecuteFunction等方法,完美支持T- SQL 之前在EF中用Linq进行left join查询和报表数据查询简直是一场噩梦,以下代码就是例子: Linq左连接查询N张表示例代码 1 public List<Info> getMapUnitList(int MapId) 2 { 3 var var1 = from mu in epm.MapUnit 4 join us in epm.Uni

Entity Framework 5.0 调用存储过程 返回值为什么是int类型

问题描述 用VS2012+EntityFramework5.0生成代码但是生成的存储过程返回值有问题为什么生成的存储过程返回值为int类型??存储过程中操作复杂最后返回几个字段值.publicvirtualintGetMenuByRID(Nullable<int>rID){varrIDParameter=rID.HasValue?newObjectParameter("RID",rID):newObjectParameter("RID",typeof(i