引用: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框架的劃分的模式
- DataBase First
- Model First
- 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以後版本)