gpt4 book ai didi

c# - 每次解析具有不同依赖实现的同一个类的多个实例

转载 作者:行者123 更新时间:2023-11-30 18:02:00 25 4
gpt4 key购买 nike

我有一项服务可以在运行时以 XML 格式动态接收配置。我有一个服务类,我需要创建它的多个实例,为每个服务类实例提供不同的依赖项实现。考虑以下示例:

interface ILogger { }
class FileLogger : ILogger { }
class DebugLogger : ILogger { }
class ConsoleLogger : ILogger { }

interface IStorage { }
class RegistrySrorage : IStorage { }
class FileStorage : IStorage { }
class DatabaseStorage : IStorage { }

class MyService
{
ILogger _logger;
IStorage _storage;
public MyService(ILogger logger, IStorage storage)
{
_logger = logger;
_storage = storage;
}
}

我可以像这样手动进行依赖注入(inject):

IEnumerable<MyService> services = new List<MyService>()
{
new MyService(new FileLogger(), new RegistrySrorage()),
new MyService(new FileLogger(), new DatabaseStorage()),
new MyService(new ConsoleLogger(), new FileStorage()),
new MyService(new DebugLogger(), new FileStorage()),
// same implementations as in previous instance are used but with different
// constructor parameter: for example, different destination in FileStorage
new MyService(new DebugLogger(), new FileStorage()),
};

有没有一种方法可以创建 XML 配置并有一个 DI 框架来提供类似于上面的手动示例的已配置 MyService 实例的集合?

更新

我自己找到了 autofac 的解决方案,但我认为这不是最好的方法。

创建的服务列表:

<component service="System.Collections.IList, mscorlib" type="System.Collections.ArrayList, mscorlib" name="ServicesList">
<parameters>
<parameter name="c">
<list>
<item value="loggerUID,storageUID"/>
</list>
</parameter>
</parameters>
</component>

然后创建所有必需组件的列表以解决依赖关系并唯一地命名它们:

<component service="Test.ILogger"
type="Test.FileLogger"
name="loggerUID">
<parameters>
<parameter name="logFile" value="C:\Temp\MyLogForSvc_1.log" />
</parameters>
</component>

然后在第一遍代码中检索所有服务的列表(名为“ServicesList”的组件)。在第二遍中,从 XML 加载组件后,我使用提供的组件名称作为键在代码中注册所有服务(此处不进行健全性检查):

foreach (string cfg in servicesList)
{
string[] impl = cfg.Split(',');

builder.Register<MyService>(c => new MyService(
c.ResolveKeyed<ILogger>(impl[0]),
c.ResolveKeyed<IStorage>(impl[1])))
.Named<MyService>(cfg);
}
IContainer container = builder.Build();

List<MyService> services = new List<MyService>();
foreach (string svcName in servicesList)
services.Add(container.ResolveNamed<MyService>(svcName));

欢迎提出改进建议。

最佳答案

恐怕 Autofac 没有那么灵活。它支持 XML configuration但我希望它仅支持原始类型作为构造函数参数。

另一方面,您的示例似乎是依赖注入(inject)的错误用法。当注入(inject)组件不关心谁使用它,组件消费者也不关心它接收到哪个服务实现时,基本上应该使用 DI。我会向 ILoggerIStorage 添加一个标识属性,使 MyService 接收所有可用的记录器和存储,并在其中实现处理其的逻辑具体配置以确定要使用的组合。像这样:

public interface ILogger
{
string Id { get; }
}
public class FileLogger : ILogger
{
public string Id { get { return "Logger.File"; } }
}
// etc.

public interface IStorage
{
string Id { get; }
}
public class RegistrySrorage : IStorage
{
public string Id { get { return "Storage.Registry"; } }
}

public class MyService
{
IList<Config> _EnabledConfigs;

public MyService(IEnumerable<ILogger> loggers, IEnumerable<IStorage> storages)
{
_EnabledConfigs = ParseXmlConfigAndCreateRequiredCombinations(loggers, storages);
}

class Config
{
public ILogger Logger { get; set; }
public IStorage Storage { get; set; }
}
}

// container config:
public static void ConfigureContainer(IContainerBuilder builder)
{
builder.RegisterType<FileLogger>.AsImplementedInterfaces();
// other loggers next...

builder.RegisterType<RegisterStorage>.AsImplementedInterfaces();
// then other storages

builder.RegisterType<MyService>();
}

配置如下:

<MyServiceConfig>
<EnabledCombinations>
<Combination Logger="Logger.File" Storage="Storage.Registry"/>
<!-- Add other enabled combinations -->
</EnabledCombinations>
</MyServiceConfig>

想一想。我敢打赌这会让事情变得容易得多。

作为一种选择,您可以创建一个单独的类来负责配置您的 MyService,这样 MyService 就不会包含与配置相关的逻辑。

更新

如果你真的需要这种复杂的依赖配置逻辑,最好用 c# 代码表达,你最好的选择是使用 Modules .只需将配置所需内容的代码提取到单独的 Autofac 模块中:

public class MyServiceConfigModule : Module
{
protected override void Load(ContainerBuilder builder)
{
// register some compopnent that uses MyService and initialize it with
// the required set of loggers and storages
builder.Register(ctx => new MyServiceConsumer(
new List<MyService>()
{
new MyService(new FileLogger(), new RegistrySrorage()),
new MyService(new FileLogger(), new DatabaseStorage()),
new MyService(new ConsoleLogger(), new FileStorage()),
new MyService(new DebugLogger(), new FileStorage()),
// same implementations as in previous instance are used but with different
// constructor parameter: for example, different destination in FileStorage
new MyService(new DebugLogger(), new FileStorage()),
}));
}
}

,将其放入单独的程序集“MyServiceConfig”中,并向 app.config 添加几行配置:

<autofac>
<modules>
<module type="MyServiceConfigModule, MyServiceConfig" />
</modules>
</autofac>

当您需要更改它时,您可以编写新的模块源文件,就地编译它(csc.exe 总是存在于装有 .NET 的机器上),然后用它交换旧的。当然,这种方法仅适用于“启动时”配置。

关于c# - 每次解析具有不同依赖实现的同一个类的多个实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8458575/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com