歡迎光臨
每天分享高質量文章

.NET Core 2.0+Mysql+AceAdmin 搭建快速開發框架

來自:夢亦曉

鏈接:http://www.cnblogs.com/eggTwo/p/9564101.html

 

前言

 

.NET Core已經出來一段時間了,相信大家對.NET Core的概念已經很清楚了,這裡就不再贅述。

 

筆者目前也用.NET Core做過一些專案,並且將以前framework下的一些經驗移植到了.NET Core下,並結合.NET Core本身的一些特性整理成此框架,以供學習參考。如有不足之處,歡迎指正。

 

先睹為快,演示地址:http://cloud.eggtwo.com/main/index 

 

框架介紹

 

先來一張整體分層結構圖

 

 

基礎層

 

1、Cloud.Core專案是核心專案,主要實現快取的操作、dapper操作、EF Repository、PageList、日誌等操作

 

 

2、Cloud.Utility屬於幫助類

 

領域層

 

3、Cloud.Entity物體物件,存放資料庫映射物體、Fluent API配置、列舉字典、DbContext等

 

 

4、Cloud.UnitOfWork,運算元據庫的網關,裡面封裝了對倉儲的操作、dapper的操作、事務等

 

服務層

 

5、Cloud.Service 業務邏輯的實現

 

 

6、Cloud.Dto 資料傳輸物件,物體物件不直接和表現層接觸,通過dto互轉

 

表現層

 

7、Cloud.Framework,表現層框架,封裝了超類controller,全域性授權過濾器,全域性異常過濾器,ActionFilter,HtmlHelper等操作

 

 

8、Cloud.Boss 啟動專案

 

 

使用的技術

 

  •  基於.NET Core 2.0 的 ASP.NET Core MVC

  •  基於.NET Core 2.0 的 EF

  • Dapper

  • Mysql

  • 前端框架:aceAdmin(http://ace.jeka.by/#)

 

技術要點

 

1、物體基類定義

 

 

2、泛型倉儲的封裝

 

2.1、倉儲接口的定義,泛型約束T必須是BaseEntity型別

 

public interface IRepository where T : BaseEntity

{

    DatabaseFacade Database { get; }

    IQueryable Entities { get; }

    int SaveChanges();

    Task SaveChangesAsync();

    void Disposed();

 

    bool Delete(List entitys, bool isSaveChange = true);

    bool Delete(T entity, bool isSaveChange = true);

    Task DeleteAsync(List entitys, bool isSaveChange = true);

    Task DeleteAsync(T entity, bool isSaveChange = true);

   

    Task GetAsync(Expression> predicate = null);

    Task> GetListAsync(Expression> predicate = null);

    T Get(object id);

    T Get(Expression> predicate = null);

    Task GetAsync(object id);

    Task> LoadAsync(Expression> predicate = null);

 

    bool Insert(List entitys, bool isSaveChange = true);

    bool Insert(T entity, bool isSaveChange = true);

    Task InsertAsync(List entitys, bool isSaveChange = true);

    Task InsertAsync(T entity, bool isSaveChange = true);

   

    bool Update(List entitys, bool isSaveChange = true);

    bool Update(T entity, bool isSaveChange = true, List updatePropertyList = null);

    Task UpdateAsync(List entitys, bool isSaveChange = true);

    Task UpdateAsync(T entity, bool isSaveChange = true, List updatePropertyList = null);

}

 

2.2、倉儲接口的實現

 

public class Repository : IRepository where T : BaseEntity

{

    DbContext _dbContext;

    public Repository(DbContext dbContext)

    {

        _dbContext = dbContext;

    }

    public int SaveChanges()

    {

        return _dbContext.SaveChanges();

    }

    public async Task SaveChangesAsync()

    {

        return await _dbContext.SaveChangesAsync();

    }

    public void Disposed()

    {

        throw new Exception(“不允許在這裡釋放背景關係,請在UnitOfWork中操作”);

        _dbContext.Dispose();

    }

    #region 插入資料

    public bool Insert(T entity, bool isSaveChange = true)

    {

        _dbContext.Set().Add(entity);

        if (isSaveChange)

        {

            return SaveChanges() > 0;

        }

        return false;

    }

    public async Task InsertAsync(T entity, bool isSaveChange = true)

    {

        _dbContext.Set().Add(entity);

        if (isSaveChange)

        {

            return await SaveChangesAsync() > 0;

        }

        return false;

    }

    public bool Insert(List entitys, bool isSaveChange = true)

    {

        _dbContext.Set().AddRange(entitys);

        if (isSaveChange)

        {

            return SaveChanges() > 0;

        }

        return false;

    }

    public async Task InsertAsync(List entitys, bool isSaveChange = true)

    {

        _dbContext.Set().AddRange(entitys);

        if (isSaveChange)

        {

            return await SaveChangesAsync() > 0;

        }

        return false;

    }

    #endregion

 

    #region 更新資料

    public bool Update(T entity, bool isSaveChange = true, List updatePropertyList = null)

    {

        if (entity==null)

        {

            return false;

        }

        _dbContext.Set().Attach(entity);

        if (updatePropertyList==null)

        {

            _dbContext.Entry(entity).State = EntityState.Modified;//全欄位更新

        }

        else

        {

            updatePropertyList.ForEach(c => {

                _dbContext.Entry(entity).Property(c).IsModified = true; //部分欄位更新的寫法

            });

        }

        if (isSaveChange)

        {

            return SaveChanges() > 0;

        }

        return false;

    }

    public bool Update(List entitys, bool isSaveChange = true)

    {

        if (entitys==null||entitys.Count==0)

        {

            return false;

        }

        entitys.ForEach(c => {

            Update(c, false);

        });

        if (isSaveChange)

        {

            return SaveChanges() > 0;

        }

        return false;

    }

    public async Task UpdateAsync(T entity, bool isSaveChange = true, List updatePropertyList = null)

    {

        if (entity == null)

        {

            return false;

        }

        _dbContext.Set().Attach(entity);

        if (updatePropertyList == null)

        {

            _dbContext.Entry(entity).State = EntityState.Modified;//全欄位更新

        }

        else

        {

            updatePropertyList.ForEach(c => {

                _dbContext.Entry(entity).Property(c).IsModified = true; //部分欄位更新的寫法

            });

        }

        if (isSaveChange)

        {

            return await SaveChangesAsync() > 0;

        }

        return false;

    }

    public async Task UpdateAsync(List entitys, bool isSaveChange = true)

    {

        if (entitys == null || entitys.Count == 0)

        {

            return false;

        }

        entitys.ForEach(c => {

            _dbContext.Set().Attach(c);

            _dbContext.Entry(c).State = EntityState.Modified;

        });

        if (isSaveChange)

        {

            return await SaveChangesAsync() > 0;

        }

        return false;

    }

    #endregion

 

    #region 刪除

    public bool Delete(T entity, bool isSaveChange = true)

    {

        _dbContext.Set().Attach(entity);

        _dbContext.Set().Remove(entity);

        return isSaveChange ? SaveChanges() > 0 : false;

    }

    public bool Delete(List entitys, bool isSaveChange = true)

    {

        entitys.ForEach(entity =>

        {

            _dbContext.Set().Attach(entity);

            _dbContext.Set().Remove(entity);

        });

        return isSaveChange ? SaveChanges() > 0 : false;

    }

    public virtual async Task DeleteAsync(T entity, bool isSaveChange = true)

    {

        _dbContext.Set().Attach(entity);

        _dbContext.Set().Remove(entity);

        return isSaveChange ? await SaveChangesAsync() > 0 : false;

    }

    public virtual async Task DeleteAsync(List entitys, bool isSaveChange = true)

    {

        entitys.ForEach(entity =>

        {

            _dbContext.Set().Attach(entity);

            _dbContext.Set().Remove(entity);

        });

        return isSaveChange ? await SaveChangesAsync() > 0 : false;

    }

    #endregion

 

    public IQueryable Entities => _dbContext.Set().AsQueryable().AsNoTracking();

    //public async Task> EntitiesAsync => Task.Run(()=> _dbContext.Set().AsQueryable().AsNoTracking()); 

 

    public DatabaseFacade Database => _dbContext.Database;

    #region 查找

    public T Get(object id)

    {

        return _dbContext.Set().Find(id);

    }

    public T Get(Expression> predicate = null)

    {

        return _dbContext.Set().Where(predicate).AsNoTracking().FirstOrDefault();

    }

    public async Task GetAsync(object id)

    {

        return await _dbContext.Set().FindAsync(id);

    }

    public async Task GetAsync(Expression> predicate = null)

    {

        return await _dbContext.Set().Where(predicate).AsNoTracking().FirstOrDefaultAsync();

    }

    public async Task> GetListAsync(Expression> predicate = null)

    {

        return await _dbContext.Set().Where(predicate).AsNoTracking().ToListAsync();

    }

    public async Task> LoadAsync(Expression> predicate = null)

    {

        if (predicate == null)

        {

            predicate = c => true;

        }

        return await Task.Run(() => _dbContext.Set().Where(predicate).AsNoTracking());

    }

    public void Dispose()

    {

        throw new NotImplementedException();

    }

    #endregion

}

 

3、表部分欄位更新實現

 

EF預設的更新方式是一個物體對應的表全部欄位更新,那麼我們想更新表的部分欄位怎麼處理?

 

首先定義需要更新的欄位:

 

public class PropertyExpression where T : BaseEntity

{

    private PropertyExpression() { }

    private static List propertyList = new List();

    public static PropertyExpression Init

    {

        get

        {

            propertyList.Clear();

            return new PropertyExpression();

        }

    }

 

    public PropertyExpression Property(Expression> expr)

    {

        var rtn = “”;

        if (expr.Body is UnaryExpression)

        {

            rtn = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;

        }

        else if (expr.Body is MemberExpression)

        {

            rtn = ((MemberExpression)expr.Body).Member.Name;

 

        }

        else if (expr.Body is ParameterExpression)

        {

            rtn = ((ParameterExpression)expr.Body).Type.Name;

        }

        propertyList.Add(rtn);

        return this;

    }

    public List ToList()

    {

        return propertyList;

    }

}

 

EF更新的處理

 

public bool Update(T entity, bool isSaveChange = true, List updatePropertyList = null)

{

    if (entity==null)

    {

        return false;

    }

    _dbContext.Set().Attach(entity);

    if (updatePropertyList==null)

    {

        _dbContext.Entry(entity).State = EntityState.Modified;//全欄位更新

 

    }

    else

    {

        updatePropertyList.ForEach(c => {

            _dbContext.Entry(entity).Property(c).IsModified = true; //部分欄位更新的寫法

 

        });

 

    }

    if (isSaveChange)

    {

        return SaveChanges() > 0;

    }

    return false;

}

 

使用

 

var entity = _unitOfWork.SysRoleRep.Get(model.RoleId);

if (entity == null)

{

    throw new Exception(“要查找的物件不存在”);

}

entity.Name = model.RoleName;

var updatedPropertyList = PropertyExpression.Init.Property(c => c.Name).ToList();

_unitOfWork.SysRoleRep.Update(entity, true, updatedPropertyList);

 

4、動態加載物體到DbContext

 

public class EntityTypeConfiguration : IEntityTypeConfiguration where T : class

{

    public void Configure(EntityTypeBuilder builder)

    {

        RelyConfigure(builder);

    }

    public virtual void RelyConfigure(EntityTypeBuilder builder)

    {

    }

}

  

public class Sys_Error_LogConfiguration : EntityTypeConfiguration

{

    public override void RelyConfigure(EntityTypeBuilder builder)

    {

        builder.ToTable(“sys_error_log”);

        builder.HasKey(x => x.Id);

        base.RelyConfigure(builder);

    }

}

  

public class EfDbContext : DbContext

{

    public EfDbContext(DbContextOptions options) : base(options)

    {

    }

    //配置資料庫連接

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

    {

       // optionsBuilder.UseSqlServer(“xxxx connection string”);

        base.OnConfiguring(optionsBuilder);

    }

    //第一次使用EF功能時執行一次,以後不再執行

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        //獲取當前程式集中有基類並且基類是泛型的類

        var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(c => c.BaseType != null && c.BaseType.IsGenericType).ToList();

        foreach (var type in typesToRegister)

        {

            //泛型定義相同

            if (type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>))

            {

                dynamic configurationInstance = Activator.CreateInstance(type);

                modelBuilder.ApplyConfiguration(configurationInstance);

            }

        }

       base.OnModelCreating(modelBuilder);

    }

}

 

5、工作單元

 

工作單元是對倉儲和事務的封裝

 

原理參考:https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

 

public class EfUnitOfWork : IUnitOfWork

{

     private EfDbContext _dbContext;//每次請求背景關係只會創建一個

     public EfUnitOfWork(EfDbContext context)

     {

         this._dbContext = context;

     }

     public int SaveChanges()

     {

 

         return _dbContext.SaveChanges();

     }

     public async Task SaveChangesAsync()

     {

         return await _dbContext.SaveChangesAsync();

     }

     private bool disposed = false;

 

     protected virtual void Dispose(bool disposing)

     {

         if (!this.disposed)

         {

             if (disposing)

             {

                 _dbContext.Dispose();//隨著工作單元的銷毀而銷毀

             }

         }

         this.disposed = true;

     }

 

     public void Dispose()

     {

         Dispose(true);

         GC.SuppressFinalize(this);

     }

     public IDbContextTransaction BeginTransaction()

     {

         var scope = _dbContext.Database.BeginTransaction();

         return scope;

     }

 

     public List SqlQuery(string sql, object param = null) where T : class

     {

         var con= _dbContext.Database.GetDbConnection();

         if (con.State!= ConnectionState.Open)

         {

             con.Open();

         }

         var list= MysqlDapperReader.SqlQuery(con, sql, param);

         return list;

         //throw new NotImplementedException();

     }

 

     public Task> SqlQueryAsync(string sql, object param = null) where T : class

     {

         throw new NotImplementedException();

     }

     #region Sys Repository

     private IRepository _sysUserRep;

     public IRepository SysUserRep

     {

         get

         {

             if (_sysUserRep == null)

             {

                 //var s= HttpContext.Current.Items[“currentUser”];

                 //var s = HttpContext.Current.RequestServices.GetService>();

                 //HttpContext.RequestServices.GetService>();

                 _sysUserRep = new Repository(_dbContext);

             }

             return _sysUserRep;

         }

     }

     private IRepository _sysRoleRep;

     public IRepository SysRoleRep

     {

         get

         {

             if (_sysRoleRep == null)

             {

                 _sysRoleRep = new Repository(_dbContext);

             }

             return _sysRoleRep;

         }

     }

     private IRepository _sysRoleUserRep;

     public IRepository SysRoleUserRep

     {

         get

         {

             if (_sysRoleUserRep == null)

             {

                 _sysRoleUserRep = new Repository(_dbContext);

             }

             return _sysRoleUserRep;

         }

     }

 

     private IRepository _sysPermissionRep;

     public IRepository SysPermissionRep

     {

         get

         {

             if (_sysPermissionRep == null)

             {

                 _sysPermissionRep = new Repository(_dbContext);

             }

             return _sysPermissionRep;

         }

     }

     private IRepository _sysModuleRep;

     public IRepository SysModuleRep

     {

         get

         {

             if (_sysModuleRep == null)

             {

                 _sysModuleRep = new Repository(_dbContext);

             }

             return _sysModuleRep;

         }

     }

 

     private IRepository _sysErrorLogRep;

     public IRepository SysErrorLogRep

     {

         get

         {

             if (_sysErrorLogRep == null)

             {

                 _sysErrorLogRep = new Repository(_dbContext);

             }

             return _sysErrorLogRep;

         }

     }

 

     private IRepository _sysOperationLogRep;

     public IRepository SysOperationLogRep

     {

         get

         {

             if (_sysOperationLogRep == null)

             {

                 _sysOperationLogRep = new Repository(_dbContext);

             }

             return _sysOperationLogRep;

         }

     }

     #endregion

}

 

6、業務的實現方式

 

以前我是service中直接創建倉儲然後用倉儲運算元據庫,方式如下:

 

 

這種方式比較繁瑣,後來我將創建倉儲統一放在工作單元中進行,在service中直接創建UnitOfWork,方式如下:

 

 

7、Service的動態註冊

 

public static class AutoIocRegister

{

    ///

    /// 動態註入IOC,註意類和接口的命名規則,接口在類名前面加”I”

    ///

    ///

    ///程式集名稱

    public static void BatchAddScoped(this IServiceCollection services, string assemblyName)

    {

        var libs = DependencyContext.Default.CompileLibraries;

        var serviceLib = libs.Where(c => c.Name.Contains(assemblyName)).FirstOrDefault();

        var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(serviceLib.Name));

        var serviceClassList = assembly.GetTypes().Where(c => c.IsClass).ToList();

        foreach (var item in serviceClassList)

        {

            var interfaceName = “I” + item.Name;

            var interfaceType = assembly.GetTypes().Where(c => c.IsInterface && c.Name == interfaceName).FirstOrDefault();

            if (interfaceType == null) continue;

            services.AddScoped(interfaceType, item);

        }

    }

} 

 

呼叫:

 

services.BatchAddScoped(“Cloud.Service”);

 

8、日誌記錄

 

日誌分操作日誌和錯誤日誌,可以設置在資料庫和文本中同時記錄:

 

通過全域性過濾器GlobalExceptionFilter和GlobalAuthorizeFilter處理

 

 

9、前端的封裝(分頁、彈出層、ajax等)

 

先放幾張圖吧,詳細的以後再介紹

 

 

演示地址:http://cloud.eggtwo.com/main/index

 


●編號172,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

 

Web開發

更多推薦25個技術類公眾微信

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等

    赞(0)

    分享創造快樂