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>());
}

具体请参考源码

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

posted on 2013-10-18 09:45  winhu  阅读(1897)  评论(5编辑  收藏  举报

导航