zoukankan      html  css  js  c++  java
  • 基于Mongodb的轻量级领域驱动框架(序)

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

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

    public class Person
            {
                public Person(string name)
                {
                    Name = name;
                }
    
                public ObjectId Id { get; private set; }
    
                public string Name { get; private set; }
    
                public Person ChangeName(string name)
                {
                    Name = name;
                    return this;
                }
            }
    var person = new Person("丁丁");
                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改造一下,像这样:

    public class Person
            {
                public Person(string name)
                {
                    persons = CollectionFactory<Person>.GetCollection();
                    Name = name;
                    persons.Insert(this);
                }
    
                MongoCollection<Person> persons;
    
                public ObjectId Id { get; private set; }
    
                public string Name { get; private set; }
    
                public Person ChangeName(string name)
                {
                    Name = name;
                    persons.Save(this);
                    return this;
                }
            }

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

    var person = new Person("丁丁");
    person.ChangeName("丁丁2");

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

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

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

    public class Person
            {
                public Person(string name)
                {
                    Name = name;
                }
    
                public virtual ObjectId Id { get; private set; }
    
                public virtual string Name { get; private set; }
    
                public virtual Person ChangeName(string name)
                {
                    Name = name;
                    return this;
                }
            }

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

    class DataInterceptor<T_AggregateRoot> : StandardInterceptor where T_AggregateRoot : class
        {
    
            public DataInterceptor(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_AggregateRoot aggregateRoot1;
            T_AggregateRoot aggregateRoot2;
            Type aggregateRootType;
    
            protected override void PreProceed(IInvocation invocation)
            {
                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 (Exception exception)
                    {
                        Logger.Exception(exception);
                    }
                }
            }
    
            protected override void PostProceed(IInvocation invocation)
            {
                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>
            /// <param name="aggregateRoot1"></param>
            /// <param name="aggregateRoot2"></param>
            /// <returns></returns>
            internal void Update(T_AggregateRoot aggregateRoot1, T_AggregateRoot aggregateRoot2)
            {
                if (aggregateRoot1 == null)
                    return;
    
                CompareObjects compareObjs = new CompareObjects();
                compareObjs.MaxDifferences = int.MaxValue;
                //比较私有属性
                compareObjs.ComparePrivateProperties = true;
                compareObjs.Compare(aggregateRoot1, aggregateRoot2);
                var id = BsonValue.Create(((dynamic)aggregateRoot2).Id);
                IMongoQuery query = Query.EQ("_id", id);
                IMongoUpdate updates;
                List<IMongoUpdate> allChanges = new List<IMongoUpdate>();
                List<IMongoUpdate> allChangesForDelete = new List<IMongoUpdate>();
                //分别对null值,集合元素的增删改,进行不同的处理
                foreach (Difference dif in compareObjs.Differences)
                {
                    string fieldName = dif.PropertyName.Substring(1);
                    fieldName = fieldName.Replace("[", ".").Replace("]", "");
                    BsonValue fieldValue = null;
    
                    //处理数组删除的情况
                    if (dif.IsDelete)
                    {
                        IMongoUpdate update2 = MongoDB.Driver.Builders.Update.PopLast(fieldName);
                        allChangesForDelete.Add(update2);
                        continue;
                    }
    
                    //处理null值
                    if (dif.Object2.Target == null && dif.Object2Value == null)
                    {
                        try
                        {
                            dynamic nullValueLogContent = new ExpandoObject();
                            nullValueLogContent.AggregateRoot1 = aggregateRoot1;
                            nullValueLogContent.AggregateRoot2 = aggregateRoot2;
                            nullValueLogContent.Differences = compareObjs.Differences;
                        }
                        catch { }
                        fieldValue = BsonNull.Value;
                        IMongoUpdate update2 = 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);
                        }
                        else if (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());
                        }
                    }
    
                    IMongoUpdate update = 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 (IMongoUpdate up in allChangesForDelete)
                {
                    collection.Update(query, up);
                }
            }

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

    /// <summary>
            /// 创建代理
            /// </summary>
            /// <param name="aggregateRoot"></param>
            /// <returns></returns>
            T_AggregateRoot CreateProxy(T_AggregateRoot aggregateRoot)
            {
                var aggregateRootType = aggregateRoot.GetType();
                var constructor = aggregateRootType.GetConstructors().OrderBy(c => c.GetParameters().Length).First();
                var parameters = constructor.GetParameters().Select(p => default(object)).ToArray();
                return (T_AggregateRoot)proxyGenerator.CreateClassProxyWithTarget(aggregateRootType, aggregateRoot, parameters, new DataInterceptor<T_AggregateRoot>(this.Update, this.Remove));
            }

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

    /// <summary>
        /// 聚合根泛型集合类
        /// </summary>
        public class AggregateRootCollection<T_AggregateRoot> where T_AggregateRoot : class
        {
            ...
        }

    然后用法类似这样:

    var persons = new AggregateRootCollection<Person>("TestDb");
                var personProxy = persons.Add(new Person("丁丁"));
                personProxy.ChangeName("丁丁2");

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

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

  • 相关阅读:
    [BZOJ4876][ZJOI2017]线段树
    [FJOI2016]建筑师(斯特林数)
    WC2018伪题解
    [BZOJ3514]CodeChef MARCH14 GERALD07加强版(LCT+主席树)
    [BZOJ2594][WC2006]水管局长加强版(LCT+Kruskal)
    [洛谷3796]【模板】AC自动机(加强版)
    [洛谷3808]【模板】AC自动机(简单版)
    [BZOJ3261]最大异或和
    [BZOJ3439]Kpm的MC密码
    [POI2006]Periods of Words
  • 原文地址:https://www.cnblogs.com/royding/p/3711751.html
Copyright © 2011-2022 走看看