在EntityFramework Code First的示例中,一般情况下都是要创建一个继承DBContext的类,然后在此类中声明若干DBSet<>的属性,然后才可以使用。最近我就遇到一件为难的事情,项目中的业务对象较多,有一大半是继承了一个自定义的基类ModelBase,如果按照以往的方式就不得不在DBContext里面声明长长的属性,其实就是想有个简便的办法,加上如果后续增加了ModelBase的子类,也不想再去修改DBContext的代码,于是一个念头产生了。
    最初我尝试用反射的方式,定义一个方法,传入我想创建的业务对象的类型(Type),结果发现这样是没有办法对一个已存在的类(DBContext)去增加泛型属性DBSet<Type>的。
    之后又尝试了创建一个EntityTypeConfiguration<ModelBase>的配置类,然后在其中利用反射,为当前程序集中所有子类都配置映射关系,结果发现基类无可避免地被映射成了一张表,因为EntityTypeConfiguration<ModelBase>的原因,首先就纳入到了DBContext之中了。这并不是我所期望的效果。
    于是,我祭出了杀手锏---Emit。基本思路就是动态创建继承DBContext的一个子类对象,根据传入的类来决定是创建其本身或是其子类的DBSet<>属性,这样可控性比较强。
    先用一个简单的例子来说明一下。声明一个类

    public class Author{public int Id { get; set; }public string Name { get; set; }}

如果按照旧方法,原本应该这样声明

    public class Blog : DbContext{public DbSet<Author> Authors { get; set; }}

现在换成如下方式。

        public static DbContext CreateInstance(){AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("TestContext"), AssemblyBuilderAccess.RunAndSave);ModuleBuilder mb = ab.DefineDynamicModule("TestContext");//创建指定名称的类型,注意此名称必须要符合EF的约定,即与连接字符串配置中的Name一致TypeBuilder tb = mb.DefineType("Blog", TypeAttributes.Public, typeof(DbContext));PropertyBuilder pbAuthor = tb.DefineProperty("Authors", PropertyAttributes.HasDefault, typeof(DbSet<>).MakeGenericType(typeof(Author)), null);//创建私有字段,用于属性的读写//不能像c#源代码那样直接声明get/set,因为本质上编译后还是有私有字段的FieldBuilder fbAuthors = tb.DefineField("_authors", typeof(DbSet<>).MakeGenericType(typeof(Author)), FieldAttributes.Private);//Get方法部分System.Reflection.MethodAttributes methodAttributes = System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.HideBySig;MethodBuilder mbGet = tb.DefineMethod("get_Authors", methodAttributes);ILGenerator iL = mbGet.GetILGenerator();Label label = iL.DefineLabel();iL.Emit(OpCodes.Ldarg_0);iL.Emit(OpCodes.Ldfld, fbAuthors);iL.Emit(OpCodes.Stloc_0);iL.Emit(OpCodes.Br_S, label);iL.MarkLabel(label);iL.Emit(OpCodes.Ldloc_0);iL.Emit(OpCodes.Ret);//Set方法部分MethodBuilder mbSet = tb.DefineMethod("set_Authors", methodAttributes);mbSet.SetParameters(typeof(System.Data.Entity.DbSet<>).MakeGenericType(typeof(Author)));ParameterBuilder value = mbSet.DefineParameter(1, ParameterAttributes.None, "value");iL = mbSet.GetILGenerator();iL.Emit(OpCodes.Ldarg_0);iL.Emit(OpCodes.Ldarg_1);iL.Emit(OpCodes.Stfld, fbAuthors);iL.Emit(OpCodes.Ret);//将定义的方法引用与属性相关联
            pbAuthor.SetGetMethod(mbGet);pbAuthor.SetSetMethod(mbSet);DbContext blog = (DbContext)Activator.CreateInstance(tb.CreateType());  return blog;}

最后来一段测试的代码

using (DbContext blog = CreateInstance()){        Author author = new Author{Name = "123"};blog.Set<Author>().Add(author);blog.SaveChanges();
}

总结一下,其实Emit创建DbContext的重心就在于对每一个需要操作的业务对象都创建一对Get/Set方法,上述代码可以进一步提炼,单独写成一个方法,传入不同的Type替换掉Author所占的位置即可。此方法很另类,性能上也并非很理想(在对象数量比较庞大的情况下反而效率较高),因此在使用EF的过程中就初始化的时候创建一次即可,在有对象的变更或增减时,可以随着Migration(EF4.3以后新增特性)一起更新。

转载于:https://www.cnblogs.com/BeanHsiang/archive/2012/05/04/2482808.html

使用Emit创建DBContext对象相关推荐

  1. EntityFramework Core 1.1是如何创建DbContext实例的呢?

    前言 上一篇我们简单讲述了在EF Core1.1中如何进行迁移,本文我们来讲讲EF Core1.1中那些不为人知的事,细抠细节,从我做起. 显式创建DbContext实例 通过带OnConfiguri ...

  2. Rust 不同方式创建Arc对象性能对比

    背景 在C++(C++11)中有了移动语义后,标准库中的很多容器都实现了原地构建对象的接口, 例如 vector::emplace_pack. 得益于此,在适当的场合下,可以大量减少临时对象的创建和对 ...

  3. OpenCV 笔记(06)— Mat 结构、像素值存储方法、创建 Mat 对象各种方法、Mat 对象的运算

    数字图像中的每个点都称为像素(对于图像元素),并且每个像素可以存储一个或多个值,这取决于它是否是仅存储一个值的黑白图像(也称为二进制图像,比如只存储0或1),还是存储两个值的灰度图像,或者是存储三个值 ...

  4. java string对象创建_String对象创建个数

    s = new String("xyz");创建了几个String Object?两个对象,一个是"xyx",一个是指向"xyx"的引用对象 ...

  5. 使用Blender中的几何节点创建程序对象

    大小解压后:2.4G 持续时间3h 30m 1280X720 MP4 语言:英语+中英文字幕(根据原英文字幕机译更准确) 使用Blender中的几何节点按程序创建对象 信息: 使用Blender中的几 ...

  6. 通过创建 HttpCookie 对象的实例编写 Cookie

    通过创建 HttpCookie 对象的实例编写 Cookie HttpCookie myCookie = new HttpCookie("UserSettings"); myCoo ...

  7. 创建UITextField对象

    //创建UITextField对象 UITextField * tf=[[UITextField alloc]init];   //设置UITextField的文字颜色 tf.textColor=[U ...

  8. Civil 3D 二次开发 创建AutoCAD对象—— 00 ——

    不积跬步无以至千里,不积小流无以成江海.虽然创建一条直线.添加一个图层这样的小程序没有什么实际意义(内部命令很简单就可以完成),但对于初学二次开发的您来说,这可是一大步,这一步跨出去,您就跨进了二次开 ...

  9. android java style_Android 在Java代码中设置style属性--使用代码创建ProgressBar对象

    强烈推荐: 在andriod开发中,很大一部分都要与资源打交道,比如说:图片,布局文件,字符串,样式等等.这给我们想要开发一些公共的组件带来很大的困难,因为公共的组件可能更愿意以jar包的形式出现.但 ...

  10. python做bi系统_如何使用Python创建可视化对象

    早前,Power BI就已经支持使用Python创建可视化对象了,当你遇到自定义程度较高的可视化对象时,Python就大大的派上了用场:那么我们如何使用呢?接下来小悦就为各位伙伴们介绍一下吧~ 首先, ...

最新文章

  1. 【论文速读】城市自动驾驶应用的概率语义地图
  2. js DOM之基础详解
  3. 【面试】上中断和下中断
  4. SQL Server 查看表定义的 2 种方法
  5. 《应用程序性能测试的艺术(第2版)》—第2章 2.4节小结
  6. [vim]vim 插件汇总
  7. 关于移动手机端富文本编辑器qeditor图片上传改造
  8. php将get传参解析成数组,php解析url (parse_url) 参数成数组 (parse_str)
  9. C#算法设计查找篇之03-插值查找
  10. 周三晚八点直播丨如何通过APEX 实现自动化运维
  11. leetcode 《简单》 数学部分 Python实现
  12. 2020-12-29 zabbix 安装 snmpwalk 命令和基本使用
  13. 【胡学长 带你学 Global Mapper 】Global Mapper Pro 23.1 -x64安装教程(附*英*软件包下载)
  14. win7计算机远程桌面连接,教您win7远程桌面连接
  15. BacNet IP开发过程
  16. mysql 联合索引 abc_ABC联合索引生效问题(整理笔记!!!)
  17. Docker容器内存占用过高解决方法
  18. Word 使用技巧大全
  19. C/C++超全资料,编程发烧友不可不分享
  20. mybatis黑马:一级缓存和二级缓存

热门文章

  1. python3.7怎么安装的_怎么安装python3.7:python 3.7入门教程
  2. 阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第6节 Lambda表达式_2_冗余的Runnable代码...
  3. 分布式session之redis解决方案实现
  4. 51Nod1601 完全图的最小生成树计数 Trie Prufer编码
  5. 赛码网算法: 格子游戏
  6. 【工匠大道】博客园小技巧
  7. IOS音频1:之采用四种方式播放音频文件(一)AudioToolbox AVFoundation OpenAL AUDIO QUEUE...
  8. CMD获取当前目录的绝对路径
  9. CentOS 5.4 安装和卸载桌面
  10. Rails开始遇到的一个端口占用问题的解决方法