转自: http://www.cnblogs.com/jinwangjia/p/4240795.html
项目中使用EntityFramework
项目代码:http://yunpan.cn/cKAw3sSJWQwUX 提取码 871c
前言 1
1. 建立解决方案项目结构 1
2. 导入EntityFramework类库 1
3. 编写实体类 1
4. 编辑连接字符串 3
5. 编写BaseBLL类 3
6. 编写业务处理超类 4
7. 编写业务处理类 6
8. 应用 6
9. 疑难杂症 7
前言
数据库的使用在软件编程中占有很大的一个比例。尤其是在管理系统中,基本上没有哪个系统能离开数据库而单独存在。在编写数据库访问程序时最让人头疼的就是拼SQL,一般都需要在数据库和VS里颠倒好多次才能写出我们想要的那个语句。ORM的出现很好的解决了这个问题,它可以把SQL相关程序的编写变成类似强类型的语句,方便编写和调试,大大提高了工作效率。下面就把我在项目中如何使用EntiryFramework做一下简单的记录。开发环境:VS2010,SQLServer208,EntityFramework6
1. 建立解决方案项目结构
EntityFramework是用来访问数据库的,根工程的类型没有关系,我们可以把它用用C/S的工程当中,也可以把它用到 B/S的工程当中。为了展示EntityFramework的使用方法,我们首先得建立一个例子解决方案。这个解决方案的最基础的普通三层结构,因为数据的访问主要靠EntityFramework来实现,所以数据访问层的实现基本看不出来了,使得整个解决方案好像就是个两层的一样。下面我们通过表格的形式来说明一下这个解决方案中各个工程的类型和做用。
工程名 | 层次 | 类型 | 作用 | 是否引入EF |
JZAPP | 展示层 | 应用程序 | 应用程序 | 否 |
BLL | 业务逻辑层 | 类库 | 逻辑运算,数据访问 | 是 |
Model | 实体类 | 类库 | 数据库表到类的映射 | 是 |
2. 导入EntityFramework类库
用Nuget把最新版本的EntityFramework导入到BLL,Model两个项目中。
3. 编写实体类
下面我们以下面图中这个实体类做为样本,来了解一下EntityFramework中的实体类如何定义。实体类是数据库表的映射,所以这个类的名称,属性名称,属性类型都是根我们设计的表有关联了。具体数据库中的类型映射为C#中的什么类型没有做过详细的研究,大概用过的总结如下,如果以后还会用到其他的,还会添加到这里来的。
SqlServer中的类型 | C#中的类型 |
Varchar(N) | string |
bit | bool |
datetime | DateTime |
int | integer |
数据库定义如下:
根据表格,我们可以把上面的数据库表写为如下实体类,实体类统一放到Model工程里:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations; namespace Model { [Table("User")] public class UserDefinition { /// <summary> /// 用?户§id /// </summary> [Key,DatabaseGenerated(DatabaseGeneratedOption.None)] public string UserId { set; get; } public string LoginName { set; get; } public string Name { set; get; } public string Password { set; get; } public bool Sex { set; get; } public string DeptId { set; get; } public DateTime CreateDate { set; get; } public int Status { set; get; } public string SizeId { get; set; } public bool Visible { get; set; } } }
- [Table("User")]:定义UserDefinition对应的数据库表为User
- [Key]:定义UserId 为主键
- [DatabaseGenerated(DatabaseGeneratedOption.None)]:定义字段的数据生成项;DatabaseGeneratedOption有三个值;Identity:自增长;None:不处理;Computed:计算列。
4. 编辑连接字符串
连接字符串放在JZAPP工程的配置文件里(app.config),如果工程中有这个文件那就直接使用,如果没有就新建一个。
在配置文件里添加一个connectionStrings节。它是configuration的子节点。
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <add name="ABC" connectionString="Data Source=127.0.0.1;Initial Catalog=abc;Persist Security Info=True;User ID=sa;Password=landmark" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>
- Name:本连接字符串的名称,它在本程序中用于BaseBLL类的构造函数中
- Data Source:数据库服务器的名称,也可以是IP,URL
- Initial Catalog:数据库的名称贷
- User ID:数据库的用户名
- Password:数据库密码
5. 编写BaseBLL类
这个类是访问数据库的基础,有了这个类才可以写其他的业务处理。如果没有业务处理,可以直接用这个类操作数据库。它必须继承自DbContext。
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
using System.Data.Entity; using Model; using System.Data.Entity.SqlServer; namespace BLL { public class BaseBLL:DbContext { public DbSet<UserDefinition> User { get; set; } public BaseBLL() : base("abc") { } } }
编写这个类要注意三点:
- BaseBLL必须继承自DbContext
- 用DbSet<>类型实例化每一个实体类
- 在构造函数中赋值连接字符串的名称
6. 编写业务处理超类
我们可以针对不同的业务来编写代码,在程序开发中,遇到重复性太强的地方,把它总结一下,写出了以下几个常函数,把它们定义成了一个父类:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
public class ParentBLL<T> where T : class { public virtual void Add(T p) { using (BaseBLL bll = new BaseBLL()) { bll.Set<T>().Add(p); bll.SaveChanges(); } } public T Find(object p) { using (BaseBLL bll = new BaseBLL()) { return bll.Set<T>().Find(p); } } public IList<T> Query() { using (BaseBLL bll = new BaseBLL()) { return (from o in bll.Set<T>() select o).ToList(); } } public virtual void Remove(T p) { using (BaseBLL bll = new BaseBLL()) { bll.Set<T>().Attach(p); bll.Set<T>().Remove(p); bll.SaveChanges(); } } public virtual void Update(T p) { using (BaseBLL bll = new BaseBLL()) { bll.Entry(p).State = EntityState.Modified; bll.SaveChanges(); } } }
7. 编写业务处理类
业务处理类一开始应该是非常简单的,我们直接用业务处理类的父类来实现它就我们开始的使用了。
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
using Model; using System.Data.Entity.SqlServer; namespace BLL { public class UserBLL : ParentBLL<UserDefinition> { } }
8. 建库SQL
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
create table "User" ( UserId varchar(100) not null, LoginName varchar(100) null, Name varchar(100) null, Password varchar(100) null, Sex bit null, DeptId varchar(100) null, CreateDate datetime null, Status int null, SizeId varchar(100) not null, Visible bit null, constraint PK_USER primary key (UserId) ) Go
9. 应用
下面的程序中用上面的框架写的一个添加数据程序。
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
private void button1_Click(object sender, EventArgs e) { var bll = new UserBLL(); var p = new UserDefinition(); p.UserId = Guid.NewGuid().ToString(); p.Name = Guid.NewGuid().ToString(); p.SizeId = Guid.NewGuid().ToString(); p.CreateDate = DateTime.Now; bll.Add(p); }
10. 疑难杂症
写到这里关于EF的应用就算是已经说完了。凭借上面的代码,我们已经可以用EF来访问数据库了。如果这里面还有一人雷。如果把上面的代码直接复制到工程里去执行可能做引发一个很莫名其妙的错误,那就是EF的一个程序集没有输出到JZAPP的输出目录里面。
EF6在安装到项目时在项目里添加了两个程序集分别为EntityFramework和EntityFramework.Sqlserver.我们的解决方案里。Model和BLL工程引用了EntityFramework和EntityFramework.Sqlserver;JZAPP又引用了 Model和BLL。JZAPP没有直接引用EntityFramework和EntityFramework.Sqlserver。编译解决方案后,Model和BLL工程的输出目录里有EntityFramework和EntityFramework.Sqlserver程序集。JZAPP工程的输出目录里只有EntityFramework一个程序集。工程可以编译通过,但是运行就会出错误,这个问题刚开始很让我莫名其妙。后来经过哥儿们的帮忙看到一个博客。里面有一句话(间接引用程序集,如果只是引用而没有调用,程序集不会被输出到目标目录)让我知道了原因。经过测试这句话是正确的。解决方法就是在程序中调用一下这个程序集里的程序。如下:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
using Model; using System.Data.Entity.SqlServer; namespace BLL { public class UserBLL : ParentBLL<UserDefinition> { private string UserName() { using (var bll = new BaseBLL()) { return SqlFunctions.UserName(); } } } }
随便找一个地方写一个没用的函数就可以了。经过测试这个方法编译可以通过,也能解决上面的问题。但是调用会出错。没关系了,解决问题就好了。SqlFunctions类具体怎么用以后再研究吧。
好像还有其他更好的方法来解决这个问题,如果你知道,请告诉我,谢谢。