Entity Framework 系列: 概要

引用:http://www.cnblogs.com/mecity/archive/2011/07/08/2099853.html

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注入)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EF.DAL
{
    public interface 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
{
    public abstract class 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;
        }
        public void 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
{
    public interface IBlogCategoryRepository:IRepository<BlogCategory>
    {
    }
}
BlogArticleRepository實現
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EF.Model;
namespace EF.DAL
{
    public class 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
{
    public class 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);
        }
        public void 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
    {
        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();
        }
    }
}
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)
        {
            throw new 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以後版本)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章