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

在Microsoft DI工厂方法模式中,如何将委托注册后解析为Func?

问题场景与需求

我们有一系列结构相似的ViewModel类,每个类内部都定义了专属的工厂委托,构造函数同时包含业务传入的自定义Data参数和需从DI容器注入的服务:

public class ViewModel1
{
    public delegate ViewModel1 Factory(List<string> data);

    public ViewModel1(List<string> data, IOtherService service)
    {
    }
}

public class ViewModel2
{
    public delegate ViewModel2 Factory(int data);

    public ViewModel2(int data, IOtherService service)
    {
    }
}
// 更多同结构ViewModel类...

创建这类ViewModel时,我们希望由容器自动注入IOtherService,同时从业务逻辑灵活传入Data参数。常规做法是为每个ViewModel单独注册工厂委托:

// 注册ViewModel1的工厂
services.AddScoped<ViewModel1.Factory>(sp =>
{
    var factory = ActivatorUtilities.CreateFactory(typeof(ViewModel1), new[] { typeof(List<string>) });
    return data => (ViewModel1)factory.Invoke(sp, new object[] { data });
});

// 使用示例
var factory = sp.GetRequiredService<ViewModel1.Factory>();
var vm = factory(new List<string> { "test", "data" });

但我们需要一套泛化的通用方案,能通过Func<TData, TModel>统一解析,实现如下简洁调用:

public static TModel GetVM<TModel, TData>(this ServiceProvider sp, TData data)
{
    var factory = sp.GetRequiredService<Func<TData, TModel>>();
    return factory(data);
}

// 目标调用方式
var vm = sp.GetVM<ViewModel2, int>(data: 42); 

核心问题:.NET默认DI容器不支持Autofac那样的自动委托兼容解析(比如自动将Func<int, ViewModel2>映射到已注册的ViewModel2.Factory),如何实现这一特性?


解决方案

要让.NET DI支持Func<TIn, TRes>与自定义工厂委托的兼容解析,可通过以下几种方式实现:

1. 扩展GetVM方法:自动匹配并转换自定义工厂委托

直接修改泛型扩展方法,让它自动查找ViewModel内部的Factory委托,转换为Func<TData, TModel>后调用:

using System.Reflection;

public static TModel GetVM<TModel, TData>(this IServiceProvider sp, TData data)
{
    // 查找ViewModel内部的Factory委托类型
    var factoryType = typeof(TModel).GetNestedTypes(BindingFlags.Public | BindingFlags.Instance)
        .First(t => t.Name == "Factory" && t.IsSubclassOf(typeof(MulticastDelegate)));
    
    // 从容器获取已注册的Factory委托
    var factoryDelegate = sp.GetRequiredService(factoryType);
    
    // 将自定义委托转换为标准Func<TData, TModel>
    var func = (Func<TData, TModel>)Delegate.CreateDelegate(
        typeof(Func<TData, TModel>), 
        factoryDelegate, 
        factoryType.GetMethod("Invoke"));
    
    return func(data);
}

调用方式保持不变,无需额外修改注册逻辑:

var vm = sp.GetVM<ViewModel2, int>(42);

2. 自定义ServiceProvider:全局支持委托兼容解析

如果希望全局范围内都能直接解析Func<TIn, TRes>,可以创建一个装饰性的ServiceProvider,自动处理委托类型匹配:

using System.Reflection;

public class DelegateCompatibleServiceProvider : IServiceProvider
{
    private readonly IServiceProvider _innerProvider;

    public DelegateCompatibleServiceProvider(IServiceProvider innerProvider)
    {
        _innerProvider = innerProvider;
    }

    public object GetService(Type serviceType)
    {
        // 仅处理Func<TIn, TRes>类型的解析请求
        if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(Func<,>))
        {
            var genericArgs = serviceType.GetGenericArguments();
            var inputType = genericArgs[0];
            var resultType = genericArgs[1];

            // 查找结果类型内部的Factory委托
            var factoryType = resultType.GetNestedTypes(BindingFlags.Public | BindingFlags.Instance)
                .FirstOrDefault(t => t.Name == "Factory" && t.IsSubclassOf(typeof(MulticastDelegate)));
            
            if (factoryType != null)
            {
                // 验证委托签名是否匹配Func<TIn, TRes>
                var invokeMethod = factoryType.GetMethod("Invoke");
                var invokeParams = invokeMethod.GetParameters();
                if (invokeParams.Length == 1 && invokeParams[0].ParameterType == inputType 
                    && invokeMethod.ReturnType == resultType)
                {
                    // 获取已注册委托并转换为目标Func类型
                    var factoryDelegate = _innerProvider.GetRequiredService(factoryType);
                    return Delegate.CreateDelegate(serviceType, factoryDelegate, invokeMethod);
                }
            }
        }

        // 非Func类型直接转发给原容器
        return _innerProvider.GetService(serviceType);
    }
}

使用时包装原ServiceProvider即可:

// 构建容器后包装
var sp = new DelegateCompatibleServiceProvider(serviceProvider);

// 现在可直接解析Func<TIn, TRes>
var factory = sp.GetRequiredService<Func<int, ViewModel2>>();
var vm = factory(42);

3. 简化方案:直接注册Func<TData, TModel>

如果不需要保留ViewModel内部的Factory委托定义,可跳过自定义委托,直接注册标准Func<TData, TModel>:

public static IServiceCollection AddViewModelFunc<TModel, TData>(this IServiceCollection services)
{
    services.AddScoped<Func<TData, TModel>>(sp =>
    {
        var factory = ActivatorUtilities.CreateFactory(typeof(TModel), new[] { typeof(TData) });
        return data => (TModel)factory.Invoke(sp, new object[] { data });
    });
    return services;
}

// 注册示例
services.AddViewModelFunc<ViewModel1, List<string>>();
services.AddViewModelFunc<ViewModel2, int>();

// 使用时直接解析Func
var factory = sp.GetRequiredService<Func<int, ViewModel2>>();
var vm = factory(42);

内容的提问来源于stack exchange,提问作者Serg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 18:34:50