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

具體請參考源碼

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM