记得去年初就开始关注Entity Framework,那时只是简单测试了一下,发现较之Nhibernate不太成熟。当时的EF主要表驱动方式开发,过度依赖edm文件,并且数据层耦 合了模型层,让一些MVC分层用户痛苦不堪。微软从Oxite1项目发展到Oxite2也在这个DAL与MODEL的理不清的关系上做过徘徊,只好在 EDM的基础上直接实现BLL。由于EntityObject模型与ObjectContext耦合,在N层架构构中EntityObject直接提供给 客户端使用的话,那ObjectContext在客户端也会被调用,因此这个时候只能通过DTO对象的方式解决,而毕竟大部分EntityObject是 可以直接传递使用的,而不是一定通过DTO传递。
我们看看现在的EF4.0和EF4.1有哪些进步,先来解释一些名词
EDM文件
EDM是实体数据关系映射的XML文件,不同于Nhibernate每个对象单独映射了一个XML文件。EDM主要有三部分构成 CSDL,SSDL,MSL。CSDL表面的是实体数据模型结构,SSDL表示对应的数据存储的架构,CSDL实体与SSDL数据结构的关系通过MSL映 射实现。EDM是通过ADO.NET 实体数据模型生成的
|
针对创建好的EDM模型最终要生成代码,生成代码的工具不同,生成的代码也不同。看看下面几种生成方式,都于基于EDM模型生成的。
ADO.NET 实体数据模型
最初EF的方式,实体模型EntityObject与ObjectContext耦合在一起,不适合分层使用。
ADO.NET 自跟踪实体生成器
分离生成基于POCO的SelfTrackingEnityObject模型和ObjectContext (这种方试即使设置了延迟加载也无法加载关联导航属性,要在使用时手动加载)
ADO.NET DbContext Generator
分离生成纯POCO模型和轻型DbContext。DbContext较之ObjectContext比较简洁,并且POCO可以充分利用。
这就是我为什么选ADO.NET DbContext Generator 的原因,我们再看看EF框架的划分的模式:
DataBase FirstModel FirstCode First
DataBase First
传统的表驱动方式创建edm,然后通过edm生成模型和数据层代码。除生成实体模型和自跟踪实现模型,还支持生成纯POCO模型和轻型DbContext。
Model First
先创建EDM模型,再生成DDL数据库脚本和模型和数据层代码。除生成实体模型和自跟踪实现模型,支持生成纯POCO模型和轻型DbContext。
Code First
手动创建POCO模型,数据层DbContext及映射关系,通过Database.SetInitializer生成数据库,这种方式较灵活,但是代码工作较多。
虽然Code First灵活,但是我们不可能手工去写大量的POCO类和映射关系。如果借助其它ORM工具生成Code First的需要POCO类,为什么试试Model First生成Code First需要的代码呢?
本篇选择基于Model First方式+通过ADO.NET DbContext Generator生成基于Code First方式代码
,是不是有点概念混乱?但是这种方式基本上和Nhibernate是一致的,而Nhibernate又有着广泛的项目基础。
Model First方式
主要解决构建模型和EDM映射文件工作。
ADO.NET DbContext Generator
基于EDM文件生成POCO模型,DbContext代码以及DDL数据库脚本。因为Code First你要自己实现POCO,DbContext的代码,这部分工作如果不借助工具实现代码量还是很大的。做项目不可能像写个Demo用简单的几个类 演示一下就完了,总不能为了演示而学习,最终还是要提高工作效率。这也是为什么我觉得EF已经成熟了决定用于项目的原因。
下面就把这个过程简单的走一遍:
1.首先创建项目,类库EF.Model,EF.DAL,EF.BLL,控制台EF.Demo。
在类库EF.DAL中创建空EDM模型 (为什么要在EF.DAL创建EDM,而不是EF.Model中创建,后面会说明),打开空的EDM模型,我们构建几个实体对象,并映射各个实体间的关系。
EDM视图如下:
|
2.添加代码生成
完成我们的对象设计后,右键EMD属性->添加代码生成项...->选择ADO.NET DbContext Generator生成器 ,这个时候EDMX就变成空模板了,属性生成代码策略被关闭
完成后,会自动生成两个tt文件,一个DemoDB.Context.tt (DbContext),一个DemoDB.tt (POCO)
|
|
(DbContext 是EF4.1内容, 另外在VS解决方案的工具里有扩展管理器可直接下载最新的VS扩展插件,通过Library Package Manager的控制台直接添加引用)
如果对象修改了,我们只要再保存EDM模板就可以及时更新DemoDB.tt中的对象。而DAL层基本上不需要修改。
3. EF.DAL创建通用数据操作类库(仿Nhibernate)
IRepository接口:(IOC注入)
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace EF.DAL{
public interface IRepositoryT> where T : class, new()
T Create();
T Update(T entity); T Insert(T entity);
void Delete(T entity);
T Find(params object[] keyValues);
ListT> FindAll();
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using System.Data.Entity;using EF.DAL;namespace EF.DAL{
public abstract class RepositoryBaseT>:IRepositoryT> where T :class,new()
public DbContext context; //提供IOC注入方式接口
public RepositoryBase(DemoDBEntities context)
{ this.context = context;
} //测试用
public RepositoryBase() { this.context = new DemoDBEntities();
} #region IRepositoryT> 成员
public T Create()
return context.SetT>().Create();
public T Update(T entity)
//执行验证业务
//context.Entry(entity).GetValidationResult(); if (context.Entry(entity).State == EntityState.Modified) context.SaveChanges();
return entity;
public T Insert(T entity)
context.SetT>().Add(entity);
context.SaveChanges(); return entity;
public void Delete(T entity)
context.SetT>().Remove(entity);
context.SaveChanges();
public T Find(params object[]keyValues)
return context.SetT>().Find(keyValues);
} public ListT> FindAll()
return context.SetT>().ToList();
#endregion
}}
using System;using System.Collections.Generic;using System.Linq;using System.Text;using EF.Model;namespace EF.DAL{
public interface IBlogCategoryRepository:IRepositoryBlogCategory>
using System;using System.Collections.Generic;using System.Linq;using System.Text;using EF.Model;namespace EF.DAL{
public class BlogArticleRepository:RepositoryBaseBlogArticle>,IBlogArticleRepository
4.EF.BLL层简单的实现一下业务
BlogCategoryService 实现关联表操作(添加一个BlogCategory分类,并且在这个分类下增加一个BlogArticle文章)
using System;using System.Collections.Generic;using System.Linq;using System.Text;using EF.DAL;using EF.Model;namespace EF.BLL{
public class BlogCategoryService {
IRepositoryBlogCategory> repositoryCategory;
IRepositoryBlogArticle>
repositoryArticle;
public BlogCategoryService(IRepositoryBlogCategory>repositoryCategory,IRepositoryBlogArticle> repositoryArticle)
{ this.repositoryCategory = repositoryCategory;
this.repositoryArticle = repositoryArticle;
public BlogCategoryService() {
this.repositoryCategory = new BlogCategoryRepository();
this.repositoryArticle = new BlogArticleRepository();
} public BlogCategory CreateBlogCategory()
return repositoryCategory.Create();
} public BlogArticle CreateBlogArticle()
return repositoryArticle.Create();
public BlogCategory Insert(BlogCategory entity)
return repositoryCategory.Insert(entity);
public BlogCategory Update(BlogCategory entity)
return repositoryCategory.Update(entity);
public void Delete(BlogCategory entity) { repositoryCategory.Delete(entity);
using System;using System.Collections.Generic;using System.Linq;using System.Text;using EF.Model;using EF.BLL;namespace EF.Demo{ class Program {
static void Main(string[] args) {
BlogCategoryService service=new BlogCategoryService();
//创建博文分类
BlogCategory cate = service.CreateBlogCategory();
cate.CateName = EF分类标签;
//创建一篇博文
BlogArticle arti = service.CreateBlogArticle(); arti.Title = EF进化论; arti.Content = EF测试内容; //博文加到博文分类
cate.BlogArticle.Add(arti);
//更新 service.Insert(cate);
Console.ReadLine(); } }}
|
另外提一点 DbContext 可以转换为ObjectContext,用Refletor反编译看到是通过一个中间InternalContext实现的
实现代码: ObjectContext context = ((IObjectContextAdapter) DbContext).ObjectContext;
如果不想直接加载导航属性数据,你可以在DbContext的构造函数禁用延迟加载。
//// //
此代码是根据模板生成的。////
手动更改此文件可能会导致应用程序中发生异常行为。//
如果重新生成代码,则将覆盖对此文件的手动更改。//
//
namespace EF.DAL{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using EF.Model;
public partial class DemoDBEntities : DbContext
public DemoDBEntities() : base(name=DemoDBEntities) {
//禁用延迟加载 this.Configuration.LazyLoadingEnabled = false;
//禁用代理 this.Configuration.ProxyCreationEnabled = false;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
throw new UnintentionalCodeFirstException();
public DbSetBlogArticle> BlogArticle { get; set; }
public DbSetBlogCategoryRepository> BlogCategory { get; set; }
public DbSetBlogComment> BlogComment { get; set; }
public DbSetBlogDigg> BlogDigg { get; set; }
public DbSetBlogMaster> BlogMaster { get; set; }
public DbSetBlogTag> BlogTag { get; set; }
如果表设计DateTime字段不允许为空,EF执行SaveChanges会出错,建议使用DateTime2类型解决(SQL2008以后版本)