长期饭票

大家好,请喊我序员!
QQ:15838986
posts - 124, comments - 22, trackbacks - 0, articles - 0
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[转]Model First+DbContext Generator生成基于Code First方式代码的EDM文件

Posted on 2012-06-05 10:03  Baode  阅读(...)  评论(...编辑  收藏

EDM文件

  EDM是实体数据关系映射的XML文件,不同于Nhibernate每个对象单独映射了一个XML文件。EDM主要有三部分构成CSDL,SSDL,MSL。CSDL表面的是实体数据模型结构,SSDL表示对应的数据存储的架构,CSDL实体与SSDL数据结构的关系通过MSL映射实现。EDM是通过ADO.NET
实体数据模型生成的

 

 

  生成EDM文件的方式有两种,一种基于是数据库,一种是创建空EDM模型。前者就是后面要提到的DataBase
First方式,后者是Model First方式。

  针对创建好的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 First
  • Model First
  • CodeFirst

  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视图如下:

 

 

  右键属性选择根据模型生成数据库-> 生成DemoDB.edmx.sql脚本->打开脚本 右键执行SQL 生成到数据库

  2.添加代码生成

  完成我们的对象设计后,右键EMD属性->添加代码生成项...->选择ADO.NET DbContext Generator生成器 ,这个时候EDMX就变成空模板了,属性生成代码策略被关闭

完成后,会自动生成两个tt文件,一个DemoDB.Context.tt (DbContext),一个DemoDB.tt (POCO)

 

 

 

  我们将DemoDB.edmx和Demo.tt 两个文件COPY到EF.Model中,并且删除掉EF.DAL中的这两个文件。由于DemoDB.edmx和Demo.tt 两个文件是在EF.DAL创建的,所以移到EF.Model中他们的命名空间还是EF.DAL。不用担心,我们在EF.Model中打开DemoDB.edmx和Demo.tt两个模板文件,点击保存后,模板会自动修改命名空间为EF.Model。注意了EF.DAL中的DemoDB.Context.tt模板不要打开保存,否则DbContext的代码会丢失。 这样我们完成了Model和DAL代码的分离工作了。

 

  (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{
publicinterface IRepository<T> where T
:
class, new()
{
T Create();
T Update(T entity); T Insert(T entity);
void Delete(T
entity);
T Find(params object[] keyValues);
List
<T> FindAll();
}
}

 

  RepositoryBase 抽象基类实现

 


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{
publicabstractclass RepositoryBase<T>:IRepository<T> where T
:
class,new()
{
public DbContext context; //提供IOC注入方式接口
public RepositoryBase(DemoDBEntities context)
{
this.context = context;
}
//测试用
public RepositoryBase() { this.context =new DemoDBEntities();
}
#region IRepository
<T> 成员
public T
Create()
{
return context.Set<T>().Create();
}
public T
Update(T entity)
{
//执行验证业务
//context.Entry<T>(entity).GetValidationResult();
if (context.Entry<T>(entity).State == EntityState.Modified)
context.SaveChanges();

return entity;
}
public T
Insert(T entity)
{
context.Set
<T>().Add(entity);
context.SaveChanges();
return entity;
}
publicvoid Delete(T
entity)
{
context.Set
<T>().Remove(entity);
context.SaveChanges();
}
public T
Find(params object[]keyValues)
{
return context.Set<T>().Find(keyValues);
}
public List<T> FindAll()
{
return context.Set<T>().ToList();
}
#endregion
}}

 

 IBlogCategoryRepository 接口(IOC注入)

 


using System;using System.Collections.Generic;using
System.Linq;using System.Text;using EF.Model;namespace EF.DAL{

publicinterface IBlogCategoryRepository:IRepository<BlogCategory>
{
}
}

 

  BlogArticleRepository实现

 


using System;using System.Collections.Generic;using
System.Linq;using System.Text;using EF.Model;namespace EF.DAL{
publicclass BlogArticleRepository:RepositoryBase<BlogArticle>,IBlogArticleRepository
{
}
}

 

  看看后面两个具体数据操作类的代码极其简单,这就是EF4.0 之后的泛型的优点 ,可以使代码尽量的简洁。

 

  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{
publicclass BlogCategoryService {
IRepository
<BlogCategory> repositoryCategory;
IRepository
<BlogArticle>
repositoryArticle;
public BlogCategoryService(IRepository<BlogCategory>repositoryCategory,IRepository<BlogArticle> 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);
}
publicvoid Delete(BlogCategory entity) {
repositoryCategory.Delete(entity);
}
}
}

 

  5.EF.Model测试导航属性关联操作(同时往两张表插入记录)

 


using System;using System.Collections.Generic;using
System.Linq;using System.Text;using EF.Model;using EF.BLL;namespace
EF.Demo{
class Program {
staticvoid 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(); } }}

 

  6.结果

 

 

  通过Model First的方式+ADO.NET DbContext Generator生成器 实现Code First方式业务(EDMX通过DbContext构造注入其中),到达Hibernate的效果。EDMX相当于Hibernate
对象模型XML映射文件,POCO相当于Hibernate对象模型(virtual实现关联导航加载),DbContext通过泛型构建IRepository数据操作类。之前看到相关测试,微软的EF
ADO.NET 测试效率高出Hibernate 30%左右,不知道是不是真的-_-|||。

  另外提一点 DbContext 可以转换为ObjectContext,用Refletor反编译看到是通过一个中间InternalContext实现的

  实现代码: ObjectContext context = ((IObjectContextAdapter) DbContext).ObjectContext;

 

  如果不想直接加载导航属性数据,你可以在DbContext的构造函数禁用延迟加载。

 


//------------------------------------------------------------------------------// <auto-generated>//
此代码是根据模板生成的。////
手动更改此文件可能会导致应用程序中发生异常行为。//
如果重新生成代码,则将覆盖对此文件的手动更改。//

</auto-generated>//------------------------------------------------------------------------------
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)
{
thrownew UnintentionalCodeFirstException();
}
public DbSet<BlogArticle> BlogArticle { get; set; }
public DbSet<BlogCategoryRepository> BlogCategory { get; set; }
public DbSet<BlogComment> BlogComment { get; set; }
public DbSet<BlogDigg> BlogDigg {
get; set; }
public DbSet<BlogMaster> BlogMaster
{ get; set; }
public DbSet<BlogTag> BlogTag {
get; set; }
}
}

 

  EF提供了强大的查询框架,有时间再写篇探讨自定义查询的,至此,不必犹豫不决了,可以在项目中实践一下。

 

  如果表设计DateTime字段不允许为空,EF执行SaveChanges会出错,建议使用DateTime2类型解决(SQL2008以后版本)