一步一步學EF系列【5、升級篇 實體與數據庫的映射】live writer真坑,第4次補發


前言

       之前的幾篇文章,被推薦到首頁后,又被博客園下了,原因內容太少,那我要寫多點呢,還是就按照這種頻率進行寫呢?本身我的意圖這個系列就是想已最簡單最容易理解的方式進行,每篇內容也不要太多,這樣初學者容易理解學習,否則天花亂墜的一大篇初學者從頭看到尾也要暈了。所以每次突出重點進行濃縮精華時的講,當然我這樣精簡講,你們要學深入的話,也還是要把有些概念學深入一下。也歡迎大家共同討論學習。我這里創建了一個QQ群(435498053)大家也可以加群交流。

正文

     本篇還是作為之前的升級篇,其實前面2-3篇可以合並,但我覺得直接合並不能讓人很容易理解,先來一篇最基礎的,然后在循序漸進的進行深入和代碼方面的封裝簡化。我們不廢話了,接着上篇講,上篇最后的時候大家還記得最終寫好的代碼嗎?我把代碼貼出來一塊回顧一下。

       //實體集合
        public IDbSet<BlogUser> BlogUsers { get; set; }
        public IDbSet<Post> Posts { get; set; }
     
        /// <summary>
        /// 重寫配置類
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // base.OnModelCreating(modelBuilder);
            modelBuilder.Configurations.Add(new BlogUserConfiguration());
            modelBuilder.Configurations.Add(new PostConfiguration());
        }

      上一篇我們提出這樣寫是為了簡化在OnModelCreating中把每個表的配置寫到這里,但是用心的朋友應該看到,你這樣寫其實也還是很麻煩啊!按照現在的寫法難道我每次建一個實體都要在這里創建一個實體集合,然后在下面在加入一個modelBuilder.Configurations.Add(new XXXConfiguration());所以問題還是一樣的存在。具體看下面

      由代碼可以看出,當前的上下文類與業務實體是強耦合的,分別耦合在DbSet<TEntity>的屬性與OnModelCreating方法上。那解決辦法呢?當然要解耦。對於屬性,可以使用DbContext.Set<TEntity>()方法來實現指定實體的屬性,對於OnModelCreating中的方法實現中的映射配置對象,則可提取一個通用接口,通過接口進行分別映射。那我們就分兩步來講解如何解耦!

一、提取一個IEntityMapper通用接口,通過接口進行分別映射。

          1、定義接口的代碼如下:

    /// <summary>
    ///     實體映射接口
    /// </summary>
    public interface IEntityMapper
    {
        ///<summary>
        ///     將當前實體映射對象注冊到當前數據訪問上下文實體映射配置注冊器中
        /// </summary>
        /// <param name="configurations">實體映射配置注冊器</param>
        void RegistTo(ConfigurationRegistrar configurations);
    }

        2、在實體映射類中添加IEntityMapper的實現。我們已BlogUser作為實例演示。代碼如下

/// <summary>
    /// 博客用戶信息映射類
    /// </summary>
    public class BlogUserConfiguration : EntityTypeConfiguration<BlogUser>,IEntityMapper
    {
        public BlogUserConfiguration()
        {
            //設置主鍵
            HasKey(m => m.BlogUserId);
        }

        public void RegistTo(System.Data.Entity.ModelConfiguration.Configuration.ConfigurationRegistrar configurations)
        {
            configurations.Add(this);
            //throw new NotImplementedException();
        }
    }

       3、這樣定義好了,那是不是要考慮的是怎么能在OnModelCreating自動調用實現IEntityMapper進行自動注冊呢!這里辦法有很多,有知道IOC的朋友應該會想到用依賴注入的方式就可以,引入所有實現了IEntityMapper的類的對象。但是我這里先不用IOC組件干這個事情,我們還是先按照最傳統的方式來進行。

          我們具體要怎么做的思路都有了吧?就是引入所有實現了IEntityMapper的類的對象,然后遍歷,調用其中的RegistTo進行實體映射類對象的添加。

         第一步,獲取所有實現了IEntityMapper的類的對象,看代碼

     private static ICollection<IEntityMapper> GetAllEntityMapper()
        {
            ICollection<Assembly> EntityMapperAssemblies = EntityMapperAssemblies = new[]
                        {
                            Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "EFCore.dll"))
                        };
            if (EntityMapperAssemblies.Count == 0)
            {
                throw new InvalidOperationException("上下文“{0}”初始化失敗,請添加實體映射程序集");
            }
            Type baseType = typeof(IEntityMapper);
            Type[] mapperTypes = EntityMapperAssemblies.SelectMany(assembly => assembly.GetTypes())
                .Where(type => baseType.IsAssignableFrom(type) && type != baseType && !type.IsAbstract).ToArray();
            ICollection<IEntityMapper> result = mapperTypes.Select(type => Activator.CreateInstance(type) as IEntityMapper).ToList();
            return result;
        }

     上面的這段我是參考過別人的代碼的,具體哪個博客我不記得了,到時候找到我在添加上引用。原理也比較簡單就是我加載我寫實體類的EFCore.dll的程序集,這個dll名稱要按你實際項目的dll名稱為主,這個名字現在是我自己的demo。然后把實現IEntityMapper的類找到,然后通過CreateInstance創建該類型的實例,原理就這個。

第二步,遍歷對象,調用其中的RegistTo進行實體映射類對象的添加

/// <summary>
        /// 重寫配置類
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // base.OnModelCreating(modelBuilder);
            //modelBuilder.Configurations.Add(new BlogUserConfiguration());
            //modelBuilder.Configurations.Add(new PostConfiguration());

            modelBuilder.Entity<BlogUser>().HasKey(m => m.BlogUserId);

            IEnumerable<IEntityMapper> EntityMappers = GetAllEntityMapper();
            if (EntityMappers == null)
            {
                return;
            }
            foreach (var mapper in EntityMappers)
            {
                mapper.RegistTo(modelBuilder.Configurations);
            }

        }

 

然后就完成了,運行你的代碼。是不是也成功了。

我把完整的代碼在附上

        /// <summary>
        /// 重寫配置類
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // base.OnModelCreating(modelBuilder);
            //modelBuilder.Configurations.Add(new BlogUserConfiguration());
            //modelBuilder.Configurations.Add(new PostConfiguration());

            modelBuilder.Entity<BlogUser>().HasKey(m => m.BlogUserId);

            IEnumerable<IEntityMapper> EntityMappers = GetAllEntityMapper();
            if (EntityMappers == null)
            {
                return;
            }
            foreach (var mapper in EntityMappers)
            {
                mapper.RegistTo(modelBuilder.Configurations);
            }

        }


        private static ICollection<IEntityMapper> GetAllEntityMapper()
        {
            ICollection<Assembly> EntityMapperAssemblies = EntityMapperAssemblies = new[]
                        {
                            Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "EFCore.dll"))
                        };
            if (EntityMapperAssemblies.Count == 0)
            {
                throw new InvalidOperationException("上下文“{0}”初始化失敗,請添加實體映射程序集");
            }
            Type baseType = typeof(IEntityMapper);
            Type[] mapperTypes = EntityMapperAssemblies.SelectMany(assembly => assembly.GetTypes())
                .Where(type => baseType.IsAssignableFrom(type) && type != baseType && !type.IsAbstract).ToArray();
            ICollection<IEntityMapper> result = mapperTypes.Select(type => Activator.CreateInstance(type) as IEntityMapper).ToList();
            return result;
        }

我這里作為演示也不要新建類進行封裝,寫的一起主要看起來方便而已。

二、對於屬性,可以使用DbContext.Set<TEntity>()方法來實現指定實體的屬性

         上面我們通過提前了一個IEntityMapper接口實現了OnModelCreating里面的代碼的解耦。那這里我們就要想辦法處理如下的代碼。 

public IDbSet<BlogUser> BlogUsers { get; set; }
public IDbSet<Post> Posts { get; set; }
……

        上面也提到了解決辦法就是通過DbContext.Set<TEntity>()方法來實現指定實體的屬性,具體怎么操作呢?其實這個要比處理配置類簡單多了,當然也還是直接看代碼。

  

public ActionResult Index()
 {
 
   var db= new BlogDbContext();

   return View(db.Posts.ToList());

}
這個代碼是第一篇里面的,獲取Posts里面的數據,然后返回的前台展示。還記得嗎?不記得翻到第一篇看看。


上面的代碼大家都見過第一篇里面的,那如果解耦的話這里用db.Posts是不是就不存在了,那要怎么做呢!同樣看代碼

public ActionResult Index()
        {
            var dbContext = new BlogDbContext();
           IQueryable
<Post> Posts = dbContext.Set<Post>
();
            return View(Posts.ToList());
        }

這個就是上面說的用DbContext.Set<TEntity>()方法來實現指定實體的屬性。簡單嗎?一句話就可以搞定。

 

最后我把我們最終的源碼發布一下

public BlogDbContext()
            : base()
        { }
        ///實體集合
        // public IDbSet<BlogUser> BlogUsers { get; set; }
        //public IDbSet<Post> Posts { get; set; }

        /// <summary>
        /// 重寫配置類
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // base.OnModelCreating(modelBuilder);
            //modelBuilder.Configurations.Add(new BlogUserConfiguration());
            //modelBuilder.Configurations.Add(new PostConfiguration());

            modelBuilder.Entity<BlogUser>().HasKey(m => m.BlogUserId);

            IEnumerable<IEntityMapper> EntityMappers = GetAllEntityMapper();
            if (EntityMappers == null)
            {
                return;
            }
            foreach (var mapper in EntityMappers)
            {
                mapper.RegistTo(modelBuilder.Configurations);
            }

        }


        private static ICollection<IEntityMapper> GetAllEntityMapper()
        {
            ICollection<Assembly> EntityMapperAssemblies = EntityMapperAssemblies = new[]
                        {
                            Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "EFCore.dll"))
                        };
            if (EntityMapperAssemblies.Count == 0)
            {
                throw new InvalidOperationException("上下文“{0}”初始化失敗,請添加實體映射程序集");
            }
            Type baseType = typeof(IEntityMapper);
            Type[] mapperTypes = EntityMapperAssemblies.SelectMany(assembly => assembly.GetTypes())
                .Where(type => baseType.IsAssignableFrom(type) && type != baseType && !type.IsAbstract).ToArray();
            ICollection<IEntityMapper> result = mapperTypes.Select(type => Activator.CreateInstance(type) as IEntityMapper).ToList();
            return result;
        }

現在如果你在新加一個實體類,就可以不用改這里呆任何一句代碼了。已經完全解耦了。是不是蠻簡單。

結語

      這篇完了以后,其實最基礎版的EF學習算是完了。之前的這幾篇也希望初學者都能掌握。后面的話要深入的講解一下,到時候會講到IOC ,還有Repository,UnitOfWork,DbContext。這些也都是我們在正式項目使用中要用到的。

     歡迎大家交流。多些支持。QQ群(435498053) 輕松做生意外貿軟件


免責聲明!

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



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