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

.NET 6.0命名服务工厂实现:Blazor同接口多配置注册问题

实现Blazor客户端的命名式服务工厂

核心思路

无需新增接口或实现类,通过注册带配置逻辑的实例创建委托,用字符串名称关联不同配置,工厂根据名称匹配对应的委托,创建并返回已配置好的实例。

步骤1:定义通用工厂接口与实现

先实现通用的命名服务工厂,用于存储不同名称对应的实例创建逻辑:

// 通用工厂接口,支持按名称创建指定类型实例
public interface INamedServiceFactory<TService>
{
    TService Create(string name);
}

// 通用工厂实现类
public class NamedServiceFactory<TService> : INamedServiceFactory<TService>
{
    private readonly Dictionary<string, Func<IServiceProvider, TService>> _factoryMap = new();
    public IServiceProvider? ServiceProvider { get; set; }

    // 注册名称对应的实例创建逻辑
    public void Register(string name, Func<IServiceProvider, TService> factoryFunc)
    {
        if (_factoryMap.ContainsKey(name))
            throw new InvalidOperationException($"服务名称「{name}」已注册");
        
        _factoryMap.Add(name, factoryFunc);
    }

    // 根据名称创建并返回配置好的实例
    public TService Create(string name)
    {
        if (!_factoryMap.TryGetValue(name, out var factoryFunc) || ServiceProvider == null)
            throw new KeyNotFoundException($"未找到名称为「{name}」的服务配置");
        
        return factoryFunc(ServiceProvider);
    }
}

步骤2:编写服务注册扩展方法

实现你期望的AddFoodFactory风格扩展方法,本质是将每个名称的配置逻辑注册到通用工厂:

public static class NamedServiceFactoryExtensions
{
    // 通用注册方法:支持接口-实现类绑定,以及实例配置委托
    public static IServiceCollection AddNamedServiceFactory<TInterface, TImplementation>(
        this IServiceCollection services,
        string name,
        Action<TImplementation> configureAction)
        where TImplementation : class, TInterface
    {
        // 获取或注册对应的NamedServiceFactory实例(单例)
        var factory = services.GetOrAddFactory<TInterface>();

        // 注册该名称对应的实例创建逻辑:先实例化,再执行配置
        factory.Register(name, sp =>
        {
            // 自动注入实现类的构造函数依赖
            var instance = ActivatorUtilities.CreateInstance<TImplementation>(sp);
            // 执行配置委托,设置差异化属性
            configureAction(instance);
            return instance;
        });

        return services;
    }

    // 辅助方法:获取已注册的工厂,不存在则创建并注册为单例
    private static NamedServiceFactory<TService> GetOrAddFactory<TService>(this IServiceCollection services)
    {
        var existingFactory = services.FirstOrDefault(sd => 
            sd.ServiceType == typeof(INamedServiceFactory<TService>) && 
            sd.ImplementationType == typeof(NamedServiceFactory<TService>));

        if (existingFactory != null)
        {
            return (NamedServiceFactory<TService>)existingFactory.ImplementationInstance!;
        }
        else
        {
            var factory = new NamedServiceFactory<TService>();
            // 注册工厂为单例
            services.AddSingleton<INamedServiceFactory<TService>>(factory);
            // 注入服务提供者,确保工厂能解析依赖
            services.AddSingleton(sp => 
            {
                factory.ServiceProvider = sp;
                return factory;
            });
            return factory;
        }
    }
}

步骤3:适配业务场景(FoodService)

假设你的业务服务定义如下:

public interface IFoodService
{
    string GetRoom();
}

public class FoodService : IFoodService
{
    // 可配置的差异化属性
    public string StorageRoom { get; set; } = string.Empty;

    public string GetRoom()
    {
        return StorageRoom;
    }
}

注册服务(Program.cs)

完全按照你期望的方式注册:

builder.Services.AddNamedServiceFactory<IFoodService, FoodService>("Citrus", service =>
{
    service.StorageRoom = "A1";
});

builder.Services.AddNamedServiceFactory<IFoodService, FoodService>("Starch", service =>
{
    service.StorageRoom = "A2";
});

步骤4:在Blazor组件中使用

在组件中注入工厂,按名称创建实例:

@inject INamedServiceFactory<IFoodService> FoodServiceFactory

@code {
    private IFoodService? CitrusService;
    private IFoodService? StarchService;

    protected override void OnInitialized()
    {
        CitrusService = FoodServiceFactory.Create("Citrus");
        StarchService = FoodServiceFactory.Create("Starch");

        // 测试输出:分别打印"A1"和"A2"
        Console.WriteLine(CitrusService.GetRoom());
        Console.WriteLine(StarchService.GetRoom());
    }
}

与参考实现的核心差异

你参考的Tim Corey实现是针对不同实现类的工厂,而本方案是针对同一实现类的不同配置实例:

  • 无需为每个配置新增实现类,仅通过Action委托完成属性配置
  • 工厂存储的是实例创建+配置的委托,而非已实例化的对象集合
  • 自动支持实现类的构造函数依赖注入(比如FoodService需要其他服务时,ActivatorUtilities会自动解析)

内容的提问来源于stack exchange,提问作者DONAR144-Release

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 09:45:33