现在的位置: 首页 > 综合 > 正文

entityframework 实体产生类型和分层

2013年01月08日 ⁄ 综合 ⁄ 共 7350字 ⁄ 字号 评论关闭

原文地址 http://www.aspxcs.net/HTML/0956044034.html

记得去年初就开始关注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模型。前者就是后面要提到的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框架的划分的模式
  1. DataBase First
  2. Model First
  3. Code 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视图如下
右键属性选择根据模型生成数据库-> 生成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注入)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace EF.DAL
  6. {
  7. public interface IRepository<T> where T :
    class, new()
  8. {
  9. T Create();
  10. T Update(T entity);
  11. T Insert(T entity);
  12. void Delete(T entity);
  13. T Find(params object[] keyValues);
  14. List<T> FindAll();
  15. }
  16. }

RepositoryBase 抽象基类实现

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data;
  6. using System.Data.Entity;
  7. using EF.DAL;
  8. namespace EF.DAL
  9. {
  10. public abstract
    class RepositoryBase<T>:IRepository<T> where T :class,new()
  11. {
  12. public DbContext context;
  13. //提供IOC注入方式接口
  14. public RepositoryBase(DemoDBEntities context)
  15. {
  16. this.context = context;
  17. }
  18. //测试用
  19. public RepositoryBase()
  20. {
  21. this.context = new DemoDBEntities();
  22. }
  23. #region IRepository<T> 成员
  24. public T Create()
  25. {
  26. return context.Set<T>().Create();
  27. }
  28. public T Update(T entity)
  29. {
  30. //执行验证业务
  31. //context.Entry<T>(entity).GetValidationResult();
  32. if (context.Entry<T>(entity).State == EntityState.Modified)
  33. context.SaveChanges();
  34. return entity;
  35. }
  36. public T Insert(T entity)
  37. {
  38. context.Set<T>().Add(entity);
  39. context.SaveChanges();
  40. return entity;
  41. }
  42. public void Delete(T entity)
  43. {
  44. context.Set<T>().Remove(entity);
  45. context.SaveChanges();
  46. }
  47. public T Find(params
    object[]keyValues)
  48. {
  49. return context.Set<T>().Find(keyValues);
  50. }
  51. public List<T> FindAll()
  52. {
  53. return context.Set<T>().ToList();
  54. }
  55. #endregion
  56. }
  57. }

IBlogCategoryRepository 接口(IOC注入)

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using EF.Model;
  6. namespace EF.DAL
  7. {
  8. public interface IBlogCategoryRepository:IRepository<BlogCategory>
  9. {
  10. }
  11. }

BlogArticleRepository实现

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using EF.Model;
  6. namespace EF.DAL
  7. {
  8. public class BlogArticleRepository:RepositoryBase<BlogArticle>,IBlogArticleRepository
  9. {
  10. }
  11. }
看看后面两个具体数据操作类的代码极其简单,这就是EF4.0 之后的泛型的优点 ,可以使代码尽量的简洁。
4.EF.BLL层简单的实现一下业务
BlogCategoryService 实现关联表操作(添加一个BlogCategory分类,并且在这个分类下增加一个BlogArticle文章)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using EF.DAL;
  6. using EF.Model;
  7. namespace EF.BLL
  8. {
  9. public class BlogCategoryService
  10. {
  11. IRepository<BlogCategory> repositoryCategory;
  12. IRepository<BlogArticle> repositoryArticle;
  13. public BlogCategoryService(IRepository<BlogCategory> repositoryCategory,IRepository<BlogArticle> repositoryArticle)
  14. {
  15. this.repositoryCategory = repositoryCategory;
  16. this.repositoryArticle = repositoryArticle;
  17. }
  18. public BlogCategoryService()
  19. {
  20. this.repositoryCategory = new BlogCategoryRepository();
  21. this.repositoryArticle =
    new BlogArticleRepository();
  22. }
  23. public BlogCategory CreateBlogCategory()
  24. {
  25. return repositoryCategory.Create();
  26. }
  27. public BlogArticle CreateBlogArticle()
  28. {
  29. return repositoryArticle.Create();
  30. }
  31. public BlogCategory Insert(BlogCategory entity)
  32. {
  33. return repositoryCategory.Insert(entity);
  34. }
  35. public BlogCategory Update(BlogCategory entity)
  36. {
  37. return repositoryCategory.Update(entity);
  38. }
  39. public void Delete(BlogCategory entity)
  40. {
  41. repositoryCategory.Delete(entity);
  42. }
  43. }
  44. }

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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using EF.Model;
  6. using EF.BLL;
  7. namespace EF.Demo
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. BlogCategoryService service=new BlogCategoryService();
  14. //创建博文分类
  15. BlogCategory cate = service.CreateBlogCategory();
  16. cate.CateName = "EF分类标签";
  17. //创建一篇博文
  18. BlogArticle arti = service.CreateBlogArticle();
  19. arti.Title = "EF进化论";
  20. arti.Content = "EF测试内容";
  21. //博文加到博文分类
  22. cate.BlogArticle.Add(arti);
  23. //更新
  24. service.Insert(cate);
  25. Console.ReadLine();
  26. }
  27. }
  28. }
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的构造函数禁用延迟加载。
  1. //------------------------------------------------------------------------------
  2. // <auto-generated>
  3. // 此代码是根据模板生成的。
  4. //
  5. // 手动更改此文件可能会导致应用程序中发生异常行为。
  6. // 如果重新生成代码,则将覆盖对此文件的手动更改。
  7. // </auto-generated>
  8. //------------------------------------------------------------------------------
  9. namespace EF.DAL
  10. {
  11. using System;
  12. using System.Data.Entity;
  13. using System.Data.Entity.Infrastructure;
  14. using EF.Model;
  15. public partial class DemoDBEntities : DbContext
  16. {
  17. public DemoDBEntities()
  18. : base("name=DemoDBEntities")
  19. {
  20. //禁用延迟加载
  21. this.Configuration.LazyLoadingEnabled =
    false;
  22. //禁用代理
  23. this.Configuration.ProxyCreationEnabled =
    false;
  24. }
  25. protected override
    void OnModelCreating(DbModelBuilder modelBuilder)
  26. {
  27. throw new UnintentionalCodeFirstException();
  28. }
  29. public DbSet<BlogArticle> BlogArticle {
    get; set; }
  30. public DbSet<BlogCategoryRepository> BlogCategory {
    get; set; }
  31. public DbSet<BlogComment> BlogComment {
    get; set; }
  32. public DbSet<BlogDigg> BlogDigg {
    get; set; }
  33. public DbSet<BlogMaster> BlogMaster {
    get; set; }
  34. public DbSet<BlogTag> BlogTag {
    get; set; }
  35. }
  36. }

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

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

 

抱歉!评论已关闭.