如何使用现有实例选择要在 IoC 容器中创建的类型

发布于 2024-11-16 10:14:36 字数 1568 浏览 4 评论 0原文

这可能只是一个新手问题,但我有以下问题:

public class FooSettings {}
public class BarSettings {}
public class DohSettings {}
// There might be many more settings types...

public interface IProcessor { ... }

public class FooProcessor
    : IProcessor
{
     public FooProcessor(FooSettings) { ... }
}

public class BarProcessor
    : IProcessor
{
     public BarProcessor(BarSettings) { ... }
}

public class DohProcessor
    : IProcessor
{
     public DohProcessor(DohSettings) { ... }
}

// There might be many more processor types with matching settings...

public interface IProcessorConsumer {}

public class ProcessorConsumer 
    : IProcessorConsumer
{
     public ProcessorConsumer(IProcessor processor) { ... }
}

FooSettings 或 BarSettings 的实例是从外部源提供的,即:

object settings = GetSettings();

现在我想根据注入现有的设置实例来解析 ProcessorConsumer 例如:

container.RegisterAssemblyTypes(...); // Or similar
container.Inject(settings);
var consumer = container.Resolve<IProcessorConsumer>();

也就是说,如果提供 FooSettings 的实例,然后创建 FooProcessor 并将其注入到 ProcessorConsumer 中,然后解析该实例。

我一直无法弄清楚如何在 StructureMap、Ninject 或 Autofac 中执行此操作...可能是因为我是 IoC 容器的新手。因此,我们将非常感谢所有这些或其他容器的答案,以便可以对它们进行比较。

更新:我正在寻找一种可以轻松添加新设置和处理器的解决方案。此外,还将存在从设置类型到处理器类型的一对一映射。但它也允许根据其构造函数参数将其他实例/服务注入给定的处理器类型。即某些处理器可能需要 IResourceProvider 服务或类似服务。这里只是一个例子。

理想情况下,我想要类似

container.For<IProcessor>.InjectConstructorParameter(settings)

或相似的东西。从而引导IoC容器使用与注入的构造函数参数实例匹配的处理器类型。

this is probably just a newbie question, but I have the following:

public class FooSettings {}
public class BarSettings {}
public class DohSettings {}
// There might be many more settings types...

public interface IProcessor { ... }

public class FooProcessor
    : IProcessor
{
     public FooProcessor(FooSettings) { ... }
}

public class BarProcessor
    : IProcessor
{
     public BarProcessor(BarSettings) { ... }
}

public class DohProcessor
    : IProcessor
{
     public DohProcessor(DohSettings) { ... }
}

// There might be many more processor types with matching settings...

public interface IProcessorConsumer {}

public class ProcessorConsumer 
    : IProcessorConsumer
{
     public ProcessorConsumer(IProcessor processor) { ... }
}

An instance of either FooSettings or BarSettings is provided from an external source i.e.:

object settings = GetSettings();

And now I would like to resolve ProcessorConsumer based on injecting the existing instance of settings e.g.:

container.RegisterAssemblyTypes(...); // Or similar
container.Inject(settings);
var consumer = container.Resolve<IProcessorConsumer>();

That is if an instance of FooSettings is provided then a FooProcessor is created and injected into the ProcessorConsumer which is then the instance resolved.

I haven't been able to figure out how to do this in either StructureMap, Ninject nor Autofac... probably because I am a newbie when it comes to IoC containers. So answers for all of these or other containers so they can be compared would be highly appreciated.

UPDATE: I am looking for a solution which easily allows for new settings and processors to be added. Also there will be a one-to-on mapping from settings type to processor type. But which also allows for other instances/services to be injected in a given processor type, based on its constructor parameters. I.e. some processor might need a IResourceProvider service or similar. Just an example here.

Ideally, I would like something like

container.For<IProcessor>.InjectConstructorParameter(settings)

or similar. Thereby, guiding the IoC container to use the processor type matching the injected constructor parameter instance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

违心° 2024-11-23 10:14:36

您不需要为此进行依赖注入。您想要一个工厂(当然,您可以使用容器来构建)。工厂会知道如何获取 IProcessorSettings 并返回适当的 IProcessor。简而言之,您可以构建一个工厂,该工厂使用实现 IProcessorSettings 的对象的具体类型和容器来解析适当类型的实例。

You don't want dependency injection for this. You want a factory (which, of course, you can build using your container). The factory would know how to take, say, an IProcessorSettings and return the appropriate IProcessor. In short, you can build a factory that uses the concrete type of an object that implements IProcessorSettings and the container to resolve an instance of the appropriate type.

若能看破又如何 2024-11-23 10:14:36

我认为您正在寻找的是 StructureMap 中的 ForObject() 方法。它可以根据给定的对象实例关闭开放的泛型类型。您需要对设计进行的关键更改是引入泛型类型:

public interface IProcessor { }
public interface IProcessor<TSettings> : IProcessor{}

所有重要的内容仍然在 IProcessor 上声明,泛型 IProcessor 实际上是只是一个标记界面。然后,每个处理器将实现通用接口,以声明它们期望的设置类型:

public class FooProcessor : IProcessor<FooSettings>
{
     public FooProcessor(FooSettings settings) {  }
}

public class BarProcessor : IProcessor<BarSettings>
{
     public BarProcessor(BarSettings settings) {  }
}

public class DohProcessor : IProcessor<DohSettings>
{
     public DohProcessor(DohSettings settings) {  }
}

现在,给定设置对象的实例,您可以检索正确的 IProcessor

IProcessor processor = container.ForObject(settings).
  GetClosedTypeOf(typeof(IProcessor<>)).
  As<IProcessor>();

现在您可以告诉 StructureMap 使用每当解析 IProcessor 时,都会执行此逻辑:

var container = new Container(x =>
{
    x.Scan(scan =>
    {
        scan.TheCallingAssembly();
        scan.WithDefaultConventions();
        scan.ConnectImplementationsToTypesClosing(typeof(IProcessor<>));
    });

    x.For<IProcessor>().Use(context =>
    {
        // Get the settings object somehow - I'll assume an ISettingsSource
        var settings = context.GetInstance<ISettingsSource>().GetSettings();
        // Need access to full container, since context interface does not expose ForObject
        var me = context.GetInstance<IContainer>();
        // Get the correct IProcessor based on the settings object
        return me.ForObject(settings).
            GetClosedTypeOf(typeof (IProcessor<>)).
            As<IProcessor>();
    });

});

I think what you are looking for is the ForObject() method in StructureMap. It can close an open generic type based on a given object instance. The key change you need to make to your design is to introduce the generic type:

public interface IProcessor { }
public interface IProcessor<TSettings> : IProcessor{}

All of the important stuff is still declared on IProcessor, the generic IProcessor<TSettings> is really just a marker interface. Each of your processors will then implement the generic interface, to declare which settings type they expect:

public class FooProcessor : IProcessor<FooSettings>
{
     public FooProcessor(FooSettings settings) {  }
}

public class BarProcessor : IProcessor<BarSettings>
{
     public BarProcessor(BarSettings settings) {  }
}

public class DohProcessor : IProcessor<DohSettings>
{
     public DohProcessor(DohSettings settings) {  }
}

Now, given an instance of a settings object, you can retrieve the correct IProcessor:

IProcessor processor = container.ForObject(settings).
  GetClosedTypeOf(typeof(IProcessor<>)).
  As<IProcessor>();

Now you can tell StructureMap to use this logic whenever it resolves an IProcessor:

var container = new Container(x =>
{
    x.Scan(scan =>
    {
        scan.TheCallingAssembly();
        scan.WithDefaultConventions();
        scan.ConnectImplementationsToTypesClosing(typeof(IProcessor<>));
    });

    x.For<IProcessor>().Use(context =>
    {
        // Get the settings object somehow - I'll assume an ISettingsSource
        var settings = context.GetInstance<ISettingsSource>().GetSettings();
        // Need access to full container, since context interface does not expose ForObject
        var me = context.GetInstance<IContainer>();
        // Get the correct IProcessor based on the settings object
        return me.ForObject(settings).
            GetClosedTypeOf(typeof (IProcessor<>)).
            As<IProcessor>();
    });

});
爱的故事 2024-11-23 10:14:36

StructureMap 容器公开 Model 属性,该属性允许您查询它包含的实例。

var container = new Container(x =>
{
    x.For<IProcessorConsumer>().Use<ProcessorConsumer>();
    x.For<IProcessor>().Use(context =>
    {
        var model = context.GetInstance<IContainer>().Model;
        if (model.PluginTypes.Any(t => typeof(FooSettings).Equals(t.PluginType)))
        {
            return context.GetInstance<FooProcessor>();
        }
        return context.GetInstance<BarProcessor>();
    });
});

StructureMap containers expose the Model property which allows you to query for the instances it contains.

var container = new Container(x =>
{
    x.For<IProcessorConsumer>().Use<ProcessorConsumer>();
    x.For<IProcessor>().Use(context =>
    {
        var model = context.GetInstance<IContainer>().Model;
        if (model.PluginTypes.Any(t => typeof(FooSettings).Equals(t.PluginType)))
        {
            return context.GetInstance<FooProcessor>();
        }
        return context.GetInstance<BarProcessor>();
    });
});
山人契 2024-11-23 10:14:36

在 Autofac 中给出:

public class AcceptsTypeConstructorFinder
    : IConstructorFinder
{
    private readonly Type m_typeToAccept;
    public AcceptsTypeConstructorFinder(Type typeToAccept)
    {
        if (typeToAccept == null) { throw new ArgumentNullException("typeToAccept"); }
        m_typeToAccept = typeToAccept;
    }

    public IEnumerable<ConstructorInfo> FindConstructors(Type targetType)
    {
        return targetType.GetConstructors()
            .Where(constructorInfo => constructorInfo.GetParameters()
                .Select(parameterInfo => parameterInfo.ParameterType)
                .Contains(m_typeToAccept));
    }
}

以下工作:

        // Load
        var settings = new BarSettings();
        var expectedProcessorType = typeof(BarProcessor);

        // Register
        var constructorFinder = new AcceptsTypeConstructorFinder(settings.GetType());
        var builder = new ContainerBuilder();
        var assembly = Assembly.GetExecutingAssembly();

        builder.RegisterInstance(settings);

        builder.RegisterAssemblyTypes(assembly)
               .Where(type => type.IsAssignableTo<IProcessor>() && constructorFinder.FindConstructors(type).Any())
               .As<IProcessor>();

        builder.RegisterAssemblyTypes(assembly)
               .As<IProcessorConsumer>();

        using (var container = builder.Build())
        {
            // Resolve
            var processorConsumer = container.Resolve<IProcessorConsumer>();

            Assert.IsInstanceOfType(processorConsumer, typeof(ProcessorConsumer));
            Assert.IsInstanceOfType(processorConsumer.Processor, expectedProcessorType);

            // Run
            // TODO
        }

但是,我发现这相当麻烦,并且希望在 IoC 容器中内置更多的东西。

In Autofac given:

public class AcceptsTypeConstructorFinder
    : IConstructorFinder
{
    private readonly Type m_typeToAccept;
    public AcceptsTypeConstructorFinder(Type typeToAccept)
    {
        if (typeToAccept == null) { throw new ArgumentNullException("typeToAccept"); }
        m_typeToAccept = typeToAccept;
    }

    public IEnumerable<ConstructorInfo> FindConstructors(Type targetType)
    {
        return targetType.GetConstructors()
            .Where(constructorInfo => constructorInfo.GetParameters()
                .Select(parameterInfo => parameterInfo.ParameterType)
                .Contains(m_typeToAccept));
    }
}

the following works:

        // Load
        var settings = new BarSettings();
        var expectedProcessorType = typeof(BarProcessor);

        // Register
        var constructorFinder = new AcceptsTypeConstructorFinder(settings.GetType());
        var builder = new ContainerBuilder();
        var assembly = Assembly.GetExecutingAssembly();

        builder.RegisterInstance(settings);

        builder.RegisterAssemblyTypes(assembly)
               .Where(type => type.IsAssignableTo<IProcessor>() && constructorFinder.FindConstructors(type).Any())
               .As<IProcessor>();

        builder.RegisterAssemblyTypes(assembly)
               .As<IProcessorConsumer>();

        using (var container = builder.Build())
        {
            // Resolve
            var processorConsumer = container.Resolve<IProcessorConsumer>();

            Assert.IsInstanceOfType(processorConsumer, typeof(ProcessorConsumer));
            Assert.IsInstanceOfType(processorConsumer.Processor, expectedProcessorType);

            // Run
            // TODO
        }

However, I find this to be rather cumbersome and was hoping for something more built into an IoC container.

被翻牌 2024-11-23 10:14:36

现在,我并不是说这是执行此操作的正确方法。但是,如果您使用 Autofac,这可能是另一种选择。这假设您很高兴注册委托在您尝试解析 IProcessorConsumer 时调用 GetSettings。如果您能够做到这一点,您可以在注册中执行您想要的操作,如下所示:

var cb = new ConatainerBuilder();
cb.Register(c => 
{
    var settings = GetSettings();
    if(settings is FooSettings)
        return new FooProcessor((FooSettings)settings);
    else if(settings is BarSettings)
        return new BarProcessor((BarSettings)settings);
    else
       throw new NotImplementedException("Hmmm. Got some new fangled settings.");

}).As<IProcessor>();

//Also need to register IProcessorConsumer

注意:此代码可能是错误的,因为我现在无法尝试。

Now, I'm not saying this is the right way to do this. However, it may be another option if you are using Autofac. This assumes that you are happy for the registration delegate to call GetSettings at the point you try and resolve the IProcessorConsumer. If you are able to do that, you can do what you want in the registration, as below:

var cb = new ConatainerBuilder();
cb.Register(c => 
{
    var settings = GetSettings();
    if(settings is FooSettings)
        return new FooProcessor((FooSettings)settings);
    else if(settings is BarSettings)
        return new BarProcessor((BarSettings)settings);
    else
       throw new NotImplementedException("Hmmm. Got some new fangled settings.");

}).As<IProcessor>();

//Also need to register IProcessorConsumer

Note: This code may be wrong as I can't try it right now.

ゞ花落谁相伴 2024-11-23 10:14:36

这是您可以得到的最接近正确工厂方法的方法。但也存在一些问题。首先,这是代码;然后我们再谈谈。

public class FooSettings
{
    public int FooNumber { get; set; }
    public string FooString { get; set; }
}

public class BarSettings
{
    public int BarNumber { get; set; }
    public string BarString { get; set; }
}


public interface IProcessor
{
    void Process();
}

public class FooProcessor : IProcessor
{
    public FooProcessor(FooSettings settings) { }

    public void Process() { }
}

public class BarProcessor : IProcessor
{
    public BarProcessor(BarSettings settings) { }

    public void Process() { }
}


public interface IProcessorFactory
{
    IProcessor GetProcessor(object settings);
}

public interface IProcessorConsumer { }

public class ProcessorConsumer : IProcessorConsumer
{
    private IProcessorFactory _processorFactory;
    private object _settings;

    public ProcessorConsumer(IProcessorFactory processorFactory, object settings)
    {
        _processorFactory = processorFactory;
        _settings = settings;
    }


    public void MyLogic()
    {
        IProcessor processor = _processorFactory.GetProcessor(_settings);

        processor.Process();
    }
}


public class ExampleProcessorFactory : IProcessorFactory
{
    public IProcessor GetProcessor(object settings)
    {
        IProcessor p = null;

        if (settings is BarSettings)
        {
            p = new BarProcessor(settings as BarSettings);
        }
        else if (settings is FooSettings)
        {
            p = new FooProcessor(settings as FooSettings);
        }

        return p;
    }
}

那么问题出在哪里呢?这是您为工厂方法提供的不同类型的设置。有时是 FooSettings,有时是 BarSettings。稍后,可能是xxxSettings。每个新类型都会强制重新编译。如果您有一个通用的 Settings 类,情况就不会如此。

另一个问题?您的消费者通过工厂和设置并使用它来获得正确的处理器。如果有人将这些传递给消费者,只需让该实体在工厂上调用 GetProcessor 并将生成的 IProcessor 传递给消费者即可。

Here is as close as you can get to a proper factory method. But there are some issues. First, here's the code; then we'll talk.

public class FooSettings
{
    public int FooNumber { get; set; }
    public string FooString { get; set; }
}

public class BarSettings
{
    public int BarNumber { get; set; }
    public string BarString { get; set; }
}


public interface IProcessor
{
    void Process();
}

public class FooProcessor : IProcessor
{
    public FooProcessor(FooSettings settings) { }

    public void Process() { }
}

public class BarProcessor : IProcessor
{
    public BarProcessor(BarSettings settings) { }

    public void Process() { }
}


public interface IProcessorFactory
{
    IProcessor GetProcessor(object settings);
}

public interface IProcessorConsumer { }

public class ProcessorConsumer : IProcessorConsumer
{
    private IProcessorFactory _processorFactory;
    private object _settings;

    public ProcessorConsumer(IProcessorFactory processorFactory, object settings)
    {
        _processorFactory = processorFactory;
        _settings = settings;
    }


    public void MyLogic()
    {
        IProcessor processor = _processorFactory.GetProcessor(_settings);

        processor.Process();
    }
}


public class ExampleProcessorFactory : IProcessorFactory
{
    public IProcessor GetProcessor(object settings)
    {
        IProcessor p = null;

        if (settings is BarSettings)
        {
            p = new BarProcessor(settings as BarSettings);
        }
        else if (settings is FooSettings)
        {
            p = new FooProcessor(settings as FooSettings);
        }

        return p;
    }
}

So what's the issue? It's the different types of settings that you're giving to your factory method. Sometimes it's FooSettings and sometimes it's BarSettings. Later, it might be xxxSettings. Each new type will force a recompilation. If you had a common Settings class, this would not be the case.

The other issue? Your consumer gets passed the factory and the settings and uses that to get the correct processor. If you have someone passing these to your consumer, just have that entity call GetProcessor on the Factory and pass the resulting IProcessor to the consumer.

悍妇囚夫 2024-11-23 10:14:36

我认为问题在于您实际上没有以任何结构化方式指定如何从设置实例解析处理器。让设置对象返回处理器怎么样?这似乎是放置此信息的正确位置:

public interface ISettings
{
   IProcessor GetProcessor();
}

每个实现都必须解析自己的处理器实现:

public class FooSettings : ISettings
{
   //this is where you are linking the settings type to its processor type
   public IProcessor GetProcessor() { return new FooProcessor(this); }
}

并且任何需要处理器的代码都从设置对象获取它,您可以从使用者构造函数引用该对象:

var consumer = new ProcessorConsumer(Settings.GetProcessor());

I think the issue is that you haven't actually specified in any structured way how to resolve a processor from a settings instance. How about making the settings object return the processor? That seems to be the right place to put this information:

public interface ISettings
{
   IProcessor GetProcessor();
}

Each implementation must resolve its own processor implementation:

public class FooSettings : ISettings
{
   //this is where you are linking the settings type to its processor type
   public IProcessor GetProcessor() { return new FooProcessor(this); }
}

And any code needing a processor gets it from the settings object, which you can reference from the consumer constructor:

var consumer = new ProcessorConsumer(Settings.GetProcessor());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文