本來是想試著做一個簡單OA項目玩玩的,真是不做不知道,一做嚇死人,原來以為很簡單的事情,但是做起來不是忘這就是忘那的,有的技術還得重新溫習。所以還是得記錄。免得哪天電腦掛了,就全沒有了。 開始是看了園子里何鎮汐的一系列文章,寫的太好了,只看了幾篇就有想寫代碼的衝動,很大一部分都是搬他的東西。但是我還 ...
本來是想試著做一個簡單OA項目玩玩的,真是不做不知道,一做嚇死人,原來以為很簡單的事情,但是做起來不是忘這就是忘那的,有的技術還得重新溫習。所以還是得記錄。免得哪天電腦掛了,就全沒有了。
開始是看了園子里何鎮汐的一系列文章,寫的太好了,只看了幾篇就有想寫代碼的衝動,很大一部分都是搬他的東西。但是我還是領誤不了DDD,所以先就著三層搞搞。
我搞了兩個解決方案,一個本著是想做框架,把有通用的封裝了,以後要用就引dll,由於太枯燥,另一個就是想做個玩具項目,兩邊輪流搞搞
先是dll部分的,當然很多都沒實現反正是自己的,以後再慢慢補,我這裡東西很少,所以沒用文件夾之類的,全部直接一個命名空間
先是autofac封裝類,只是簡單封裝了一下,做玩具夠用,跟上一篇博客一樣的,這裡不說了
using Autofac; using System; namespace MTF.Domain { public class DependencyContainer { public static IContainer Container { get { return container; } } public static IContainer container; private DependencyContainer() { } public static void Initializer(Action<ContainerBuilder> action) { ContainerBuilder builder = new ContainerBuilder(); if (action != null) { action(builder); container = builder.Build(); } } private static void Error() { if (container == null) throw new InvalidOperationException("容器沒有初始化"); else return; } public static TService Resolve<TService>() { return container.Resolve<TService>(); } } }Dependency類代碼
BaseEntity 的設計是參考何兄的,大家可以去看他的文章,寫的太好了,這裡幾乎是搬的
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace MTF.Domain { public abstract class BaseEntity<TKey> : IEntity<TKey> { #region 初始化 public TKey GId { get; private set; } protected BaseEntity(TKey id) { this.GId = id; validationRules = new List<IValidationRule>(); } #endregion #region 實體相等性比較 public override bool Equals(object obj) { if (obj == null) return false; if (!(obj is BaseEntity<TKey>)) return false; return this == (BaseEntity<TKey>)obj; } public override int GetHashCode() { return this.GId.GetHashCode(); } public static bool operator ==(BaseEntity<TKey> entity1, BaseEntity<TKey> entity2) { if ((object)entity1 == null || (object)entity2 == null) return false; if (entity1.GId == null) return false; if (entity1.GId.Equals(default(TKey))) return false; return entity1.GId.Equals(entity2.GId); } public static bool operator !=(BaseEntity<TKey> entity1, BaseEntity<TKey> entity2) { return !(entity1 == entity2); } #endregion #region 驗證、驗證規則添加及驗證處理 private List<IValidationRule> validationRules;//驗證結果集合 private ICollection<ValidationResult> validationResults;//驗證規則集合 private IValidationHandler validationHandler;//驗證處理器 private void DefaultValidtae() { validationResults = new List<ValidationResult>(); ValidationContext context = new ValidationContext(this, null, null); Validator.TryValidateObject(this, context, validationResults, true);//預設驗證實體特性註解 if (validationRules.Count > 0)//如果有其它驗證規則 { validationRules.ForEach(o => { if (o.Validate() == ValidationResult.Success || o.Validate() == null) return; //其它規則不通過時 validationResults.Add(o.Validate());//添加到驗證結果集合 }); } } /// <summary> /// 驗證標識 /// </summary> public virtual bool IsValid { get { DefaultValidtae(); return validationResults.Count <= 0; } } /// <summary> /// 添加驗證規則 /// </summary> /// <param name="rule"></param> public void AddValidationRule(IValidationRule rule) { if (rule == null) return; validationRules.Add(rule); } /// <summary> /// 預設驗證當前實體方法 /// </summary> public virtual void Validate() { if (IsValid) return; //驗證不通過時 try { validationHandler = DependencyContainer.Resolve<IValidationHandler>();//設置驗證處理器 } catch { throw new InvalidOperationException("當前實體沒有通過驗證,但未設置驗證處理器,請檢查容器是否註入"); } validationHandler.Handle(validationResults);//處理驗證結果 } #endregion } }View Code
Repository數據操作介面,這裡也是搬的,但有一點不一樣,考慮到排序我不想用Guid類型的,所以沒有約束Entity必須實現IBaseEntity介面或者聚合根,因為我用的三層,沒用聚合根,再者我在後面的Service層想用到分頁排序,看到後面應該就能理解的。但是我並不知道這樣對不對
IUnitOfWork只搞了一個方法,以後再搞
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace MTF.Domain { public interface IRepository<TEntity,TKey> where TEntity:class { /// <summary> /// 添加單個實體 /// </summary> /// <param name="entity"></param> void Add(TEntity entity); /// <summary> /// 添加實體集合 /// </summary> /// <param name="entities"></param> void Add(IEnumerable<TEntity> entities); /// <summary> /// 修改 /// </summary> /// <param name="entity"></param> void Update(TEntity entity); /// <summary> /// 根據Id刪除實體 /// </summary> /// <param name="id"></param> void Remove(TKey id); /// <summary> /// 刪除實體 /// </summary> /// <param name="entity"></param> void Remove(TEntity entity); /// <summary> /// 查找實體列表 /// </summary> /// <returns></returns> List<TEntity> FindAll(); /// <summary> /// 查找實體列表 /// </summary> /// <returns></returns> IQueryable<TEntity> Find(); /// <summary> /// 查找實體列表 /// </summary> /// <param name="ids"></param> /// <returns></returns> //List<TEntity> Find(IEnumerable<TKey> ids); /// <summary> /// 根據Id查找實體 /// </summary> /// <param name="id"></param> /// <returns></returns> TEntity Find(params object[] id); /// <summary> /// 判斷實體是否存在 /// </summary> /// <param name="predicate"></param> /// <returns></returns> bool Exists(Expression<Func<TEntity, bool>> predicate); /// <summary> /// 索引器查找 /// </summary> /// <param name="id"></param> /// <returns></returns> TEntity this[TKey id] { get; } /// <summary> /// 保存 /// </summary> void Save(); /// <summary> /// 獲取工作單元 /// </summary> /// <returns></returns> IUnitOfWork GetUnitOfWork(); IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> where); } }View Code
using System; namespace MTF.Domain { public interface IUnitOfWork:IDisposable { void Commit(); } }View Code
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace MTF.Domain { public interface IValidationHandler { void Handle(ICollection<ValidationResult> validationResults); } }View Code
using System.ComponentModel.DataAnnotations; namespace MTF.Domain { public interface IValidationRule { ValidationResult Validate(); } }View Code
Domain現在只完成了這麼多,其它的介面還沒搞,還是以後再搞,懶的。
後面是EF部分了,引用Domain層
簡單的工作單元
using MTF.Domain; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Entity.Validation; namespace MTF.EFDatas { public class EFUnitOfWork :DbContext, IUnitOfWork { protected EFUnitOfWork(string connection) : base(connection) { } public void Commit() { try { SaveChanges(); } catch(DbUpdateConcurrencyException) { throw; } catch(DbEntityValidationException) { throw; } } } }EFUnitOfWork
簡單的EF數據操作,還是以後慢慢再搞
using MTF.Domain; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace MTF.EFDatas { public class EFRepository<TEntity,TKey>: IRepository<TEntity,TKey> where TEntity:class { protected EFUnitOfWork unitOfWork { get; private set; } public EFRepository() { this.unitOfWork = DependencyContainer.Resolve<EFUnitOfWork>(); } public void Add(TEntity entity) { unitOfWork.Set<TEntity>().Add(entity); } public void Add(IEnumerable<TEntity> entities) { unitOfWork.Set<TEntity>().AddRange(entities); } public void Update(TEntity entity) { unitOfWork.Entry(entity).State = System.Data.Entity.EntityState.Modified; } public void Remove(TKey id) { unitOfWork.Set<TEntity>().Remove(Find(id)); } public void Remove(TEntity entity) { unitOfWork.Set<TEntity>().Remove(entity); } public List<TEntity> FindAll() { return Find().ToList(); } public IQueryable<TEntity> Find() { return unitOfWork.Set<TEntity>(); } //public List<TEntity> Find(IEnumerable<TKey> ids) //{ // if (ids == null) // return null; // return Find().Where(o => ids.Contains(o.GId)).ToList(); //} public TEntity Find(params object[] id) { return unitOfWork.Set<TEntity>().Find(id); } public IQueryable<TEntity> Find(Expression<Func<TEntity,bool>> where) { return Find().Where(where); } public bool Exists(Expression<Func<TEntity, bool>> predicate) { return unitOfWork.Set<TEntity>().Any(predicate); } public TEntity this[TKey id] { get { return Find(id); } } public void Save() { unitOfWork.Commit(); } public IUnitOfWork GetUnitOfWork() { return unitOfWork; } } }EFRepository
這裡我自己寫了一個分頁類EF專用的,因為不想一定要用ID分類,所以加了個泛型類型,專門用於排序用的,本來想著這個類是專門給service用的,不應該用public的,但是想著以後別的地方可能會用到,所以還是公共了
using System; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; namespace MTF.EFDatas { /// <summary> /// 分頁 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <typeparam name="TOrder">要排序的類型</typeparam> public class Pager<TEntity,TOrder> where TEntity : class { public Pager(DbContext eow) { this.unitOfWork = eow; this.total = eow.Set<TEntity>().Count(); Initia(1, 20); } private DbContext unitOfWork; public int PageIndex { get { return pageIndex; } } private int pageIndex; public int PageCount { get { return pageCount; } } private int pageCount; public int Total { get { return total; } } private int total; public int PageSize { get { return pageSize; } } private int pageSize; public int Skip { get { return skip; } } private int skip; public int Take { get { return take; } } private int take; private void Initia(int index, int size) { if (index < 1) index = 1; if (size >= total) { pageIndex = 1; pageCount = 1; pageSize = total; skip = 0; take = pageSize; } if (size < total) { int n = total % size; int x = total / size; if (n == 0) { pageSize = size; pageCount = x; if (index > pageCount) { index = PageCount; pageIndex = index; } pageIndex = index; skip = (pageIndex - 1) * size; take = size; } else { pageCount = x + 1; if (index > pageCount) { pageIndex = pageCount; } else { pageIndex = index; } if (pageIndex == pageCount) { pageIndex = PageCount; pageSize = n; skip = (pageIndex - 1) * size; take = n; } else { pageSize = size; skip = (pageIndex - 1) * size; take = size; } } } } public void SetPageSize(int size) { pageSize = size; Initia(PageIndex, PageSize); } public IQueryable<TEntity> GetPageList(Expression<Func<TEntity, TOrder>> order) { return unitOfWork.Set<TEntity>().OrderBy(order).Skip(Skip).Take(Take); } public IQueryable<TEntity> GetPageList(int pageIndex, Expression<Func<TEntity, TOrder>> order) { this.pageIndex = pageIndex; Initia(PageIndex, PageSize); return GetPageList(order); } public IQueryable<TEntity> GetPageList(int pageIndex, int pageSize, Expression<Func<TEntity, TOrder>> order) { this.pageIndex = pageIndex; this.pageSize = pageSize; Initia(PageIndex, PageSize); return GetPageList(order); } } }自己寫的Pager
直接給代碼先,都是很簡單,不知道合不合理,這裡用到了Pager類,當作屬性使用,客戶端得到Service就可以使用它的,它們共用一個工作單元
using MTF.Domain; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; namespace MTF.EFDatas { public class BaseService<TEntity,TKey,TOrderPage> where TEntity:class { private IRepository<TEntity, TKey> Repository; private Pager<TEntity, TOrderPage> pager; public Pager<TEntity,TOrderPage> Pager { get { return pager; } } public BaseService() { Repository = DependencyContainer.Resolve<IRepository<TEntity, TKey>>(); pager = new Pager<TEntity, TOrderPage>(DependencyContainer.Resolve<EFUnitOfWork>()); } public void Add(TEntity entity) { Repository.Add(entity); } public void Add(IEnumerable<TEntity> entities) { Repository.Add(entities); } public void Update(TEntity entity) { Repository.Update(entity); } public void Remove(TKey id) { Repository.Remove(id); } public void Remove(TEntity entity) { Repository.Remove(entity); } public List<TEntity> FindAll() { return Repository.FindAll(); } public IQueryable<TEntity> Find() { return Repository.Find(); } //public List<TEntity> Find(IEnumerable<TKey> ids) //{ // return Repository.Find(ids); //} public TEntity Find(params object[] id) { return Repository.Find(id); } public IQueryable<TEntity> Find(Expression<Func<TEntity,bool>> where) { return Find().Where(where); } public bool Exists(Expression<Func<TEntity, bool>> predicate) { return Repository.Exists(predicate); } public TEntity this[TKey id] { get { return Find(id); } } public void Save() { Repository.Save(); } } }BaseService
由於沒有寫測試代碼,寫到這裡的時候 我都有點迫不及待想試試了,所以就先到此生成。另外寫一個MVC試試唄,搞個三層玩玩
DataModel層添加上面的DLL引用,我叫MTF指的是My Test Frame呵呵,不會英語,反正是搞的玩。
安裝EF和AUTOFAC包開搞,先搞個BaseEntity,由於我int 類型不會初始化的時候賦值,
但是又不想GUID來排序,所以我想的辦法是實體介面的時候,給的GId的屬性名,主鍵我還是用的int類型,Guid初始化的時候賦值,它同樣是唯一的,方便記憶體操作,int類型做自增的主鍵,在插入資料庫後才有值。
做了三個類,用戶,許可權和角色,還有三個中間類,以配多對多關係,本著學習的精神,我用的CodeFirst和F..Api(不會定這個單詞了,暈)方式配置映射
一樣的簡單玩玩的,可能還沒有註釋,直接上了
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using MTF.Domain; namespace OADemo.DataModel.Domains { public class EntityBase:BaseEntity<Guid> { protected EntityBase(Guid id) : base(id) { }//guid類型的屬性以後應該可以用到,它是在初始化的時候賦值的,記憶體中好找 public EntityBase() : this(Guid.NewGuid())//int類型映射主鍵,插入資料庫時才有值,方便資料庫不方便記憶體操作 { SubTime = DateTime.Now;//提交時間為當前時間 IsDeleted = false;//這個好像有點多餘,預設應該就是false,但不確定,所以加上了 } //子類共有的屬性 public int Id { get; set; } public bool IsDeleted { get; set; } public DateTime SubTime { get; set; } public string Remark { get; set; } } }EntityBase
using System.Collections.Generic; namespace OADemo.DataModel.Domains { public class UserEntity:EntityBase { public ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();//導航屬性 public ICollection<UserPermission> UserPermissions { get; set; } = new List<UserPermission>();//導航屬性 public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } public string RealName { get; set; } } public class RoleEntity:EntityBase { public ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>(); public ICollection<RolePermission> RolePermissions { get; set; } = new List<RolePermission>(); public string Name { get; set; } public string Description { get; set; } } public class