首页 文章资讯内容详情

基于Mongodb的轻量级领域驱动框架(序)

2026-05-31 4 花语

本文内容纲要:

混园子也有些年头了,从各个大牛那儿学了很多东西。技术这东西和中国的料理一样,其中技巧和经验,代代相传(这不是舌尖上的中国广告)。转身回头一望,几年来自己也积累了一些东西,五花八门涉猎到各种方向,今日开始选一些有价值的开博分享。

首篇分享的是一个基于Mongodb的轻量级领域驱动框架,创作的起源比较杂,首先来自Mongodb,能够直接存储对象。例如:

publicclassPerson { publicPerson(stringname) { Name=name; } publicObjectIdId{get;privateset;} publicstringName{get;privateset;} publicPersonChangeName(stringname) { Name=name; returnthis; } } varperson=newPerson("丁丁"); MongoCollection<Person>persons=database.GetCollection<Person>(typeof(Person).Name); persons.Insert(person); person.ChangeName("丁丁2"); persons.Save(person);

如上所示,有一个Person的类,创建一个Person实例,插入到mongo里,然后执行Person的方法,将改变了属性的Person实例保存到mongo里,这是最简单的Mongo用法。

那么,有没有可能通过某种方式,让对象的实例自身就具有持久化的能力呢?比如像传统仓储的做法那样,在一个聚合根里注入仓储。比如,把Person改造一下,像这样:

publicclassPerson { publicPerson(stringname) { persons=CollectionFactory<Person>.GetCollection(); Name=name; persons.Insert(this); } MongoCollection<Person>persons; publicObjectIdId{get;privateset;} publicstringName{get;privateset;} publicPersonChangeName(stringname) { Name=name; persons.Save(this); returnthis; } }

Person中内置了Mongo集合通过工厂注入的实例,于是Person就可以这么用了:

varperson=newPerson("丁丁"); person.ChangeName("丁丁2");

好,到这儿,一切都很顺利。不过Person是个信息量很少很简单的对象。如果Person是一个结构非常复杂的对象,每次使用persons.Save(this),是将整个对象更新,非常占用网络流量,这样使用场景就很有限了。有没有什么改进的办法,比如Save(this)变成将有改动的属性更新掉?

Mongo的原生驱动没有提供局部更新的功能,想要实现只有自己写。那么能否监视一个对象的状态改变呢?AOP动态织入好像可以做到。CastleDynamicProxy是很牛逼的东西,可以用它来试试。

首先,改造一下Person,将属性和方法都变成虚的,让它能被Castle所用:

publicclassPerson { publicPerson(stringname) { Name=name; } publicvirtualObjectIdId{get;privateset;} publicvirtualstringName{get;privateset;} publicvirtualPersonChangeName(stringname) { Name=name; returnthis; } }

然后写一个泛型拦截器,在方法执行前对真实对象进行深拷贝,然后在方法执行后将执行前后的对象传入更新委托:

classDataInterceptor<T_AggregateRoot>:StandardInterceptorwhereT_AggregateRoot:class { publicDataInterceptor(Action<T_AggregateRoot,T_AggregateRoot>updateAction,Action<T_AggregateRoot>deleteAction) { this.updateAction=updateAction; this.deleteAction=deleteAction; aggregateRootType=typeof(T_AggregateRoot); } Action<T_AggregateRoot,T_AggregateRoot>updateAction; Action<T_AggregateRoot>deleteAction; T_AggregateRootaggregateRoot1; T_AggregateRootaggregateRoot2; TypeaggregateRootType; protectedoverridevoidPreProceed(IInvocationinvocation) { if(!invocation.Method.Name.StartsWith("get_")&&!invocation.Method.Name.StartsWith("set_")&&!invocation.Method.Name.Equals("Abadon")) { try { aggregateRoot1=NClone.Clone.ObjectGraph((((T_AggregateRoot)invocation.InvocationTarget))); } catch(Exceptionexception) { Logger.Exception(exception); } } } protectedoverridevoidPostProceed(IInvocationinvocation) { if(!invocation.Method.Name.StartsWith("get_")&&!invocation.Method.Name.StartsWith("set_")) { aggregateRoot2=(T_AggregateRoot)invocation.InvocationTarget; if(invocation.Method.Name.Equals("Abadon")) { deleteAction.Invoke(aggregateRoot2); } else { updateAction.Invoke(aggregateRoot1,aggregateRoot2); } } } }

通过对象深比较得到差异,编译成Mongo更新语句执行更新:

///<summary> ///局部更新 ///</summary> ///<remarks> ///比较对象,找到不一致的地方,进行 ///</remarks> ///<paramname="aggregateRoot1"></param> ///<paramname="aggregateRoot2"></param> ///<returns></returns> internalvoidUpdate(T_AggregateRootaggregateRoot1,T_AggregateRootaggregateRoot2) { if(aggregateRoot1==null) return; CompareObjectscompareObjs=newCompareObjects(); compareObjs.MaxDifferences=int.MaxValue; //比较私有属性 compareObjs.ComparePrivateProperties=true; compareObjs.Compare(aggregateRoot1,aggregateRoot2); varid=BsonValue.Create(((dynamic)aggregateRoot2).Id); IMongoQueryquery=Query.EQ("_id",id); IMongoUpdateupdates; List<IMongoUpdate>allChanges=newList<IMongoUpdate>(); List<IMongoUpdate>allChangesForDelete=newList<IMongoUpdate>(); //分别对null值,集合元素的增删改,进行不同的处理 foreach(DifferencedifincompareObjs.Differences) { stringfieldName=dif.PropertyName.Substring(1); fieldName=fieldName.Replace("[",".").Replace("]",""); BsonValuefieldValue=null; //处理数组删除的情况 if(dif.IsDelete) { IMongoUpdateupdate2=MongoDB.Driver.Builders.Update.PopLast(fieldName); allChangesForDelete.Add(update2); continue; } //处理null值 if(dif.Object2.Target==null&&dif.Object2Value==null) { try { dynamicnullValueLogContent=newExpandoObject(); nullValueLogContent.AggregateRoot1=aggregateRoot1; nullValueLogContent.AggregateRoot2=aggregateRoot2; nullValueLogContent.Differences=compareObjs.Differences; } catch{} fieldValue=BsonNull.Value; IMongoUpdateupdate2=MongoDB.Driver.Builders.Update.Set(fieldName,fieldValue); allChanges.Add(update2); continue; } //原始类型或字符串直接使用对象 //对象类型则转为.ToBsonDocument(); if(dif.Object2.Target.GetType().IsPrimitive||dif.Object2.Target.GetType().Equals(typeof(string))|| dif.Object2.Target.GetType().IsEnum) { fieldValue=dif.Object2.Target==null?BsonValue.Create(dif.OriginObject2):BsonValue.Create(dif.Object2.Target); } else { //更新整个集合类 if(dif.Object2.Target.GetType().GetInterface(typeof(IDictionary).FullName)!=null ||dif.Object2.Target.GetType().GetInterface(typeof(IList).FullName)!=null) { fieldValue=BsonValue.Create(dif.OriginObject2); } elseif(dif.Object2.Target.GetType()==typeof(DateTime)) { fieldValue=dif.Object2.Target==null?BsonDateTime.Create(dif.OriginObject2):BsonDateTime.Create(dif.Object2.Target); } else { //处理普通的class类型 //由于这里OriginObject2一定不会被释放(强引用),所以使用dif.Object2.Target或者dif.OriginObject2都可以 fieldValue=BsonValue.Create(dif.Object2.Target.ToBsonDocument()); } } IMongoUpdateupdate=MongoDB.Driver.Builders.Update.Set(fieldName,fieldValue); allChanges.Add(update); } //有更新才处理 if(allChanges.Count>0) { updates=MongoDB.Driver.Builders.Update.Combine(allChanges); collection.Update(query,updates); } foreach(IMongoUpdateupinallChangesForDelete) { collection.Update(query,up); } }

写一个类似Collection的泛型类,提供集合类操作,在操作末尾对对象的实例动态织入:

///<summary> ///创建代理 ///</summary> ///<paramname="aggregateRoot"></param> ///<returns></returns> T_AggregateRootCreateProxy(T_AggregateRootaggregateRoot) { varaggregateRootType=aggregateRoot.GetType(); varconstructor=aggregateRootType.GetConstructors().OrderBy(c=>c.GetParameters().Length).First(); varparameters=constructor.GetParameters().Select(p=>default(object)).ToArray(); return(T_AggregateRoot)proxyGenerator.CreateClassProxyWithTarget(aggregateRootType,aggregateRoot,parameters,newDataInterceptor<T_AggregateRoot>(this.Update,this.Remove)); }

最终,这一系列思路的产物就是一个聚合跟集合:

///<summary> ///聚合根泛型集合类 ///</summary> publicclassAggregateRootCollection<T_AggregateRoot>whereT_AggregateRoot:class { ... }

然后用法类似这样:

varpersons=newAggregateRootCollection<Person>("TestDb"); varpersonProxy=persons.Add(newPerson("丁丁")); personProxy.ChangeName("丁丁2");

第一行实例化聚合跟集合,第二行用Add方法对新的实例进行动态织入返回代理,第三行就是神奇的执行方法后,状态的变化就立刻持久化了。

以上是这个轻量级领域驱动框架的大致介绍,目前还未发布到Github和nuget上,后续会一篇篇的更新它的实现原理。它适用于一些事务性不强的工程,让开发人员所有关注点就在业务逻辑上,告别持久化。

本文内容总结:

原文链接:https://www.cnblogs.com/royding/p/3711751.html