來自:夢亦曉
連結: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、資料庫、運維等