基于S#arp生成领域实体数据库字典

2 篇文章 0 订阅

   NHibernate 

     

       NHibernate是一个面向.NET环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping,ORM)这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。 

       对象和关系数据库之间的映射是用一个XML文档(XML document)来定义的。这个映射文档被设计为易读的,并且可以手工修改。映射语言是以.NET为中心的,意味着映射是按照持久化类的定义来创建的,而非表的定义。在Hibernate中XML映射文档可以手动定义,也有一些工具来辅助生成,包括Xdoclet、Middlegen和AndroMDA,但是在NHibernate文档中并没有上述的辅助工具,不过可以采用MyGeneration这样的代码生成工具来生成XML配置文档。

   FluentNHibernate

      连贯NHibernate(FluentNHibernate)是通过编程方式而不是使用XML配置创建NHiberne映射的API。它的目标是在项目中运用NHibernate时减少所遇到的困难,提供更好的可读性、试性 和编译时的安全性。

     有以下特性:

  • ■ 自动映射(Automapping)
  • ■ 流映射(Fluent Mapping)
  • ■ 约定(Conventions)
  • ■ 配置(Configuration)
  • ■ 持久化测试(Persistence Testing)

   S#arp架构 

       Sharp Architecture 是一个用来构建易于维护的 Web应用框架,基于 ASP.NET MVC 和NHibernate. 
无论使用任何框架,其主要优点是减少冗余的代码和提高最终项目的质量。框架使开发人员,有更多的时间
把注意力放在自己的领域和用户体验上。

     有以下特性:

  • ■ 基于领域设计驱动(Domain Driven Design)
  • ■ 模块的松耦合
  • ■ 预配置的基础设施
  • ■ 开源项目

      框架分层:

  • ■ 表示层(Presentation
  • ■ 领域实体层(Domain)
  • ■ 服务层(Tasks)
  • ■ 基础设施层(Infrastructure)
  • ■ 测试层(Specs/Tests)


   想法

       NHibernate ORM框架一定会记录实体的Mapping信息,跟数据库进行通信。所以查找S#ARP框架是否有封装好的Mapping信息的获取方法,没有就在从NHibernate API 里面找,如果找到了实体的Mapping信息后,就可以知道了表的数据结构信息。然后通过自定义描述特性(Attribute),为领域实体的类和属性上加上描述特性,通过反射获取上面的特性信息作为表和字段的描述。


   实践 - 准备工作


      定义描述特性:

      

        实体描述特性抽象类:

    /// <summary>
    /// 抽象 - 实体描述
    /// </summary>
    public abstract class BaseEntityDescAttribute : Attribute
    {
        /// <summary>
        /// 描述
        /// </summary>
        public readonly string Description;
          
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="description">描述</param> 
        protected BaseEntityDescAttribute(string description)
        { 
            Description = description;
        } 
    }
         实体描述:
    /// <summary>
    /// 实体描述特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = true)]
    public class EntityDescAttribute : BaseEntityDescAttribute
    { 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="description">描述</param>
         public EntityDescAttribute(string description)
             : base(description){ 
        }
    }

      实体属性描述:

    /// <summary>
    /// 实体属性描述
    /// </summary>
    [AttributeUsage(AttributeTargets.Property,AllowMultiple = false)]
    public class EntityPropertyDescAttribute : BaseEntityDescAttribute
    {
        /// <summary> 
        /// 构造函数
        /// </summary>
        /// <param name="description">实体描述</param>
        public EntityPropertyDescAttribute(string description)
            : base(description)
        {
        }
    }

     例子:User实体

     Mapping信息如下:

   public sealed class UserMap : ClassMap<User>
    {
        public UserMap()
        {
            Table(MapConst.Mapname + "Users");
            Id(m => m.Id).Column("UserId").GeneratedBy.HiLo(
                MapConst.Mapname + "hibernate_unique_key",
                "next_hi", 
                "0", 
                p =>p.AddParam("where", 
            string.Format(
            "table_name = '{0}'",
            MapConst.Mapname + "Users")));

            Map(m => m.UserName).Length(100).Column("UserName");
            Map(m => m.UserNo).Length(100);
            Map(m => m.ImgHead).Length(200);
            Map(m => m.SmallImgHead).Length(200);
            Map(m => m.StandardImgHead).Length(200); 
            Map(m => m.Domain).Length(15);
            Map(m => m.Email).Length(30);
            Map(m => m.Card).Length(20); 
            Map(m => m.UserSex).CustomType<UserSex>(); 
            Map(m => m.UserState).CustomType<UserState>(); 
            Map(m => m.Phone).Length(15);  
            Map(m => m.Paw);
            Map(m => m.RealName).Length(25);
            Map(m => m.EmailValidate);
            Map(m => m.QQ).Length(30); 
            Map(m => m.CreateDate); 
            Map(m => m.LastUpdate);
            Map(m => m.LoginDate);
            Map(m => m.LastLoginDate);
            Map(m => m.LoginIp).Length(15);
            Map(m => m.LastLoginIp).Length(15);
            Map(m => m.DesPassword); 
        } 
    }

      实体信息如下:

    /// <summary>
    /// 用户
    /// </summary> 
    [EntityDesc("用户,用于记录平台用户信息")]
    public class User : Entity
    {
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public User()
        {
        }

        /// <summary>
        /// 用户名
        /// </summary>
        [DomainSignature]
        [Length(255, Message = "用户名不能超出255个字符")]
        [NotNullNotEmpty]
        [EntityPropertyDesc("用户名")]
        public virtual string UserName { get; set; }

        /// <summary>
        /// 用户编码 
        /// </summary>
        [EntityPropertyDesc("用户编码")]
        public virtual string UserNo { get; set; }

        /// <summary>
        /// 密码
        /// </summary>
        [Length(255, Message = "用户名不能超出255个字符")]
        [NotNullNotEmpty]
        [EntityPropertyDesc("密码")]
        public virtual string Paw { get; set; }

        /// <summary>
        /// 用户头像(原图)
        /// </summary>
        [EntityPropertyDesc("用户头像(原图)")]
        public virtual string ImgHead { get; set; }

        /// <summary>
        /// 用户头像(小图) 50 X 50
        /// </summary>
        [EntityPropertyDesc("用户头像(小图) 50 X 50")]
        public virtual string SmallImgHead { get; set; }

        /// <summary>
        /// 用户头像(标准图) 150 X 150
        /// </summary>
        [EntityPropertyDesc("用户头像(标准图) 150 X 150")]
        public virtual string StandardImgHead { get; set; }

        /// <summary>
        /// 域名地址
        /// </summary>
        [EntityPropertyDesc("域名地址")]
        public virtual string Domain { get; set; }

        /// <summary>
        /// 邮件
        /// </summary>
        [EntityPropertyDesc("邮件")]
        public virtual string Email { get; set; }

        /// <summary>
        /// 身份证
        /// </summary>
        [EntityPropertyDesc("身份证")]
        public virtual string Card { get; set; }

        /// <summary>
        /// 性别
        /// </summary>
        [EntityPropertyDesc("性别")]
        public virtual UserSex? UserSex { get; set; }

        /// <summary>
        /// 用户状态
        /// </summary>
        [EntityPropertyDesc("用户状态")]
        public virtual UserState UserState { get; set; }

        /// <summary>
        /// 用户手机号码
        /// </summary>
        [Length(20, Message = "手机号码不能超出22个字符")]
        [EntityPropertyDesc("用户手机号码")]
        public virtual string Phone { get; set; }

        /// <summary>
        /// 明文密码
        /// </summary>
        [EntityPropertyDesc("明文密码")]
        public virtual string DesPassword { get; set; }

        /// <summary>
        /// 真实名称
        /// </summary>
        [EntityPropertyDesc("真实名称")]
        public virtual string RealName { get; set; }

        /// <summary>
        /// 是否通过邮箱验证
        /// </summary>
        [EntityPropertyDesc("是否通过邮箱验证")]
        public virtual bool? EmailValidate { get; set; }

        /// <summary>
        /// QQ
        /// </summary>
        [EntityPropertyDesc("QQ")]
        [Length(20, Message = "QQ不能超出22个字符")]
        public virtual string QQ { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        [EntityPropertyDesc("创建时间")]
        public virtual DateTime CreateDate { get; set; }

        /// <summary>
        /// 最后修改时间
        /// </summary>
        [EntityPropertyDesc("最后修改时间")]
        public virtual DateTime? LastUpdate { get; set; }

        /// <summary>
        /// 当前登录时间
        /// </summary>
        [EntityPropertyDesc("当前登录时间")]
        public virtual DateTime? LoginDate { get; set; }

        /// <summary>
        /// 上次登录时间
        /// </summary>
        [EntityPropertyDesc("上次登录时间")]
        public virtual DateTime? LastLoginDate { get; set; }

        /// <summary>
        /// 当前登录IP
        /// </summary>
        [EntityPropertyDesc("当前登录IP")]
        public virtual string LoginIp { get; set; }

        /// <summary>
        /// 最后登录IP
        /// </summary>
        [EntityPropertyDesc("最后登录IP")]
        public virtual string LastLoginIp { get; set; }

        /// <summary>
        /// 用户收货地址
        /// </summary>
        [EntityPropertyDesc("用户收货地址")]
        public virtual IList<UserAddress> UserAddress { get; set; }

        /// <summary>
        /// 用户订单
        /// </summary>
        [EntityPropertyDesc("用户订单")]
        public virtual IList<ProductOrder> ProductOrders { get; set; }
    }


   实践 - 生成数据库字典

      使用单元测试生成数据库字典

      单元测试代码如下:

        /// <summary>
        /// 文件输出目录
        /// </summary>
        private const string DB_FOLDER_PATH = "../../../../db/";

        /// <summary>
        /// 领域对象DLL名
        /// </summary>
        private const string DomainDll = "Cotide.Domain";

        /// <summary>
        /// NHibernate Configuration
        /// </summary>
        private static NHibernate.Cfg.Configuration _nhConfig;

        [Test]
        public void WriteDomainDbInfo()
        {
            // 获取NHibernate 获取领域实体元数据集合 
            // 通过NHibernateSession静态类 -> 获取默认工厂 -> 获取所有领域实体元数据
            var allClassMetadata = NHibernateSession.
                GetDefaultSessionFactory().GetAllClassMetadata();

            // 输出内容变量
            var str = new StringBuilder();
            // 遍历元数据
            foreach (var entry in allClassMetadata)
            {
                // 获取当前元数据
                var entity = ((SingleTableEntityPersister)entry.Value);
                // 获取实体映射的表名
                var tableName = entity.TableName;
                // 领域实体名称
                var entityName = entity.EntityName; 
                // 领域实体Mapping对象
                var userClassMap = _nhConfig.GetClassMapping(entityName); 
                // 领域实体主键名
                var primaryKey = userClassMap.Table.PrimaryKey.Columns.FirstOrDefault().Text;
                // 领域实体下的属性列表
                var column = userClassMap.Table.ColumnIterator;

                // 输出表头
                str.Append(string.Format("<H1>{0}({1})</H1>", tableName, entityName));

                // 获取实体描述
                // 使用反射dll -> 获取EntityDescAttribute特性描述内容
                var type = Type.GetType(entityName + "," + DomainDll);
                if (type != null)
                {
                    var attr = type.GetCustomAttributes(typeof(EntityDescAttribute), true);
                    if (attr.Length > 0)
                    {
                        str.Append(string.Format("<P>表描述:{0}</P>",
                            ((EntityDescAttribute)attr[0]).Description));
                    }
                }
                 
                // 输出表格表头
                str.Append("<table width=\"100%\" " +
                           "border=\"1\" " + 
                           "cellspacing=\"0\" " +
                           "bordercolor=\"#333\" >");
                str.Append("<tr style='background-color:#3AF;'>" +
                           "<td>序号</td>" +
                           "<td>数据库字段</td>" +
                           "<td>类型</td>" +
                           "<td>描述</td>" +
                           "<td>唯一约束</td>" +
                           "<td>允许为NULL</td>" +
                           "<td>是否主键</td>" +
                           "</tr>");
                // 遍历领域实体下的属性
                // 顺序索引
                var index = 0;
                foreach (var obj in column)
                {
                    // 属性描述
                    var attrDesc = "";

                    // 获取NHibernate Column对象
                    var item = (Column)obj.Value.ColumnIterator.FirstOrDefault();
                    if (item == null)
                        continue;

                    // 判断如果为领域实体唯一标识则不获取属性描述
                    if (primaryKey == item.Text)
                    {
                        //唯一标识
                        attrDesc = "唯一标识";
                    } 
                    else
                    {
                        // 如果超出索引的 -> 判断为非数据库字段,为领域实体属性
                        // -> 跳出本次处理
                        if(index >= entity.PropertyNames.Count())
                        {
                            continue;
                        }

                        // 获取属性上的EntityPropertyDescAttribute特性描述内容 
                        var memberItem = entity.PropertyNames[index];
                        if(type!=null)
                        {
                            var property = type.GetProperty(memberItem);
                            var memberAttr = property.GetCustomAttributes(
                                typeof(EntityPropertyDescAttribute), true);

                            if (memberAttr.Length > 0)
                            {
                                attrDesc = ((EntityPropertyDescAttribute)
                                    memberAttr[0]).Description;
                            } 
                        }
                        ++index;
                    }

                    // 输出内容
                    str.Append(string.Format("<tr>" +
                                             "<td>{0}</td>" +
                                             "<td>{1}</td>" +
                                             "<td>{2}</td>" +
                                             "<td>{3}</td>" +
                                             "<td>{4}</td>" +
                                             "<td>{5}</td>" +
                                             "<td>{6}</td>" +
                                             "</tr>",
                          index + 1,
                          item.Text, GetType(item.Value.Type),
                          attrDesc,
                          item.IsUnique ? "Yes" : "No",
                          item.IsNullable ? "Yes" : "No",
                          (primaryKey == obj.Name) ? "Yes" : "No")); 
                }
                str.Append("</table>");
            }
            // 输出数据库字典文件
            // 文件名
            const string outputFileName = "数据库字典.doc";
            // 输出文件夹路径
            string outputFilePath = Path.Combine(DB_FOLDER_PATH, outputFileName);
            // 输出操作
            File.WriteAllText(outputFilePath, str.ToString(), Encoding.GetEncoding("utf-8"));
        }

        #region Helper 

        /// <summary>
        /// 获取数据库类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static string GetType(NHibernate.Type.IType type)
        {
            if (type is NHibernate.Type.Int32Type)
            {
                return "int";
            }
            if (type is NHibernate.Type.StringType)
            {
                var sqlType = ((NHibernate.Type.StringType)type).SqlType;
                return string.Format("nvarchar({0})", GetLength(sqlType));
            }
            if (type is NHibernate.Type.BooleanType)
            {
                return "bit";
            }
            if (type is NHibernate.Type.DateTimeType)
            {
                return "datetime";
            }
            if (type is NHibernate.Type.DecimalType)
            {
                return "decimal";
            }
            if (type is NHibernate.Type.DoubleType)
            {
                return "double";
            }
            if (type is NHibernate.Type.Int64Type)
            {
                return "bitint";
            }
            if (type is NHibernate.Type.CharType)
            {
                return "char";
            }
            if (type is NHibernate.Type.TimestampType)
            {
                return "timestamp";
            }
            if (type is NHibernate.Type.Int16Type)
            {
                return "tinyint";
            }
            if (type is NHibernate.Type.XmlDocType)
            {
                return "xml";
            }
            if (type is NHibernate.Type.BinaryType)
            {
                var sqlType = ((NHibernate.Type.StringType)type).SqlType;
                return string.Format("varbinary({0})", sqlType.Length > 4000 ? "max" : sqlType.Length.ToString());
            }

            if (type is NHibernate.Type.PersistentEnumType)
            {
                var enumType = (NHibernate.Type.PersistentEnumType)type;
                return enumType.SqlType.DbType.ToString() == "Int32" ? "int" : "nvarchar(255)";
            }
            if (type is NHibernate.Type.EntityType)
            {
                // 暂时使用这种方式,以后重构
                return "int";
            }
            return "未知";
        }

        /// <summary>
        /// 获取SQL长度
        /// </summary>
        /// <param name="sqlType">SQL类型</param>
        /// <returns></returns>
        private static string GetLength(SqlType sqlType)
        {
            return sqlType.Length > 4000 ? "max" : sqlType.Length <= 0 ? "255" : sqlType.Length.ToString();
        }

        #endregion

NHibernate.Type.IType 对应的SQL类型 因为没有文档,所以都是自己去摸索,所以这部分

代码可能以后需要重构,但对于常用的类型都已经包括在里面了。

   结果

      在输出目录下生成数据库字典:

      

      字典内容如下:

      

   参考资料

  • ■ NHibernate官方(http://nhforge.org/)
  • ■ FluentnHibernate官方(http://www.fluentnhibernate.org/)
  • ■ Sharp Architecture(https://github.com/sharparchitecture)

   总结

      DBA负责数据库的管理的工作的时候,肯定需要数据库字典。

      如果每次手动维护数据库字典,多痛苦的事情。

      为了偷懒,所以花了几天时间去了解框架和一些类库的API,虽然过程是痛苦的,

      但做出来一点东西的时候挺满足的。

      “ 当面对框架的时候,如果带着设计的思想去阅读它,你就会发现里面其实很多东西都很巧妙 ” 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值