You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需手动定义构造函数,如何通过反射创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:47:05