zoukankan      html  css  js  c++  java
  • CRUD全栈式编程架构之数据层的设计

    CodeFirst

      一直以来我们写应用的时候首先都是创建数据库
      终于在orm支持codefirst之后,我们可以先建模。
      通过模型去创建数据库,并且基于codefirst可以实现方便的
      实现数据库迁移的工作.使用codefirst有以下几个技巧,
      以EntityFramework为例,结合我这个设计做了以下改进

    1.模型的识别

      建立一个基类命名Entity,里面只有一个long类型的id字段。
      所有需要映射到数据库的模型都继承自Entity,

    public class Entity
    {
      public  long Id { get; set; }
    }
    

    2.模型的映射

      选用fluntapi作为配置(可以保持模型类的整洁,并且和具体orm无关)
          新建一个配置基类继承自EntityTypeConfiguration,并且添加泛型约束,
          在构造函数中配置表名(和类名一致),和id作为主键,并且设置成由程序生成。
          如果是一般单表的话,配合System.ComponentModel.DataAnnotations下的特性
          即可完成数据库字段长度等等限制

    public class BaseEntityTypeConfig<TEntity> : EntityTypeConfiguration<TEntity>, IEntityConfiguration where TEntity : class, Entity
    {
        public BaseEntityTypeConfig()
        {
            HasKey(item => item.Id);
            Property(item => item.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    
            ToTable(typeof(TEntity).Name);
        }
    }
    

     3.模型的生成

      重写DbContext的OnModelCreating方法,反射程序集所有需要映射的类型,
      然后,查找对应配置,如果没有则构造出配置基类,即可完成模型的创建,
      ef默认会在第一次访问的时候去创建或者校验模型,
      模型的配置缓存在全局静态数据中。如果对应模型类有

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //这里 获取所有已经存在的配置
        var configs = MetaDataManager.Type.Find(item =>
        {
            if (item.BaseType == null || !item.BaseType.IsGenericType)
                return false;
            if (item.BaseType.GetGenericTypeDefinition() != typeof(BaseEntityTypeConfig<>))
                return false;
            var genericType =
                item.BaseType.GetGenericArguments()
                    .FirstOrDefault(data => data.IsSubclassOf(typeof(Entity)));
            if (genericType == null)
                return false;
            return _context.Types.Contains(genericType);
        }).ToDictionary(item => item.BaseType.GetGenericArguments().FirstOrDefault(data => data.IsSubclassOf(typeof(Entity))), item => item);
    
        //如果对应的实体有配置则从配置生成,如果没有配置,那么默认给出配置
        _context.Types.ForEach(item =>
        {
            if (IgnoreAttribute.IsDefined(item))
                return;
            Type type;
            if (!configs.TryGetValue(item, out type))
                type = typeof(BaseEntityTypeConfig<>).MakeGenericType(item);
            dynamic config = Activator.CreateInstance(type);
            modelBuilder.Configurations.Add(config);
        });
    
    
    }
    

    Repository

      关于Repository的文章很多,这里就不重复描述了。
      我这里都是采用的接口编程,全部是采用构造函数来注入。
      我这里需要为每个类型的CurdService注入一个默认Repository实现。

    Assembly.GetExecutingAssembly().GetTypes().Where(item => item.IsSubclassOf(typeof (Entity)))
                  .ForEach(item =>
                  {
                      var interfaceType = typeof (IRepository<>).MakeGenericType(item);
                      var classType = typeof (Repository<>).MakeGenericType(item);
    
                      UnityService.RegisterType(interfaceType, classType);
                  });
    

    其他一些技术

    UnitOfWork(工作单元)

      网上文章也很多,简单来说,把若干个数据库操作放在一起作为事务提交
      得益于ef的设计,ef使用dbcontext.savechanges()方法等价于unitofwork.commit()方法
      这部分的设计主要借鉴NLayerApp.
      如果没有ef我建议是把每一个增删改类型的sql命令做成委托,然后左后commit的时候
      使用事务提交。代码如下

    List<Action<DbConnection>> works = new List<Action<DbConnection>>();
    
    public void Excute(Action<DbConnection> work)
    {
        works.Add(work);
    }
    
    public void Commint()
    {
        using (TransactionScope ts = new TransactionScope())
        {
            using (var conn = new SqlConnection("{链接字符串}"))
            {
                works.ForEach(work => work(conn));
            }
            ts.Complete();
        }
    }
    

    Specification(规约)

      同样网上的文章也很多,我这里只是把他作为查询实体来使用.
      如果使用传统ado.net的方式,直接传表达式到Repository中的话
      将导致解析表达式特别复杂,而用Specification的话,相当于查询
      语句中的where部分由它来接管,这样解耦和Repository和具体orm的依赖
      这部分的设计主要借鉴NLayerApp

    多排序

      在分页中我们经常遇到多查询的情况,核心就是构造表达式,等同于构造
      sql语句中orderby的部分,同时它也支持内存中的多排序
      这部分设计主要借鉴Apworks中多排序的设计

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using Coralcode.Framework.Models;
    
    namespace Coralcode.Framework.Domains
    {
        public class SortExpression<TEntity> where TEntity : class 
        {
            /// <summary>
            /// key:属性名,value,true为升序,false为降序
            /// </summary>
            private readonly List<EditableKeyValuePair<Expression<Func<TEntity, dynamic>>, bool>> _sortList;
    
            public SortExpression(List<EditableKeyValuePair<Expression<Func<TEntity, dynamic>>, bool>> sortList)
            {
                _sortList = sortList;
            }
    
            /// <summary>
            /// 如果为空则不需要排序
            /// </summary>
            /// <returns></returns>
            public bool IsNeedSort()
            {
                return _sortList.Count != 0;
            }
    
            public IQueryable<TEntity> BuildSort(IQueryable<TEntity> query)
            {
                if (_sortList == null || _sortList.Count == 0)
                    return query;
                _sortList.ForEach(item =>
                {
                    //获取表达式变量参数 item
                    var parameter = item.Key.Parameters[0];
                    
                    //解析属性名
                    Expression bodyExpression = null;
                    if (item.Key.Body is UnaryExpression)
                    {
                        UnaryExpression unaryExpression = item.Key.Body as UnaryExpression;
                        bodyExpression = unaryExpression.Operand;
                    }
                    else if (item.Key.Body is MemberExpression)
                    {
                        bodyExpression = item.Key.Body;
                    }
                    else
                        throw new ArgumentException(@"The body of the sort predicate expression should be either UnaryExpression or MemberExpression.", "sortPredicate");
                    MemberExpression memberExpression = (MemberExpression)bodyExpression;
                    string  propertyName = memberExpression.Member.Name;
    
                    //根据属性名获取属性 
                    var property = typeof(TEntity).GetProperty(propertyName);
    
                    //创建一个访问属性的表达式 item.property
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    
                    //创建表达式 item=>item.property
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    
                    var resultExp = Expression.Call(typeof(Queryable),
                        item.Value ?  "OrderBy":"OrderByDescending" ,
                        new[] { typeof(TEntity), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));
                    query = query.Provider.CreateQuery<TEntity>(resultExp);
                });
                return query;
            }
    
    
            public IEnumerable<TEntity> BuildSort(IEnumerable<TEntity> query)
            {
                if (_sortList == null || _sortList.Count == 0)
                    return query;
                _sortList.ForEach(item =>
                {
                    //获取表达式变量参数 item
                    var parameter = item.Key.Parameters[0];
    
                    //解析属性名
                    Expression bodyExpression = null;
                    if (item.Key.Body is UnaryExpression)
                    {
                        UnaryExpression unaryExpression = item.Key.Body as UnaryExpression;
                        bodyExpression = unaryExpression.Operand;
                    }
                    else if (item.Key.Body is MemberExpression)
                    {
                        bodyExpression = item.Key.Body;
                    }
                    else
                        throw new ArgumentException(@"The body of the sort predicate expression should be either UnaryExpression or MemberExpression.", "sortPredicate");
                    MemberExpression memberExpression = (MemberExpression)bodyExpression;
                    string propertyName = memberExpression.Member.Name;
    
                    //根据属性名获取属性 
                    var property = typeof(TEntity).GetProperty(propertyName);
    
                    //创建一个访问属性的表达式 item.property
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    
                    //创建表达式 item=>item.property
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    
                    //var resultExp = Expression.Call(typeof(IEnumerable),
                    //    item.Value ? "OrderBy" : "OrderByDescending",
                    //    new[] { typeof(TEntity), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));
                   // query =resultExp.Method.Invoke(query,resultExp.Arguments.ToArray())  query.Provider.CreateQuery<TEntity>(resultExp);
                });
                return query;
            }
    
    
        }
    }
    

    Ps: 

      比较零碎,如果有什么问题可以在下面给我留言。
      其中模型创建代码中有一个_context.这个属于下个系列内容。
          主要用来搭配模块化做业务垂直分库用.
      重点是看设计思路,代码只是给一个演示,一般照搬是编译不过的.
          文章系列的结尾会放出一个完整的设计代码和一个简单的示例.
     

  • 相关阅读:
    sklearn
    Scrapy
    正则表达式re
    BeautifulSoup
    requests
    Python网络爬虫与信息提取
    Matplotlib
    Pandas
    NumPy
    制约大数据处理能力的几个问题
  • 原文地址:https://www.cnblogs.com/Skyven/p/5638932.html
Copyright © 2011-2022 走看看