基于Mongodb的轻量级领域驱动框架(序)
本文内容纲要:
混园子也有些年头了,从各个大牛那儿学了很多东西。技术这东西和中国的料理一样,其中技巧和经验,代代相传(这不是舌尖上的中国广告)。转身回头一望,几年来自己也积累了一些东西,五花八门涉猎到各种方向,今日开始选一些有价值的开博分享。
首篇分享的是一个基于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