c# - 如何配置温莎以通过依赖树将依赖作为参数传递?

标签 c# castle-windsor

我有以下组件组成:

public interface IJob {
    ILogger Logger { get; set; }
}

public class JobC : IJob
{
    public ILogger Logger { get; set; }
    private ServiceA serviceA;
    private ServiceB serviceB;

    public JobC(ServiceA serviceA, ServiceB serviceB)
    {
        this.serviceA = serviceA;
        this.serviceB = serviceB;
    }
}

public class ServiceB
{
    public ILogger Logger { get; set; }
}

public class ServiceA
{
    public ILogger Logger { get; set; }
}

如您所见,到处都是 Logger 属性。问题是,我需要在解析期间传递该属性值(不同的作业需要不同的配置记录器)。 所以如果只有顶级组件需要这个,它会像

一样简单
var childLogger = Logger.CreateChildLogger(jobGroupName);
var job = windsorContainer.Resolve(jobType);
job.Logger = childLogger;

但我需要将 childLogger 向下传递到树中,并且该树非常复杂,我不希望手动将记录器实例传递给每个需要它的组件,想知道 Windsor 是否可以帮助我解决这个问题?

更新:这可能有助于更好地理解问题: 在 wiki有通知:

Inline dependencies don't get propagated Whatever arguments you pass to Resolve method will only be available to the root component
you're trying to resolve, and its Interceptors. All the components further down (root's dependencies, and their dependencies and so on) will not have access to them.

为什么会这样,有什么解决方法吗?

更新 2: 如果我添加真实情况,可能会有所帮助。

因此,我们有应用程序,它从各种销售 channel 发送数据/从各种销售 channel 接收数据。每个销售 channel 都有相应的作业集合,如发送更新的产品信息、接收订单等(每个作业可能包含更小的作业)。所以这是合乎逻辑的,我们需要将每个 channel 的日志信息与其他 channel 的日志信息分开,但是单个 channel 的作业日志应该转到单个监听器,我们可以看到正在发生的事情的顺序(如果每个作业和子作业都有自己的日志记录监听器,我们需要按时间合并日志以了解发生了什么)。一些 channel 及其工作集在编译时是未知的(假设有 channel A,我们可以通过简单地将那个国家添加到数据库来为特定国家启动单独的 channel ,根据负载我们可以切换同步方法等)。

这意味着什么,我们可能有 UpdateProductsForChannelAJob,它将在两个不同的 channel (ChannelA US 和 ChannelA UK)中使用,因此它的记录器将取决于它所依赖的 channel 。

那么我们现在正在做的是,我们为每个 channel 创建子记录器,并在将 Job 实例解析为参数时传递它。这行得通,但有一件烦人的事情 - 我们必须在作业中手动将记录器实例传递给每个可能正在记录某些内容的依赖项(和依赖项依赖项)。

更新 3:

我在 Windsor 文档功能中发现,这听起来像我需要的:

There are times where you need to supply a dependency, which will not be known until the creation time of the component. For example, say you need a creation timestamp for your service. You know how to obtain it at the time of registration, but you don't know what its specific value will be (and indeed it will be different each time you create a new instance). In this scenarios you use DynamicParameters method.

你在 DynamicParameters delegate 中得到两个参数,其中一个是字典和

It is that dictionary that you can now populate with dependencies which will be passed further to the resolution pipeline

鉴于此,我认为这会奏效:

public interface IService
{
}

public class ServiceWithLogger : IService
{
    public ILogger Logger { get; set; }
}

public class ServiceComposition
{
    public ILogger Logger { get; set; }

    public IService Service { get; set; }

    public ServiceComposition(IService service)
    {
        Service = service;
    }
} 

public class NameService
{
    public NameService(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
}

public class NameServiceConsumer
{       
    public NameService NameService { get; set; }
}

public class NameServiceConsumerComposition
{       
    public NameService NameService { get; set; }
    public NameServiceConsumer NameServiceConsumer { get; set; }
}

[TestFixture]
public class Tests
{
    [Test]
    public void GivenDynamicParamtersConfigurationContainerShouldPassLoggerDownTheTree()
    {
        var container = new WindsorContainer();
        container.AddFacility<LoggingFacility>();
        container.Register(
            Component.For<IService>().ImplementedBy<ServiceWithLogger>().LifestyleTransient(),
            Component.For<ServiceComposition>().DynamicParameters((k, d) =>
            {
                d["Logger"] = k.Resolve<ILogger>().CreateChildLogger(d["name"].ToString());
            }).LifestyleTransient()
            );

        var service = container.Resolve<ServiceComposition>(new { name = "my child" });
        var childLogger = ((ServiceWithLogger) service.Service).Logger;
        Assert.IsTrue(((ConsoleLogger)childLogger).Name.Contains("my child"));
    }

    [Test]
    public void GivenDynamicParamtersConfigurationContainerShouldPassNameDownTheTree()
    {
        var container = new WindsorContainer();
        container.AddFacility<LoggingFacility>();
        container.Register(
            Component.For<NameService>().LifestyleTransient().DependsOn(new {name = "default"}),
            Component.For<NameServiceConsumer>().LifestyleTransient(),
            Component.For<NameServiceConsumerComposition>().DynamicParameters((k, d) =>
            {
                d["nameService"] = k.Resolve<NameService>(d["nameParam"]);
            }).LifestyleTransient()
            );

        var service = container.Resolve<NameServiceConsumerComposition>(new { nameParam = "my child" });
        Console.WriteLine(service.NameServiceConsumer.NameService.Name);
        Assert.IsTrue(service.NameServiceConsumer.NameService.Name.Contains("my child"));
    }
}

但事实并非如此。

最佳答案

解析后不要手动传递您的记录器。让温莎为你做。使用 logging facility .

关于c# - 如何配置温莎以通过依赖树将依赖作为参数传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8500952/

相关文章:

c# - 如何使用 ASP.NET Core 获取当前路由名称?

c# - hibernate : Map all decimals with the same precision and scale

c# - 根据另一个表中的条目计算的 Entity Framework 属性

c# - 如何结束响应并发回 HTTP 代码 404?

.net - 在 .NET 中修复 dll 版本问题的提示

c# - 温莎城堡 : Force resolver to use specified constructor

c# - 使用 EPPlus 返回 INT 的 Excel 日期列

c# - 从不同的类绘制到 Windows 窗体中的图片框

c# - 将 Windsor CaSTLe 注入(inject)的依赖项传递给并行线程 - Dispose() 问题

.net - 温莎城堡 : suppress exceptions thrown by Resolve()