應用程序框架實戰二十:映射層超類型


  上一篇介紹了工作單元層超類型的封裝演化過程,本文將介紹對Entity Framework映射層超類型的封裝。

  使用Entity Framework一般需要映射三種類型的對象,即實體、聚合、值對象。

  聚合與實體映射的主要區別是:聚合映射單屬性標識Id,並需要映射樂觀離線鎖Version,而實體的標識往往需要映射成復合屬性,這樣方便物理刪除聚合中的實體。Entity Framework通過EntityTypeConfiguration進行實體映射。

  值對象以嵌入值模式映射,這需要使用ComplexTypeConfiguration。

  封裝映射配置並不是必須的,但封裝以后可以獲得如下好處。

  1. 輔助記憶

  如果你跟我一樣記憶力很差,記不住上面兩個類名,那么通過封裝一個自定義的類型可以幫助你進行記憶。一旦封裝完成,你就可以把系統或第三方的Api扔到一邊。

  2. 划分邏輯結構

  把所有映射代碼放到一個方法,不方便閱讀,我把它們划分成不同的方法,可以獲得更清晰的結構。

  3. 減少代碼冗余

  對於聚合而言,可以把Id標識和Version樂觀離線鎖封裝到層超類型,從而減少代碼冗余。  

映射層超類型實現

  實體映射基類EntityMapBase

  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

  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

  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 


免責聲明!

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



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