上一篇介紹了工做單元層超類型的封裝演化過程,本文將介紹對Entity Framework映射層超類型的封裝。框架
使用Entity Framework通常須要映射三種類型的對象,即實體、聚合、值對象。ide
聚合與實體映射的主要區別是:聚合映射單屬性標識Id,並須要映射樂觀離線鎖Version,而實體的標識每每須要映射成複合屬性,這樣方便物理刪除聚合中的實體。Entity Framework經過EntityTypeConfiguration進行實體映射。ui
值對象以嵌入值模式映射,這須要使用ComplexTypeConfiguration。spa
封裝映射配置並非必須的,但封裝之後能夠得到以下好處。code
1. 輔助記憶。對象
若是你跟我同樣記憶力不好,記不住上面兩個類名,那麼經過封裝一個自定義的類型能夠幫助你進行記憶。一旦封裝完成,你就能夠把系統或第三方的Api扔到一邊。blog
2. 劃分邏輯結構。繼承
把全部映射代碼放到一個方法,不方便閱讀,我把它們劃分紅不一樣的方法,能夠得到更清晰的結構。接口
3. 減小代碼冗餘。ci
對於聚合而言,能夠把Id標識和Version樂觀離線鎖封裝到層超類型,從而減小代碼冗餘。
EntityMapBase從EntityTypeConfiguration繼承,泛型參數TEntity使用IEntity接口約束,構造方法將映射配置從邏輯上分離到4個方法中,即映射表、映射標識、映射屬性、映射導航屬性。
在構造方法中調用虛方法有時候可能致使意想不到的錯誤,這種狀況發生在子類構造方法的代碼依賴某些虛方法,因爲調用順序混亂可能致使失敗,不過這種狀況仍是比較少見,若是你碰到上述問題,請果斷扔掉該映射基類,直接從EntityTypeConfiguration派生。
EntityMapBase用於映射實體,代碼以下。
using System.Data.Entity.ModelConfiguration; using Util.Domains; namespace Util.Datas.Ef { /// <summary>
/// 實體映射 /// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
public abstract class EntityMapBase<TEntity> : EntityTypeConfiguration<TEntity> where TEntity : class, IEntity { /// <summary>
/// 初始化映射 /// </summary>
protected EntityMapBase() { MapTable(); MapId(); MapProperties(); MapAssociations(); } /// <summary>
/// 映射表 /// </summary>
protected abstract void MapTable(); /// <summary>
/// 映射標識 /// </summary>
protected abstract void MapId(); /// <summary>
/// 映射屬性 /// </summary>
protected virtual void MapProperties() { } /// <summary>
/// 映射導航屬性 /// </summary>
protected virtual void MapAssociations() { } } }
AggregateMapBase繼承於EntityMapBase,並重寫了MapId和MapProperties,對標識Id和樂觀鎖進行映射。
另外,提供了兩個泛型版本的AggregateMapBase, 提供AggregateMapBase<TEntity>的目的是使聚合映射更易用,由於個人大多數聚合都使用Guid類型,這樣能夠省一個參數。
AggregateMapBase用於映射聚合,代碼以下。
using System; using System.ComponentModel.DataAnnotations.Schema; using Util.Domains; namespace Util.Datas.Ef { /// <summary>
/// 聚合根映射 /// </summary>
/// <typeparam name="TEntity">聚合根類型</typeparam>
/// <typeparam name="TKey">實體標識類型</typeparam>
public abstract class AggregateMapBase<TEntity, TKey> : EntityMapBase<TEntity> where TEntity : AggregateRoot<TKey> { /// <summary>
/// 映射標識 /// </summary>
protected override void MapId() { HasKey( t => t.Id ); } /// <summary>
/// 映射屬性 /// </summary>
protected override void MapProperties() { Property( t => t.Version ).HasColumnName( "Version" ).IsRowVersion().HasDatabaseGeneratedOption( DatabaseGeneratedOption.Computed ).IsOptional(); } } /// <summary>
/// 聚合根映射 /// </summary>
/// <typeparam name="TEntity">聚合根類型</typeparam>
public abstract class AggregateMapBase<TEntity> : AggregateMapBase<TEntity, Guid> where TEntity : AggregateRoot<Guid> { } }
ValueObjectMapBase從ComplexTypeConfiguration繼承,它惟一須要的就是映射屬性,建立這個類只有一個緣由——幫助你記憶。
ValueObjectMapBase用於映射值對象,代碼以下。
using System.Data.Entity.ModelConfiguration; namespace Util.Datas.Ef { /// <summary>
/// 值對象映射 /// </summary>
/// <typeparam name="TValueObject">值對象類型</typeparam>
public abstract class ValueObjectMapBase<TValueObject> : ComplexTypeConfiguration<TValueObject> where TValueObject : class { /// <summary>
/// 初始化值對象映射 /// </summary>
protected ValueObjectMapBase() { MapProperties(); } /// <summary>
/// 映射屬性 /// </summary>
protected abstract void MapProperties(); } }
之因此說映射基類不是必須的,是由於映射配置通常由代碼生成器建立,因此可以從基類得到的好處不是很是明顯。另外,不少人會以爲這致使過分封裝。建立這幾個類在很大程度上屬於我我的習慣問題,介紹它們的目的是想告訴你,若是不想動腦筋記憶,就本身封裝一層。
.Net應用程序框架交流QQ羣: 386092459,歡迎有興趣的朋友加入討論。
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/xiadao521/
下載地址:http://files.cnblogs.com/xiadao521/Util.2014.12.8.1.rar