• 映射层超类型


    映射层超类型

    上一篇介绍了工作单元层超类型的封装演化过程,本文将介绍对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 

    版权所有,转载请注明出处 何镇汐的技术博客
     
  • 相关阅读:
    JS 定义函数的参数的个数,传入参数的个数对调用的影响
    jQuery常见操作总结
    jQuery hover事件
    Ajax实现登陆并友好提示错误信息
    JS实现弹出登录框
    .NET源码 SortedSet(红黑树)
    C#链接SQL Server数据库
    628. Maximum Product of Three Numbers 最大的三个数的乘积
    501. Find Mode in Binary Search Tree 找二叉搜索树的众数
    235. Lowest Common Ancestor of a Binary Search Tree 二叉搜索树的LCA
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4151953.html
Copyright © 2020-2023  润新知