zoukankan      html  css  js  c++  java
  • MongoDB:利用官方驱动改装为EF代码风格的MongoDB.Repository框架 二

    本次改动的主要内容是实现MongoDB.Repository对MongoDBRef的支持。

    MongoDB对一对一,一对多,多对多关系的维护,官方推荐文档嵌入方式,反映到模型的设计如下:

        public class Student : Entity
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }
    
        public class Teacher : Entity
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }
    
        public class Grade : RefEntity
        {
            public string Name { get; set; }
        }    
        public class School : Entity
        {
            public string Name { get; set; }
            public List<MongoDBRef> Students { get; set; }
            public List<MongoDBRef> Teachers { get; set; }
            public MongoDBRef Master { get; set; }
        }

    该种设计在数据库中的存储格式如下图:

    其中的Students, Teachers, Master都以嵌入在School集合中,而在相应的Student,Teacher集合中,并没有相关的数据。

    如果是多学校,我们要查询系统中所有的学生,或老师,那将是一件非常费力的工作。而我们最终目的应该是在School中存储键值,应该使用MongoDBRef实现,如下:

        public class School : Entity
        {
            public string Name { get; set; }
            public List<MongoDBRef> Students { get; set; }
            public List<MongoDBRef> Teachers { get; set; }
            public MongoDBRef Master { get; set; }
        }

    而这种设计,MongoDBRef的数据结构则过于简单,只有Id, CollectionName,DatabaseName三个字段,如果要得到Master,只能去Teacher集合中再次执行查询操作。

    而本次提交,解决的正是这个问题,使代码可以写成如school.Pick<Student>(student.Id).Name的形式。

    主要接口:IRefEntity,IDBRefContainer。

    IRefEntity:为减少数据冗余,并提供一个Update方法。Update方法的逻辑是先保存当前实体,即执行IEntity.Save(),然后对DBRefs中的数据进行保存,接口定义如下:

        public interface IRefEntity : IEntity
        {
            /// <summary>
            /// list of MongoDBRef
            /// </summary>
            List<MongoDBRef> DBRefs { get; set; }
    
            /// <summary>
            /// save IEntity first, then save list of MongoDBRef
            /// </summary>
            void Update();
        }

    IDBRefContainer接口,为IRefEntity.DBRefs的数据提供一个对应的实体容器,已完成相应的查询、添加、删除操作,接口定义如下:

        public interface IDBRefContainer
        {
            bool Exists(string id);
            bool Exists<T>() where T : IEntity;
            bool Exists<T>(Predicate<T> match) where T : IEntity;
    
            T Pick<T>(string id) where T : IEntity;
            T Pick<T>(Expression<Func<T, bool>> where) where T : IEntity;
    
            List<T> GetAll<T>() where T : IEntity;
            List<T> GetMany<T>(Expression<Func<T, bool>> where) where T : IEntity;
    
            void Add<T>(T entity) where T : IEntity;
            void Add<T>(List<T> entities) where T : IEntity;
    
            int Remove<T>(Expression<Func<T, bool>> where) where T : IEntity;
            void Remove<T>(T entity) where T : IEntity;
            void Remove<T>() where T : IEntity;
    
            int Count<T>() where T : IEntity;
            int Count<T>(Expression<Func<T, bool>> where) where T : IEntity;
    
            List<IEntity> GetAll();
        }

    需要注意的是,尽量使用IEntity.Save()进行保存操作,而减少使用IDBRefEntity.Update()进行更新保存 操作,因为IDBRefEntity.Update()针对的是所有IDBRefContainer中的数据,其执行效率还有待改善和提高。本人也一直在纠结,这个IDBRefEntity.Update()是否需要或者是否应该提供。

    下面给出一个具体的测试用例:

            [TestCase]
            public void test()
            {
                grade = new Grade();
                grade.Name = "No1";
                foreach (Student student in students)
                    grade.Add<Student>(student);
                foreach (Teacher teacher in teachers)
                    grade.Add<Teacher>(teacher);
                grade.Update();
    
                students[2].Name = "NameChanged";
                students[2].Save();
    
                var g = MongoEntity.Get<Grade>(grade.Id);
    
                Assert.AreSame(students[2].Name, grade.Pick<Student>(students[2].Id).Name);
                Assert.AreNotSame(grade.Pick<Student>(students[2].Id).Name, g.Pick<Student>(students[2].Id).Name);
                Assert.AreEqual(grade.Count<Student>(), g.Count<Student>());
                Assert.AreEqual(grade.Count<Teacher>(), g.Count<Teacher>());
    }

    具体请参考源码

    本人深感能力不足,欢迎大家指正、指教。

  • 相关阅读:
    jstl标签的fmt:formatDate格式化日期 String to Date
    Spring MVC使用ModelAndView进行重定向
    配置SpringAop时需要用到的AspectJ表达式
    深入分析Java Web中的编码问题
    第六十五条:不要忽略异常
    第六十四条:努力使失败保持原子性
    第六十三条:在细节消息中包含能捕获失败的信息
    第六十二条:每个方法抛出的异常都要有文档
    第六十一条:抛出与抽象相对应的异常
    第六十条:优先使用标准的异常
  • 原文地址:https://www.cnblogs.com/winhu/p/MongoDB.html
Copyright © 2011-2022 走看看