一步一步学ef系列4升级篇实体与数据库的映射livewriter真坑,第4次补发

在西天取经的路上…… 在西天取经的路上……     2022-08-14     146

关键词:

前言

       之前的几篇文章,被推荐到首页后,又被博客园下了,原因内容太少,那我要写多点呢,还是就按照这种频率进行写呢?本身我的意图这个系列就是想已最简单最容易理解的方式进行,每篇内容也不要太多,这样初学者容易理解学习,否则天花乱坠的一大篇初学者从头看到尾也要晕了。所以每次突出重点进行浓缩精华时的讲,当然我这样精简讲,你们要学深入的话,也还是要把有些概念学深入一下。也欢迎大家共同讨论学习。我这里创建了一个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) 

一步一步学vue

接上篇,这次是真的接上篇,针对上篇未完成的部分,增加鉴权功能,开始之前,我们先要介绍一个新的知识,路由元数据。 在vue-router中,定义元数据的方式:constrouter=newVueRouter({routes:[{path:‘/foo‘,component:Foo,children:[{path:... 查看详情

一步一步学jvm-运行时数据区域

程序计数器(ProgramCounterRegister)        像我们平时读书一样,当我们在去做别的事情之前,我们会对我们读到什么地方了做一个标记,方便我们再回来的时候接着重新读。如果这本书有很多人读呢... 查看详情

一步一步学rendermonkey

http://blog.csdn.net/tianhai110/article/details/5668832 转载请注明出处:http://blog.csdn.net/tianhai110/ 网上一些关于renderMonkey的教程:《RenderMonkey的基本使用方法》http://www.cnblogs.com/mixiyou/archive/2009/10/05/ 查看详情

一步一步学多线程-synchronized

  当线程执行请求synchronized方法或块时,monitor会设置几个虚拟逻辑数据结构来管理这些多线程。      请求的线程会首先被加入到线程排队队列中,线程阻塞,当某个拥有线程锁的线程unlock之后,则排队队列里的线程... 查看详情

一步一步学vue

为了提升代码的逼格,之后代码改为Vue文件组件,之前代码虽然读起来容易理解,而且适合在小的项目中使用,但是有如下缺点:全局定义(Globaldefinitions) 强制要求每个component中的命名不得重复字符串模板(Stringtemplates) 缺... 查看详情

一步一步学jvm-java内存模型

主内存与工作内存        Java内存模型的主要目标是定义程序中各个变量的访问规则。即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。这里的变量和Java编程中所说的变量有所区... 查看详情

一步一步学j2se-concurrenthashmap原理

  ConcurrentHshMap的数据结构是由一个Segment数组和多个HashEntry数组组成,在Segement数组中包含了HashEntry数组。数据结构如下图所示:  Segement数组的意义就是将一个大的table分割成多个小的table来加锁,而每一个Segment元素存储的... 查看详情

一步一步学vue

...不同,我们会对其进行增删改查的基本操作,之后进行进一步的完善,按照常规的系统使用经验,一般我们新增和编辑都是在模态框中处理,这里我们不会去构建复杂的模态框,只用一个简单的div层来代替,后期接下来的文章中... 查看详情

一步一步学vue

  前言:我以后在文章最后再也不说我下篇博文要写什么,之前说的大家也可以忽略,如果你不忽略,会失望的??,不过说出去的话还是要表示一下的,简单介绍一下路由钩子:  正如其名,vue-router 提供的导航钩子主要... 查看详情

一步一步学vue

本篇是是vue路由的开篇,会以一个简单的demo对vue-router进行一个介绍,主要覆盖以下几个常用场景:1、路由跳转2、嵌套路由3、路由参数 1、Vue-Router  一般来说,路由定义就是定义地址访问规则,然后由路由引擎根据这些... 查看详情

一步一步学jvm-垃圾回收算法

标记-清除算法        算法分为标记和清除两个阶段:首先标记所有需要回收的对象,在标记完成后统一回收所有被标记的对象。        该算法存在的缺点:  1、 ... 查看详情

一步一步学nlp:熟悉nlp

NLP学习AI工程师必备的核心技能现实生活中的问题---->数学优化问题---->通过合适的工具解决whatisNLPNLP=NLP+NLUNLU:语音/文本->意思(meaning)Natural+langugeUnderstandingNLG:意思->文本/语音Natural+LangugeGenerationwhatis... 查看详情

一步一步学nlp:熟悉nlp

NLP学习AI工程师必备的核心技能现实生活中的问题---->数学优化问题---->通过合适的工具解决whatisNLPNLP=NLP+NLUNLU:语音/文本->意思(meaning)Natural+langugeUnderstandingNLG:意思->文本/语音Natural+LangugeGenerationwhatis... 查看详情

一步一步学j2se-hashmap的实现原理

HashMap数据结构图  HashMap的数据结构是通过数组加链表实现的。数组是HashMap的主体,链表是为了解决Hash碰撞问题。  HashMap的Put方法1、 在put的时候首先判断key值是不是null,如果是null,则处理null值为key所放的位置2、 ... 查看详情

linux一步一步学linux——enable命令(212)(代码片段)

00.目录文章目录00.目录01.命令概述02.命令格式03.常用选项04.参考示例05.总结06.附录01.命令概述enable命令可以用于启动或关闭shell的内建指令。如要执行的文件名称与shell内建指令相同,可用enable-n来关闭shell内建指令。若不加-n... 查看详情

一步一步学vue

本篇完成如下场景:1、系统包含首页、客户信息查询、登录三个模块2、默认进入系统首页,如果要进行用户查询,则需要进行登录授权3、查询用户后点击列表项,则进入详情页面基于上述场景需求描述,在客户端我们考虑,需... 查看详情

一步一步学jvm-垃圾回收器

Serial收集器        Serial收集器是最基本、历史最悠久的收集器。这个收集器是一个单线程的收集器。它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。Serial收集器是新生代的收... 查看详情

我的新书一步一步学springboot2:微服务项目实战

开心一笑【声音有磁性的人适合做直播,可以吸引到很多老铁】提出问题我的新书具体内容???购买地址淘宝地址1:https://detail.tmall.com/item.htm?spm=a230r.1.14.4.288c2acbVNjuUw&id=571212826401&cm_id=1401 查看详情