-1 扩展 有一点 增删改 adk 对象 structure mis
本章简言 |
上一章笔者对于 WinForm 开发过程用到的几个知识点做了讲解。笔者们可以以此为开端进行学习。而本章我们来讲一个跟 ORM 思想有关的知识点。在讲之前让我们想一下关于 JAVA 的 hibernate 知识点。hibernate 也是 ORM 框架。记得 hibernate 里面有一个叫 HQL。先不管 HQL 的好与坏。主要是明白 HQL 的目地是什么。ORM 的思想就是为了让用户在操作数据的时候用上面向对象的思想来看,而不是二维数据了。所以 HQL 笔者认为就是一个面向对象思想的 SQL 语句。那么为什么笔者要讲到 HQL 呢?事实上笔者认为 Linq 有一点跟他类似。如果项目架构是三层的话,就是让业务层的开发人员不用在看二维数据了。就连 SQL 语句都是面向对象思想形式来操作了。而 EF(Entity Framework) 可以说就是 hibernate。即是可以理解为 Linq 的数据源。但是 HQL 要在 hibernate 上面才能有效果。Linq 却可以不用 EF。
Linq 语法 |
.NET 对于 Linq 知识的分类让笔者有时候觉得很无力。为什么呢?最早的时候笔者认为 Linq 知识点分三大块。分别为 Linq to SQL、Linq to Entity、Linq to Database。随着对 Linq 使用的增加却发现还有 Linq to Xml 、Linq to Excel 等。笔者想读者们是不是看出门道来了。可以说. NET 在设计 Linq 的时候,应该是有充分的想过将来扩展的问题。当然这不是本章的目标。笔者在开发过程中最常用的就是 Linq to SQL 和 Linq to Entity。另外还有一个叫 Linq to Object. 对于 Linq to Object 笔者一直认为就是 Linq to Entity。笔者的意思是指他们的知识该应放在一块。好了。先笔者讲一下关于 Linq to SQL。
对于 Linq to SQL 来讲,只要学习 SQL 语法的人都不用担心很容易就上手。记得笔者学习的时候,一看我去不就 HQL 的另一种形态吗?当然 HQL 可不是 Linq 还是要学习一下的。讲那么多没有用。用列子才是最好的。
一、建立 EF 环境。先建一个项目,然后通过 NUGET 来获得对应的 EF 的 DLL。对于 NUGET 是什么。相信看过《Java 进击 C#——项目开发环境》的人应该可以了解到。选择 "引用" 右击》管理 Nuget 程序包。
相信看了上面的图片的时候,我们已经发现了 EntityFramework 了吧。点击 "安装" 就可以了。这个时候项目就会多出一个叫 packages.config 文件。这里面记录着当前安装的 dll 信息。同时物理目录里面会多出一个文件夹 packages 来存在这些 dll。
我们看到引用里面多出了关于 EF 的引用 dll。这个时候我们就可以做 EF 的事情了。
二、新建 EF 上下文。EF 有一个很重要的类。可以说是学习 EF 的核心点。这个类就是 DbContext。笔者新建一类叫 AomiContext 继承他。如下
- public class AomiContext : DbContext
- {
- }
DbContext 类有几个构造函数。笔者这里讲一个常用的吧。如下
- public DbContext(string nameOrConnectionString);
就是个构造函数意思就是传一个连接字符串或是配置文件的连接字符的配置名。记得上一节中讲的 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
- -->
- <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468
- -->
- <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468
- -->
- <!-- 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=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
- requirePermission="false" />
- </configSections>
- <connectionStrings>
- <add name="aomi" connectionString="Data Source=.;Initial Catalog=Ado;Persist Security Info=True;User ID=sa;Password=123"
- providerName="System.Data.SqlClient" />
- </connectionStrings>
- <entityFramework>
- <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
- <parameters>
- <parameter value="v11.0" />
- </parameters>
- </defaultConnectionFactory>
- <providers>
- <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"
- />
- </providers>
- </entityFramework>
- </configuration>
上面的 connectionStrings 部分是笔者自己写的。其他是自动生成的。.NET 自己有一个配置连接字符串的节点。我们就是在这个节点上写入自己的连接就可以了。
- <add name="aomi" connectionString="Data Source=.;Initial Catalog=Ado;Persist Security Info=True;User ID=sa;Password=123"
- providerName="System.Data.SqlClient" />
好了。接下来就是把 AomiContext 类修改一下。让他跟对应的连接字符串的配置发生关系。如下
- public class AomiContext : DbContext
- {
- public AomiContext()
- : base("Aomi")
- { }
- }
看到红色部分的代码了吧。把 Aomi 就是对应上面配置 add 节点的 name 的值。这个时候 EF 会自己去配置文件里面去找。
三、建立表和类的映射。
对应数据库的表:
- CREATE TABLE [dbo].[Catalogs](
- [ID] [int] NOT NULL,
- [CatalogName] [nvarchar](50) NULL,
- [CatalogCode] [nvarchar](50) NULL,
- CONSTRAINT [PK_Catalogs] PRIMARY KEY CLUSTERED
- (
- [ID] ASC
- )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
- ) ON [PRIMARY]
对应数据库的数据
- INSERT [dbo].[Catalogs] ([ID], [CatalogName], [CatalogCode]) VALUES (1, N'小吃', N'c0001')
- INSERT [dbo].[Catalogs] ([ID], [CatalogName], [CatalogCode]) VALUES (2, N'计算机', N'c0002')
笔者建一个类用于跟数据库里面的表相对应。这个时候要记得属性要跟表里里面的列名一样子才行。代码如下
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace LinqExample {
- public class Catalogs {
- public int ID {
- set;
- get;
- }
- public string CatalogName {
- set;
- get;
- }
- public string CatalogCode {
- set;
- get;
- }
- }
- }
有了对应的类之后,还不行。我们还要修改一下 AomiContext 类。这样子就可以通过 AomiContext 类来访问对应的类对象了。即是数据了。如下
- public class AomiContext : DbContext
- {
- public AomiContext()
- : base("Aomi")
- { }
- public IDbSet<Catalogs> Catalogs { set; get; }
- }
四、执行 EF。
- class Program
- {
- static void Main(string[] args)
- {
- using (AomiContext ac = new AomiContext())
- {
- IQueryable<Catalogs> queryable = from c in ac.Catalogs select c;
- List<Catalogs> catalogList = queryable.ToList();
- foreach (Catalogs catalog in catalogList)
- {
- Console.WriteLine(string.Format("ID:{0} CatalogName:{1}", catalog.ID, catalog.CatalogName));
- }
- }
- Console.ReadKey();
- }
- }
执行结果:
从上面的例子我们可以看到用了 from c in ac.Catalogs select c; 来获得对应的数据。这便是 linq to sql。简章讲他是一个面向对象的 SQL 语句。SQL 语句是以 select 开头,结尾不确定。而 linq to sql 一般是以 from 开头,以 select 结尾。表示从哪一个数据源开始,最后要以什么样子返回。可是我们可以看到他返回是一个 IQueryable<T> 类型。事实这个时候他并没有去执行获得数据。可以解理为他现在只是去组装 SQL 语句。只到 queryable.ToList(); 才是去执行获得数据。为了学习上的方便笔者又加几个关键字让大家看一下。
- using (AomiContext ac = new AomiContext())
- {
- IQueryable<Catalogs> queryable = from c in ac.Catalogs where c.CatalogName.Contains("吃") orderby c.CatalogCode ascending select c;
- List<Catalogs> catalogList = queryable.ToList();
- foreach (Catalogs catalog in catalogList)
- {
- Console.WriteLine(string.Format("ID:{0} CatalogName:{1}", catalog.ID, catalog.CatalogName));
- }
- }
看完了 linq to sql 之后,让我看一下关于 linq to entity 又是什么东东呢?可是这样子讲 linq 的语法都相像。只是用法和写法不一样子而以。把上面的例子变一变吧。
- using (AomiContext ac = new AomiContext())
- {
- //IQueryable<Catalogs> queryable = from c in ac.Catalogs where c.CatalogName.Contains("吃") orderby c.CatalogCode ascending select c;
- IQueryable<Catalogs> queryable = ac.Catalogs.Where(t => t.CatalogName.Contains("吃")).OrderBy(t => t.CatalogCode).Select(c=> c);
- List<Catalogs> catalogList = queryable.ToList();
- foreach (Catalogs catalog in catalogList)
- {
- Console.WriteLine(string.Format("ID:{0} CatalogName:{1}", catalog.ID, catalog.CatalogName));
- }
- }
看样子笔者不用多说也明白。就是变成了对应的关键字方法而以。没有错。就是这样子。这个时候笔者就可以这样子认为不管是 Linq to entity 还是 Linq to sql 都必须要有对应的数据源。这里 EF 就是为他们提供数据源的。他们俩个对应都是返回 IQueryable<T> 类型。只是 Linq to entity 是用方法。而 Linq to sql 更多像 SQL 语句。
好了。让我们看一下关于 linq to object 吧。可以这样了讲吧——不管是 Linq to sql 还是 linq to entity 他们俩个都离不开 linq to object。linq to object 是专对内存中的数据进行处理。我们可以看到上面例子中有出现一段 queryable.ToList()。如果笔者说 ToList() 是 linq to object 会不会有人喷我。为什么笔者说他是 linq to object 呢?主要是 ToList() 是对于 IEnumerable<T> 进行静态扩展的。IEnumerable<T> 一般都是用于数组和集合。位于内存中的。而上面都是专对于 IQueryable<T> 类型的。好了。如果你实在觉得笔者分的不对的话,那就是不要分了。都为 Linq 语法就行了。linq 还提供了一些比较常用的方法。
First:返回第一个数据。没有数据就出生异常。同时也可以传入第一个数据的条件作为参数。如 queryable.First(t => t.CatalogName.Contains("吃"));。
FirstOrDefault:同样子返回第一个数据,没有数据的话,就返回 NULL。同时也可以传入第一个数据的条件作为参数。
Last:同理获得最后一方法。用法同上一样子。
LastOrDefault:同上一样子。跟 FirstOrDefault 用法一样子。
Skip:给定一个数字,那么数字前面都不会取出来,后以的才取出来。一般都跟 Take 方法一起用来作分页功能。
Take:表示要返回的数量。你可以理解为 SQL 语句中的 TOP 关键字。
让笔者举个 linq to object 的列子吧。
- List<string> src = new List<string>();
- src.Add("a1");
- src.Add("b2");
- src.Add("c4");
- src.Add("d5");
- string value = src.First(t => t.StartsWith("a"));
- Console.WriteLine(value);
注意:笔者是这样子分的。一般静态扩展 IQueryable<TSource> 的方法属于 linq to entity。而静态扩展 IEnumerable<TSource> 则为 linq to object。俩者很像。只是 linq to entity 必须要有数据源。linq to object 一般是处理内存数据。
Entity Framework |
Entity Framework 做为 ORM 框架之一。所以 ORM 框架必须有的东西他多有。学习 Entity Framework 就必须知道他有什么知识点。Entity Framework 根据开发模式的不同分为 Code First、Model First 和 Database First。让笔者用土一点的说法来讲吧。
Code First 模式:就是通过写代码来生成对应的数据库和表。
Model First 模式:事实上跟 Code First 有一点像。只是他用了. NET 的一个叫 xxx.edmx 的文件来操作而以。通过他来生成对应的数据库和表。
Database First 模式:却跟前面俩个相反。先建数据库和表在生成对的类。即是代码。
我们现在要学习 Entity Framework。笔者个人意见读者们最好选择 Code First 模式来学习。为什么。不管是 Model First 模式还是 Database First 模式大部分都是软件工具帮你生成对应的代码。所以很多东西我们根本看不到。而 Code First 模式就是要开发人员手把手的写了。记得笔者在使用 hibernate 的时候。并没有说只做一边的事情。一般都是数据库的表建完之后。还是要去写对应的映射配置文件 (xxx.hbm.xml)。好一点就自己写一个代码生成器。Entity Framework 意图就是帮开发人员做掉一边的工作。不过这也是笔者不喜欢的。正因为这样子 Entity Framework 多出了一个知识点那就是数据迁移。我们都知道在开发的过程中。可能会因为当初表没有设计好。突然发现需要增加一个字段。这样个时候 Entity Framework 就要做很多事情。假设我们用的是 Code First 吧。我们在代码中的类增加一个属性。这个时候 Entity Framework 就是要去判断哪些属性是旧的。哪些属性是修改的。哪些属性是新增加的。然后 Entity Framework 在更新数据库。就是 Entity Framework 的数据迁移。
从上面的讲解中我们知道 EF 想帮我做了另一半的事情,所以就必须对数据库操作才行。那么就是存在对数据库设置,对表设置,对数据操作。以下全是在在 Code First 模式下的讲说。
1、EF 对数据库的设置。执行代码的时候,EF 会去判断是否存在对应的数据库。而对数据库进行操作。是创建还是删除在创建。还是更新呢?主要看你设置对应的数据库操作的模式。那么 EF 为我们提供了三个。当然我们可以自己写一个。三个类都在 System.Data.Entity 命名空间下。分别是 CreateDatabaseIfNotExists、DropCreateDatabaseAlways、DropCreateDatabaseIfModelChanges。文英好的人都能看得懂是什么一会事。
- public AomiContext()
- : base("Aomi")
- {
- Database.SetInitializer<AomiContext>(new CreateDatabaseIfNotExists<AomiContext>());
- }
注意:关于 Database.SetInitializer 方法的赋值是可以放在别的地方。但一定要执行 EF 之前。
2、EF 对表的设置。这些设置大部是关于表和表与表之间的关系如何体现在类和类与类之间的关系。下面笔者做了一个简单的映射例子。
- public class AomiContext : DbContext
- {
- public AomiContext()
- : base("Aomi")
- { }
- public IDbSet<Catalogs> Catalogs { set; get; }
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- base.OnModelCreating(modelBuilder);
- modelBuilder.Configurations.Add(new CatalogsMap());
- }
- }
下面 CatalogsMap 类就相当于映射配置文件 (xxx.hbm.xml)。其中包括一对一,一对多,多对多之类的关系也是在这里配置。有一点要注意的是上面红色代码。即是把映射关系加入 EF 配置里面
- public class CatalogsMap : EntityTypeConfiguration<Catalogs>
- {
- public CatalogsMap()
- {
- this.HasKey(t => t.ID);
- this.Property(t => t.CatalogName).HasColumnName("CatalogName");
- this.Property(t => t.CatalogCode).HasColumnName("CatalogCode");
- }
- }
3、对数据的操作。对数据的操作一般就是增删改查了。
增加:
- using (AomiContext ac = new AomiContext())
- {
- Catalogs catalogs = new Catalogs();
- catalogs.ID = 6;
- catalogs.CatalogName = "商品";
- catalogs.CatalogCode = "s0001";
- ac.Catalogs.Add(catalogs);
- ac.SaveChanges();
- }
事实上面的代码是没有问题。可是执行的时候却会发生错误。为什么呢?笔者也不是清楚什么原因。查找没有问题。可是在增加却会出问题。让我看一下异常吧。这里笔者只复制出一部分。
- InnerException: System.Data.SqlClient.SqlException
- _HResult=-2146232060
- _message=不能将值 NULL 插入列 'ID',表 'Ado.dbo.Catalogs';列不允许有 Null 值。INSERT 失败。
- 语句已终止。
- HResult=-2146232060
- IsTransient=false
- Message=不能将值 NULL 插入列 'ID',表 'Ado.dbo.Catalogs';列不允许有 Null 值。INSERT 失败。
- 语句已终止。
- Source=.Net SqlClient Data Provider
- ErrorCode=-2146232060
- _doNotReconnect=false
他说我的 ID 没有设置值,可是我设置了。笔者想你们一定会认为是没有设置标识。也就是自动增长。不是这样子的。笔者本来就是没有想过要自动增长啊。那么为什么会错呢?事实笔者也是以这样子的角度去想的。会不会 EF 默认就认为 ID 是自动增长。因为 ID 是 int 类型的。又是主键。所以我就在映射配置里面加了一段代码。如下
- public class CatalogsMap : EntityTypeConfiguration<Catalogs>
- {
- public CatalogsMap()
- {
- this.HasKey(t => t.ID);
- this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
- this.Property(t => t.CatalogName).HasColumnName("CatalogName");
- this.Property(t => t.CatalogCode).HasColumnName("CatalogCode");
- }
- }
红色部分就是增加的代码。这个时候就不会出错了。那么这段代码是什么意思。就是告诉 EF 这里的 ID 只是普通的。没有别的设置。
修改:
- using (AomiContext ac = new AomiContext())
- {
- Catalogs updateCatalogs = ac.Catalogs.FirstOrDefault(t => t.ID == 4);
- updateCatalogs.CatalogName = "纸类";
- ac.SaveChanges();
- }
删除:
- using (AomiContext ac = new AomiContext())
- {
- Catalogs deleteCatalogs = ac.Catalogs.FirstOrDefault(t => t.ID == 1);
- ac.Catalogs.Remove(deleteCatalogs);
- ac.SaveChanges();
- }
笔者在做 EF 的增删改的时候,我心里面一直在想俩个问题?
第一:EF 并没有像 Hibernate 那样子。有增加的方法和更新的方法。他只有一种概念那就是数据有没有发生改变。根据改变数据来更新数据库的数据。
第二:Hibernate 在处理对象的时候。会用到对象的三种状态。这三种状态在不同的书里面有不同的叫法。笔者一般喜欢叫他们为普通状、持久状、游离状。可是如果把 Hibernate 这个知识放在 EF 这边来的话,也不是说不可以。只是觉得这个时候有一点怪。EF 这边并没有类似相关的说明。可是笔者还是觉得有必要用他放在 EF 这边。为什么呢?先让我们看一下情况吧。如果我们把上面的 ID 变成了标识。即为自动增长。在增加的时候就没有必要去设置这个值。那么增加成功之后我们要如果去获得对应的 ID 值呢?难道在获取一遍吗?显然不是。如下。
- class Program
- {
- static void Main(string[] args)
- {
- using (AomiContext ac = new AomiContext())
- {
- Catalogs catalogs = new Catalogs();
- catalogs.ID = 0;
- catalogs.CatalogName = "商品";
- catalogs.CatalogCode = "s0001";
- ac.Catalogs.Add(catalogs);
- ac.SaveChanges();
- Console.WriteLine("ID:" + catalogs.ID);
- }
- Console.ReadKey();
- }
- }
笔者把上面的数据全部删除掉。并且把 Catalogs 表的列 ID 修改为标识。即是自动增长。在新建 Catalogs 对象的时候把 ID 设置为 0。然后我们在看一下增加成功之后 ID 是不是还是为 0。但是记得把 CatalogsMap 类里面的映射配置修改一下。修改如下。
- this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
好了,让我们看一下结果值是多少。如下。我们看到是 7。为什么不是 0。笔者的 SQL Server 的标识是从 6 开始的。所以这边是 7。如果你们一开始的话。是 1。但决对不是 0。为什么?就是因为对象变成了持久状了。
普通状:就是正常用关键字 new 来创建。
持久状:就是通过 EF 之后,比如增加。这个时候对象跟数据库同步。
游离状:关闭 EF 之后的对象。不过笔者认为 EF 没有这种状态。因为 Hibernate 有唤醒这个功能。
本章总结 |
本章主要讲到关于 Linq 和 EF 的知识点。Linq 的一些入门用法和 EF 的基本知识点。当然,有关 EF 的类与类之间的关系和数据迁移笔者笔者必没有说。希望读者们自行查看。
Java 进击 C#——应用开发之 Linq 和 EF
来源: http://www.bubuko.com/infodetail-2081562.html