一步一步学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) 个人QQ 121740617

时间: 2024-11-02 20:10:46

一步一步学EF系列【5、升级篇 实体与数据库的映射】live writer真坑,第4次补发的相关文章

一步一步学EF系列【4、升级篇 实体与数据库的映射】live writer真坑,第4次补发

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

一步一步学EF系列【6、IOC 之AutoFac】

前言 之前的前5篇作为EF方面的基础篇,后面我们将使用MVC+EF 并且使用IOC ,Repository,UnitOfWork,DbContext来整体来学习.因为后面要用到IOC,所以本篇先单独先学习一下IOC,我们本本文单独主要学习Autofac,其实对于Autofac我也是边学边记录.不对的地方,也希望大家多多指导. 个人在学习过程中参考博客: AutoFac文档:http://www.cnblogs.com/wolegequ/archive/2012/06/09/2543487.htm

一步一步学EF系列【3、数据迁移】

我们每篇的内容都不多,所以希望在学习的过程中最后能亲自敲一下代码 这样更有利于掌握. 我们现在接着上篇的例子,我们现在给随便的表增加一个字段 CreateTime 创建日期 运行一下 看看会怎么样 修改实体类,代码给大家分享一下 public partial class Post { /// <summary> /// 随笔的主键id /// </summary> public int PostId { get; set; } // 随笔的标题 public string Post

一步一步学EF系列【2、Fluent API的方式来处理实体与数据表之间的映射关系。】

EF里面的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面,还有一个就是Fluent API,通过新增相应的配置类来覆盖默认配置另外.我们主要学习Fluent API,Data Annotations可以自行去学习一下. 补充一下为什么要用Fluent API 使用DataAnnotation非常简单,但对于EntityFramework中的特性,就要在实体类中引入Ent

【DG】[三思笔记]一步一步学DataGuard

[DG][三思笔记]一步一步学DataGuard 它有无数个名字,有人叫它dg,有人叫它数据卫士,有人叫它data guard,在oracle的各项特性中它有着举足轻理的地位,它就是(掌声)......................Oracle Data Guard.而对于我而言,我一定要亲切的叫它:DG(注:主要是因为打着方便). 不少未实际接触过dg的初学者可能会下意识以为dg是一个备份恢复的工具.我要说的是,这种形容不完全错,dg拥有备份的功能,某些情况下它甚至可以与primary数据库

Linux C编程学习5---参考《那年,一步一步学linux c》全系列(目录索引)

漫无目的的搜索一些东西,发现的一个很好的资源,所以就一定要收藏下来,方便自己学习Linux C 的时候也能够去参考一下别人的学习之路,来更加促进我的学习和思考 说明 转载请注明出处:谢谢:http://blog.csdn.net/muge0913/article/details/7342977 博主的邮箱是:[email protected] 文章中若有不对或某些功能更好的实现方法,请指出或直接留言. 该系列文章中所用结构数据代码均来自linux2.6.39. 1.那年,一步一步学linux c

一步一步学Linq to sql系列文章 转lovecherry

http://www.cnblogs.com/lovecherry/archive/2007/08/13/853754.html 现在Linq to sql的资料还不是很多,本人水平有限,如果有错或者误导请指出,谢谢. 一步一步学Linq to sql(一):预备知识 一步一步学Linq to sql(二):DataContext与实体 一步一步学Linq to sql(三):增删改 一步一步学Linq to sql(四):查询句法 一步一步学Linq to sql(五):存储过程 一步一步学L

Rhythmk 一步一步学 JAVA (21) JAVA 多线程

1.JAVA多线程简单示例 1.1 .Thread  集成接口 Runnable 1.2 .线程状态,可以通过  Thread.getState()获取线程状态: New (新创建) Runnable (可以运行) Blocked  (被阻塞) Waiting  (等待) Timed waiting (计时等待) Terminated  (被终止) ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

Rhythmk 一步一步学 JAVA (22) JAVA 网络编程

1.获取主机信息 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test     public void GetDomainInfo() throws UnknownHostException {         String domain = "www.baidu.com";         InetAddress netAddress = InetAddress.getByName(domain);         // 获取