程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 數據層的設計

數據層的設計

編輯:關於.NET

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.這個屬於下個系列內容。
      主要用來搭配模塊化做業務垂直分庫用.
  重點是看設計思路,代碼只是給一個演示,一般照搬是編譯不過的.
      文章系列的結尾會放出一個完整的設計代碼和一個簡單的示例.
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved