前言

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

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

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

    前言 之前的几篇文章,被推荐到首页后,又被博客园下了,原因内容太少,那我要写多点呢,还是就按照这种频率进行写呢?本身我的意图这个系列就是想已最简单最容易理解的方式进行,每篇内容也不要太多,这样初学者容 ...

  2. 一步一步学EF系列3【升级篇 实体与数据库的映射】

    之前的三张为基础篇,如果不考虑架构问题,做一般的小程序,以足够用了.基本的增删改查也都有了.但是作为学习显然是不够的.通过之前三章的学习,有没有发现这样写有什么问题,有没有觉得繁琐的?可能有人会说,之 ...

  3. 一步一步学EF系列四【升级篇 实体与数据库的映射】

    之前的三张为基础篇,如果不考虑架构问题,做一般的小程序,以足够用了.基本的增删改查也都有了.但是作为学习显然是不够的.通过之前三章的学习,有没有发现这样写有什么问题,有没有觉得繁琐的?可能有人会说,之 ...

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

    前言 之前的前5篇作为EF方面的基础篇,后面我们将使用MVC+EF 并且使用IOC ,Repository,UnitOfWork,DbContext来整体来学习.因为后面要用到IOC,所以本篇先单独先 ...

  5. 一步一步学EF系列 【7、结合IOC ,Repository,UnitOfWork来完成框架的搭建】

    前言 距离上一篇已经有段时间了,最近这段时间赶上新项目开发,一直没有时间来写.之前的几篇文章,主要把EF的基础都讲了一遍,这批文章就来个实战篇. 个人在学习过程中参考博客: Entity Framew ...

  6. 一步一步学EF系列一【最简单的一个实例】

    整个文章我都会用最简单,最容易让人理解的方式给大家分享和共同学习.(由于live Writer不靠谱 又得补发一篇) 一.安装 Install-Package EntityFramework 二.简单 ...

  7. 一步一步学EF系列2【最简单的一个实例】

    整个文章我都会用最简单,最容易让人理解的方式给大家分享和共同学习.(由于live Writer不靠谱 又得补发一篇) 一.安装 Install-Package EntityFramework 二.简单 ...

  8. 一步一步学EF系列三【数据迁移】

    我们每篇的内容都不多,所以希望在学习的过程中最后能亲自敲一下代码 这样更有利于掌握. 我们现在接着上篇的例子,我们现在给随便的表增加一个字段 CreateTime 创建日期 运行一下 看看会怎么样 修 ...

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

    EF里面的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面,还有一个就是F ...

随机推荐

  1. 【BZOJ】1604: [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居(set+并查集+特殊的技巧)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1604 这题太神了... 简直就是 神思想+神做法+神stl.. 被stl整的我想cry...首先,, ...

  2. EasyDarwin开发出相似于美拍、秒拍的短视频拍摄SDK:EasyVideoRecorder

    EasyVideoRecorder Github:https://github.com/EasyDarwin/EasyVideoRecorder EasyVideoRecorder作为一款短视频拍摄的 ...

  3. WPF 在TextBox失去焦点时检测数据,出错重新获得焦点解决办法

    WPF 在TextBox失去焦点时检测数据,出错重新获得焦点解决办法 在WPF的TextBox的LostFocus事件中直接使用Focus()方法会出现死循环的问题 正确的使用方式有2中方法: 方法一 ...

  4. Margin外边距叠加

    外边距(Margin)叠加 只有普通文档流中块级元素(块框)的垂直外边距才会发生外边距叠加.行内框.浮动框和绝对定位框之间的外边距不会叠加 两个相邻兄弟块框 父元素与子元素,并 父元素没有内边距或边框 ...

  5. C++ 访问控制 public, protected, private, 友元

    1. 变量属性与继承之间的关系 #include <iostream> using namespace std; class A { public: int x; protected: i ...

  6. Java连接MongoDB样例

    package com.moonlit.example; import com.mongodb.BasicDBObject; import com.mongodb.BulkWriteOperation ...

  7. switch语句相关

    Cannot switch on a value of type long. Only convertible int values, strings or enum variables are pe ...

  8. Struts入门(三)深入Struts用法讲解

    访问Servlet API Action搜索顺序 动态方法调用 指定多个配置文件 默认Action Struts 后缀 接收参数 处理结果类型 1.访问Servlet API 首先我们了解什么是Ser ...

  9. Go语言中的一些函数

    1.并行 通过使用goroutine和channel,go语言可以很好地支持并发,但是在我的电脑上是默认只使用一个核执行,要使用多核,在代码前面加入 import("runtime" ...

  10. 160607、springmvc+spring使用taskExecutor

    第一步:导入spring core的jar+springmvc的jar 第二步:springmvc的配置文件中 <bean id="taskExecutor" class=& ...