8天掌握EF的Code First开发系列之2 简单的CRUD操作

本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来。

本篇目录

  • 创建控制台项目
  • 根据.Net中的类来创建数据库
  • 简单的CRUD操作
  • 数据库模式更改介绍
  • 本章小结

本人的实验环境是VS 2012,windows 7,MSSQL Server 2008 R2。

创建控制台项目

1. 新建控制台应用项目

2. 通过NuGet安装Entity Framework 6

根据.Net中的类来创建数据库

上面的步骤之后,我们就可以开始写代码了。在写代码之前,你要始终记得,每个类就是相应的数据表中的一行数据,该类的属性对应的是这行数据的列。为了表示赞助者们对我的支持,我决定用他们的数据进行举例。

1. 创建实体数据模型

我们现在创建一个捐赠者类Donator:

namespace EFCodeFirst
{
    public class Donator
    {
        public int DonatorId { get; set; }

        public string Name { get; set; }

        public decimal Amount { get; set; }

        public DateTime DonatorDate { get; set; }
    }
}

我们需要定义和期望的数据库类型相匹配的属性。上面的例子中,.Net中的int类型会映射到SQL Server中的int类型,string类型会映射到Nvarchar(max)类型,decimal和Datetime也和SQL Server中的一样。大多数时候,我们不需要关心这些细节,我们只需要编写能够表示数据的模型类就行了,然后使用标准的.Net类型定义属性,其他的就让EF自己计算出保存数据所需要的RDBMS类型。

2. 创建数据库上下文

接下来我们创建数据库上下文,它是数据库的抽象。目前,我们只有一张表Donator,因而要给该数据库上下文定义一个属性来代表这张表。再者,一张表中一般肯定不止一条数据行,所以我们必须定义一个集合属性,EF使用DbSet来实现这个目的。

namespace EFCodeFirst
{
    public class Context : DbContext
    {
        public Context()
            :base("name = EFCodeFirst")
        {

        }

        public DbSet<Donator> Donators { get; set; }
    }
}

在这里,DbContext 是所有基于 EF 的上下文基类,通过它可以访问到数据库中的所有表。上面的代码中调用了父类的构造函数,并且传入了一个键值对,键是 name ,值是 EFCodeFrist ,这个键值对是定义在应用程序的配置文件中的,取决于你的应用程序类型,可能是 app.config 或者 web.config 。在我们的控制台应用程序中就是 app.config。

在 app.config 文件的 configuration 的节点下(不要在第一个节点下,否则报错)添加:

 <connectionStrings>
    <add name="EFCodeFirst"
         connectionString="server=DTSZOPAULHUANG\SQLEXPRESS;Database=EFCodeFirst;Integrated Security=SSPI"
         providerName="System.Data.SqlClient"/>
  </connectionStrings>

接下来就应该是创建数据库了,创建数据库的方式有两种:

  1. 在后面的博客中我们会通过数据库迁移来实现数据库的创建,原理就是数据库会在第一次查询,更新或插入操作时创建。
  2. 通过EF数据库的API来创建。

这里我们先通过第二种方法来创建数据库。首先我们必须确保数据库中没有和我们现在要创建的数据库同名的数据库存在,否则会提示错误。当然,我们也可以通过EF的API访问数据库来创建数据库。

namespace EFCodeFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new Context())
            {
                context.Database.CreateIfNotExists();
            }

            Console.WriteLine("Database 创建成功");

            Console.ReadKey();
        }
    }
}

最后,我们只需要确保我们的连接字符串没有问题。现在,运行程序,打开SSMS(当然也可以在VS的服务器资源管理器查看)进行确认即可。

可以很清楚地看到,数据表名是自定义数据库上下文的属性,而表中的列是数据模型的属性。此外,注意一下列的类型。EF默认将DonatorId作为了主键,string类型的Name在数据库中的类型是nvarchar(max),这些都是在使用EF时必须注意的命名规范(或者约定)。

简单的CRUD操作

首先,大脑中时刻要有这张图,这张图也是很重要的概念:

1. 创建记录——Create

你可以这样认为,将对象添加到集合中就相当于将数据行插入到数据库的相应的表中。我们使用 DbSet 的 Add 方法来实现新数据的添加,而 DbContext 类的 SaveChanges 方法会将未处理的更改提交到数据库,这是通过检测上下文中所有的对象的状态来完成的。所有的对象都驻留在上下文类的 DbSet 属性中,比如,我们的例子只有一个 Donators 属性,那么所有的捐赠人的数据都会存储到这个泛型集合属性中。数据库上下文会跟踪 DbSet 属性中的所有对象的状态,这些状态有这么几种:DeletedAddedModified 和 Unchanged 。如果你想在一个表中插入多行数据,那么只需要添加该表对应的类的多个对象的实例即可,然后就使用 SaveChanges 方法将更改提交到数据库,该方法是以单事务执行的。最终,所有的数据库更改都会以单个工作单元持久化。既然是事务的,那么这就允许将批量相关的更改作为单个操作提交,这样就保证了事务一致性和数据完整性。

修改Main方法如下:

    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new Context())
            {
                context.Database.CreateIfNotExists();

                var donators = new List<Donator>
                {
                    new Donator { Name   = "陈志康", Amount = 50, DonatorDate = new DateTime(2016, 4, 7) },
                    new Donator { Name = "海风",  Amount = 5, DonatorDate = new DateTime(2016, 4, 8) },
                    new Donator { Name = "醉千秋", Amount = 18.8m, DonatorDate = new DateTime(2016, 4, 15) }
                };

                context.Donators.AddRange(donators);
                context.SaveChanges();

            }

            Console.ReadKey();
        }
    }

这里需要注意两点:

  1. 不需要给 DonatorId 属性赋值,因为它对应到 SQL Server 表中的主键列,它的值是自动生成的,当 SaveChanges 执行之后,打个断点就能看到返回的 DonatorId 已经有值了。
  2. Context 的实例用了 using 语句包装起来,这是因为 DbContext 实现了 IDisposabl e接口。Dbcontext 还包含了 DbConnection 的实例,该实例指向了具有特定连接字符串的数据库。在EF中合适地释放数据库连接和 ADO.NET 中同等重要。

执行结果与在VS中查看数据是否生成:

2. 查询记录——Retrieve

查询时也是直接通过DbSet进行查询的。

        private static void Search()
        {
            using (var context = new Context())
            {
                var donators = context.Donators;

                Console.WriteLine("Id\t\t姓名\t\t金额\t\t赞助日期");

                foreach (var donator in donators)
                {
                    Console.WriteLine("{0}\t\t{1}\t\t{2}\t\t{3}",
                        donator.DonatorId, donator.Name, donator.Amount, donator.DonatorDate.ToShortDateString());
                }
            }
        }

如果像下面那样打一个断点,你会看到一个结果视图,点击那个类似刷新的图标会看到查询的结果,这个东西道出了 EF 中很重要的一个概念: 延迟查询。但是此时还没有真正查询数据库,只有当LINQ的查询结果被访问或者枚举时才会将查询命令发送到数据库。EF 是基于 Dbset 实现的 IQueryable 接口来处理延迟查询的。

最后,我使用了一个foreach循环将结果枚举出来,这样就执行了SQL,结果如下:

3. 更新记录——Update

在 SQL 中,更新需要使用 Update 命令。而在 EF 中,我们要找到 DbSet 集合中要更新的对象,然后更改其属性,最后调用 SaveChanges 方法即可。下面将赞助者的名称进行修改。

        private static void Update()
        {
            using (var context = new Context())
            {
                var donator = context.Donators.FirstOrDefault(item => item.Name.Equals("醉千秋"));

                if (donator != null)
                {
                    donator.Name = "醉、千秋";

                    context.SaveChanges();
                }
            }
        }

这里我们使用了 FirstOrDefault() 扩展方法来判断序列中是否存在特定的元素,如果存在则修改目标对象的 Name 属性,之后调用 SaveChanges 方法。当然这里也可以使用 First、Single 或者 SingleOrDefault 方法都可以。这四个方法之间的区别请参考这里

最后检测数据库操作成功的结果:

4. 删除记录——Delete

接下来要删除记录,先把剩下的打赏者数据全部放到数据库,然后再在最后加入一条测试数据,如下:

INSERT dbo.Donators VALUES  ( N‘雪茄‘, 10, ‘2016-04-08‘)
INSERT dbo.Donators VALUES  ( N‘王小乙‘, 10, ‘2016-04-09‘)
INSERT dbo.Donators VALUES  ( N‘键盘里的鼠标‘, 12, ‘2016-04-13‘)
INSERT dbo.Donators VALUES  ( N‘smallpig‘, 10, ‘2016-04-13‘)
INSERT dbo.Donators VALUES  ( N‘Darren‘, 5, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘jeffrey‘, 10, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘危杨益‘, 6.66, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘Mr.Lan‘, 10, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘周旭龙‘, 5, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘403‘, 10.24, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘cuibty‘, 8.88, ‘2016-04-15‘)
INSERT dbo.Donators VALUES  ( N‘dennylo‘, 10.24, ‘2016-04-17‘)
INSERT dbo.Donators VALUES  ( N‘lee‘, 5, ‘2016-04-18‘)
INSERT dbo.Donators VALUES  ( N‘利平‘, 18.8, ‘2016-04-18‘)
INSERT dbo.Donators VALUES  ( N‘听海船说‘, 20, ‘2016-04-19‘)
INSERT dbo.Donators VALUES  ( N‘喝前摇一摇‘, 5, ‘2016-04-19‘)
INSERT dbo.Donators VALUES  ( N‘黄大仙‘, 50, ‘2016-04-19‘)
INSERT dbo.Donators VALUES  ( N‘夜未眠‘, 10, ‘2016-04-19‘)
INSERT dbo.Donators VALUES  ( N‘A.L‘, 8.88, ‘2016-04-19‘)
INSERT dbo.Donators VALUES  ( N‘transient‘, 5, ‘2016-04-19‘)
INSERT dbo.Donators VALUES  ( N‘晓东‘, 6.66, ‘2016-04-20‘)
INSERT dbo.Donators VALUES  ( N‘待打赏‘, 10, ‘2016-04-20‘)

要删除一条数据,就先要找到这条数据,删除代码如下:

        private static void Delete()
        {
            using (var context = new Context())
            {
                //根据Name找到要删除的测试数据
                var toBeDeletedDonator = context.Donators.Single(d => d.Name == "待打赏");

                if (toBeDeletedDonator != null)
                {
                    //如果满足条件,就将该对象使用Remove方法标记为Deleted
                    context.Donators.Remove(toBeDeletedDonator);
                    //最后持久化到数据库
                    context.SaveChanges();

                }
            }
        }

注意:上面使用了 First 方法来查找待删除的数据,但是如果在数据库中没有满足条件的记录存在,则会抛出异常,所以使用 First 方法时要注意。

数据库模式更改介绍

如果你修改了Donator类或者又添加了新的DbSet属性(即添加了新表),在操作的过程中你可能会遇到一些异常。现在,我想再添加一张表 PayWays ,用来存储打赏者的打赏方式,比如微信,支付宝,QQ红包等。

定义 PayWay 类, 包含两个属性,以后可能会增加属性和 Donator 表关联:

    public class PayWay
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }

修改 Context 类,使之包含 PayWays 数据集合。

 public class Context : DbContext
    {
        public Context()
            :base("name = EFCodeFirst")
        {

        }

        public DbSet<Donator> Donators { get; set; }

        public DbSet<PayWay> PayWays { get; set; }
    }

我们这里更加明显地可以看到,数据库上下文代表了整个数据库,它包含多个表,每张表都成为了Context类的一个属性。

现在,如果我们运行程序,并循环枚举支付方式会出现什么情况呢?

意思是:自从数据库创建以来,模型背后的数据库上下文‘Context’已经发生了变化,也就是当初的数据库和现在的上下文对不上了。呵呵!当然对不上了,数据库上下文被我更改了,而数据库没改啊!

在后面我会介绍数据库迁移和对已存在的数据库进行迁移,但是我们现在也要解决这个当前问题。这就引入了初始化器(Initializer的概念。初始化器会在首次实例化过程期间或者EF首次访问数据库时运行。EF中需要关心的初始化器有三种:

  • CreateDatabaseIfNotExists<TContext>
  • DropCreateDatabaseIfModelChanges<TContext>
  • DropCreateDatabaseAlways<TContext>

这三种初始化器看其名字都很好理解,CreateDatabaseIfNotExists<TContext> 指如果数据库不存在则创建,DropCreateDatabaseIfModelChanges<TContext> 指如果模型改变了(包括模型类的更改以及上下文中集合属性的添加和移除)就销毁之前的数据库再创建数据库,DropCreateDatabaseAlways<TContext> 总是销毁再重新创建数据库,如果没有指定的话默认使用第一个初始化器。
现在我们使用第二个初始化器,要使用该初始化器,我们应该在实例化数据库上下文之前就要让 EF 知道。我们可以使用 EF 的 API 中 Database 类的 SetInitialize r静态方法。在我们的控制台应用中,我们应该将它放在Main方法的第一行:

        static void Main(string[] args)
        {
            using (var context = new Context())
            {
                Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context>());

                var payWays = context.PayWays;

                Console.WriteLine("姓名\t\t");

                foreach (var payWay in payWays)
                {
                    Console.WriteLine("{0}\t\t", payWay.Name);
                }
            }

            Console.ReadKey();
        }

运行程序,但是很不幸,还是有异常抛出。

这是因为数据库在其他的应用程序中是打开的,如SQL Server Management Studio。此时,只要关闭其他的应用程序就可以了。关闭 SQL Server Management Studio 再运行程序。

程序运行正确且数据库结构发生了变化

需要注意的是,因为上面的初始化器会销毁之前的数据库,因此之前累积的所有数据也都丢失了。很显然,这种用法不适合生产环境,但是我们学习EF或者项目早期是很方便的。另外一个有趣的功能是,初始化器允许我们在目标数据库创建之后运行其他代码,可以通过重写Seed方法即可。该方法需要一个数据库上下文的实例参数:

Database.SetInitializer(new DatabaseInitializer());
public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<Context>
    {
        protected override void Seed(Context context)
        {
            context.PayWays.AddRange(new List<PayWay>
            {
                new PayWay{Name = "支付宝"},
                new PayWay{Name = "微信"},
                new PayWay{Name = "QQ红包"}
            });

        }
    }

可以看到,在Seed方法中,我们不需要调用SaveChanges方法。此外,要更新生产数据库,我们可以使用EF Migrations。运行后,Donators表中的数据都销毁了,而Seed方法给PayWays表中添加了数据。

本章小结

这篇博客中,我们创建了第一个基于 EF Code First 的控制台应用程序。我们使用 Nuget 添加了 EF 的引用。然后我们确定要将赞助楼主的数据存储在数据库中,然后创建了一个 Donator 类映射到数据库中的 Donators 表。然后创建了数据库抽象 Context 类,它继承自 DbContext ,并在它的构造函数中指定了想要的连接字符串,同时把该连接字符串添加到应用的配置文件中。然后,我们给数据库上下文添加了一个属性 Donators ,它是 Donator 的集合,即 DbSet 。之后,让程序跑了起来。然后我们发现数据库中产生了和该属性同名的数据表。数据库的创建过程使用了很多约定,包括表名和主键的确定。

时间: 2024-10-29 19:10:40

8天掌握EF的Code First开发系列之2 简单的CRUD操作的相关文章

8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to Entities 使用LINQ to Entities操作实体 LINQ操作 懒加载和预加载 插入数据 更新数据 删除数据 本章小结 本人的实验环境是VS 2013 Update 5,windows 10,MSSQL Server 2008. 上一篇<Code First开发系列之领域建模和管理实体关系>,我们主要介绍了EF中“约定大于配置”的概念,如何创建数据

Code First开发系列之管理并发和事务(转)

转自:http://www.cnblogs.com/farb/p/ConcurrencyAndTransctionManagement.html 返回<8天掌握EF的Code First开发>总目录 本篇目录 理解并发 理解积极并发 理解消极并发 使用EF实现积极并发 EF的默认并发 设计处理字段级别的并发应用 实现RowVersion 理解事务 创建测试环境 EF的默认事务处理 使用TransactionScope处理事务 使用EF6管理事务 使用已存在的事务 选择合适的事务管理 本章小结

【iOS开发系列】用简单工厂模式理解OC反射机制

// 在iOS开发中,简单工厂模式使用得并不多.但是.我认为这是OC反射机制很好的一个例子, // 所以本文将以计算器为例,讲解简单工厂模式和OC的反射机制. // [简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类( // 这些产品类继承自一个父类或接口)的实例.该模式中包含的角色及其职责:工厂角色.抽 // 象产品角色.具体产品角色] // --百度百科 简单工厂模式 // 上面这句话可能不怎么好理解,我在网上找到了一个例子,可能例子本身不能完全解释这个 // 设

【java开发系列】—— struts2简单入门示例

前言 最近正好有时间总结一下,过去的知识历程,虽说东西都是入门级的,高手肯定是不屑一顾了,但是对于初次涉猎的小白们,还是可以提供点参考的. struts2其实就是为我们封装了servlet,简化了jsp跳转的复杂操作,并且提供了易于编写的标签,可以快速开发view层的代码. 过去,我们用jsp和servlet搭配,实现展现时,大体的过程是: 1 jsp触发action 2 servlet接受action,交给后台class处理 3 后台class跳转到其他的jsp,实现数据展现 现在有了stru

【java开发系列】—— spring简单入门示例

1 JDK安装 2 Struts2简单入门示例 前言 作为入门级的记录帖,没有过多的技术含量,简单的搭建配置框架而已.这次讲到spring,这个应该是SSH中的重量级框架,它主要包含两个内容:控制反转\依赖注入,和AOP面向切面编程. 1 控制反转IOC\依赖注入DI,因为翻译的不同,因此有两个名字. 控制反转意思就是说,当我们调用一个方法或者类时,不再有我们主动去创建这个类的对象,控制权交给别人(spring). 依赖注入意思就是说,spring主动创建被调用类的对象,然后把这个对象注入到我们

Android开发系列(九):创建数据库以及完成简单的CRUD操作

本篇博文主要实现简单的创建数据库以及实现CRUD操作. 首先,我们建立一个Android Project,命名为db 一.完成数据库的创建操作: 用SQLiteOpenHelper类中的getWritableDatabase()和getReadableDatabase()都可以获取一个操作数据库的SQLiteDatabase实例,其中getReadableDatabase()方法中会调用getWritableDatabase()方法. 区别:其中,getWritableDatabase() 方法

[转] 使用 MVC 5 的 EF6 Code First 入门 系列

译文:http://www.cnblogs.com/Bce-/category/573301.html 原文:http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application 共12篇,值得学习学习. PS:肯为自己的译文加上个[渣]字的,质量都不会差到哪去.

关于在ASP.NET MVC 中使用EF的Code First的方式来读取数据库时的Validation failed for one or more entities. See &#39;EntityValidationErrors&#39; property for more details.

今天在做一个小网站的时候遇到很多问题唉,我还是个菜鸟,懂的也不多,今天一个表单的提交按钮用不了,都弄了几个小时唉.不过最后还是搞定了,还有浏览器有开发人员选项,不然我都不知道我还要继续排查多久哦,今天晚上在把数据存入数据库的又出现了问题.我使用的是Entity Framework的Code First模式来访问数据库的.对于数据的验证我在数据模型上加了数据注解.当然在前台也引入了一些JS这样就可以再不把数据提交到服务器时完成验证功能.在后台保存用户提交的数据的时候,我们要用到ModelStatu

EF 中 Code First 的数据迁移以及创建视图

写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功),我摸索出了一种简单有效的方法,这里分享给大家. EF是Entity Framework(实体框架)的简写,是微软出品的用来操作数据库的一个框架,会ASP.NET MVC的朋友对他肯定都不陌生.由于学艺不精,我对EF存在一疑虑,就不以[提问]的方式来问了,我以[总结]的方式来表达,如果总结有误的地