[轉]Model First+DbContext Generator生成基於Code First方式代碼的EDM文件


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{
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以后版本)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM