无需手动定义构造函数,如何通过反射创建EF DbContext实例?
解决方案:动态生成子类自动实现所需构造函数
我之前也遇到过你这种需要维护大量EF上下文,又不想每个都写重复构造函数的麻烦场景。既然不能修改EF模板,那动态生成继承目标上下文的子类就是最完美的方案——它能自动帮你实现需要的构造函数,完全不用手动编写任何分部类代码。
核心思路
当你的目标上下文(比如ContextForDataBase1)没有提供string或DbConnection重载的构造函数时,我们通过反射动态生成一个继承它的子类,在子类里实现对应的构造函数并直接调用基类(DbContext)的构造逻辑,然后创建这个子类的实例(因为继承关系,可以安全向上转型为目标上下文类型)。
实现动态上下文工厂
先写一个工具类,负责动态生成子类并创建实例,还加入了缓存避免重复生成类型提升性能:
using System; using System.Data.Common; using System.Reflection; using System.Reflection.Emit; using System.Data.Entity; public static class DynamicContextFactory { // 缓存动态生成的类型,避免重复创建 private static readonly Dictionary<Type, Type> _stringConstructorCache = new Dictionary<Type, Type>(); private static readonly Dictionary<Type, Type> _connectionConstructorCache = new Dictionary<Type, Type>(); private static readonly object _cacheLock = new object(); /// <summary> /// 通过连接字符串创建上下文实例(自动处理构造函数缺失情况) /// </summary> public static T CreateWithConnectionString<T>(string connectionString) where T : DbContext { var contextType = typeof(T); // 先检查目标类型是否已有对应构造函数 var existingCtor = contextType.GetConstructor(new[] { typeof(string) }); if (existingCtor != null) { return (T)Activator.CreateInstance(contextType, connectionString); } // 没有的话从缓存取或动态生成子类 lock (_cacheLock) { if (!_stringConstructorCache.TryGetValue(contextType, out var dynamicType)) { dynamicType = BuildDynamicContextSubclass<T>(new[] { typeof(string) }); _stringConstructorCache[contextType] = dynamicType; } return (T)Activator.CreateInstance(dynamicType, connectionString); } } /// <summary> /// 通过现有DbConnection创建上下文实例(自动处理构造函数缺失情况) /// </summary> public static T CreateWithExistingConnection<T>(DbConnection connection, bool contextOwnsConnection) where T : DbContext { var contextType = typeof(T); // 检查目标类型是否已有对应构造函数 var existingCtor = contextType.GetConstructor(new[] { typeof(DbConnection), typeof(bool) }); if (existingCtor != null) { return (T)Activator.CreateInstance(contextType, connection, contextOwnsConnection); } // 从缓存取或动态生成子类 lock (_cacheLock) { if (!_connectionConstructorCache.TryGetValue(contextType, out var dynamicType)) { dynamicType = BuildDynamicContextSubclass<T>(new[] { typeof(DbConnection), typeof(bool) }); _connectionConstructorCache[contextType] = dynamicType; } return (T)Activator.CreateInstance(dynamicType, connection, contextOwnsConnection); } } /// <summary> /// 动态生成继承自目标上下文的子类,并实现指定参数的构造函数 /// </summary> private static Type BuildDynamicContextSubclass<T>(Type[] ctorParamTypes) where T : DbContext { var baseType = typeof(T); var assemblyName = new AssemblyName($"Dynamic_{baseType.Name}_{Guid.NewGuid():N}"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); // 定义子类 var typeBuilder = moduleBuilder.DefineType( $"Dynamic{baseType.Name}", TypeAttributes.Public | TypeAttributes.Class, baseType); // 找到基类(DbContext)对应的构造函数 var baseCtor = typeof(DbContext).GetConstructor(ctorParamTypes); if (baseCtor == null) { throw new InvalidOperationException($"DbContext 没有匹配参数类型的构造函数"); } // 实现子类的构造函数,调用基类构造逻辑 var ctorBuilder = typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, ctorParamTypes); var ilGenerator = ctorBuilder.GetILGenerator(); // 加载this指针 ilGenerator.Emit(OpCodes.Ldarg_0); // 加载构造函数参数 for (int i = 0; i < ctorParamTypes.Length; i++) { ilGenerator.Emit(OpCodes.Ldarg, i + 1); } // 调用基类构造函数 ilGenerator.Emit(OpCodes.Call, baseCtor); // 返回 ilGenerator.Emit(OpCodes.Ret); return typeBuilder.CreateType(); } }
改进你的包装类
把原来的EntityDbConnection<T>改成调用这个工厂类,这样就完全不用关心目标上下文有没有构造函数了:
using System; using System.Data.Common; using System.Data.Entity; public class EntityDbConnection<T> : IDisposable where T : DbContext { private readonly T _context; public EntityDbConnection(string connectionString) { _context = DynamicContextFactory.CreateWithConnectionString<T>(connectionString); } public EntityDbConnection(DbConnection existingConnection, bool contextOwnsConnection) { _context = DynamicContextFactory.CreateWithExistingConnection<T>(existingConnection, contextOwnsConnection); } // 暴露上下文实例供业务代码使用 public T Context => _context; public void Dispose() { _context?.Dispose(); } }
使用方式(完全和你原来的一样)
不管你的上下文有没有写构造函数,都可以直接用:
// 通过连接字符串创建 using(var contextWrapper = new EntityDbConnection<ContextForDataBase1>(_connectionstring)) { var data = contextWrapper.Context.SomeTable.ToList(); // ...业务逻辑 } // 通过现有连接创建 using(var contextWrapper = new EntityDbConnection<ContextForDataBase2>(existingSqlConn, false)) { // ...业务逻辑 }
补充说明
- 性能优化:通过缓存动态生成的类型,只会在第一次使用某个上下文类型时生成一次子类,后续直接复用,性能几乎和直接使用原类型一致。
- 兼容性:这个方案适用于EF6(从你代码里的
EntityConnection判断你用的是EF6),如果是EF Core,逻辑类似,只需要调整基类构造函数的参数(EF Core用DbContextOptions)。 - 异常处理:你原来代码里的
MissingMethodException现在完全不需要了,工厂类会自动处理构造函数缺失的情况。
内容的提问来源于stack exchange,提问作者Neakas
相关产品推荐
相关产品推荐

