简体   繁体   English

基于已解析的类型,StructureMap在运行时覆盖依赖项

[英]StructureMap override dependencies at runtime based on resolved type

In my MVC application I defined two interfaces: IQueryMappingsConfigurator and ICommandMappingsConfigurator . 在我的MVC应用程序中,我定义了两个接口: IQueryMappingsConfiguratorICommandMappingsConfigurator Those two interfaces are used for supplying EntityFramework mappings for query and command contexts. 这两个接口用于为查询和命令上下文提供EntityFramework映射。

In the same solution I have two services: IMembershipService and IMessagingService ; 在同一个解决方案中,我有两个服务: IMembershipServiceIMessagingService for each of those services there is a Registry specifying implementations for ICommandMappingsConfigurator and IQueryMappingsConfigurator : 对于每个这些服务,都有一个Registry指定ICommandMappingsConfiguratorIQueryMappingsConfigurator

// In Services.Membership project
public class MembershipRegistry : Registry
{
    public MembershipRegistry()
    {
        For<ICommandMappingsConfigurator>()
            .Use<MembershipCommandMappingsConfigurator>();

        For<IQueryMappingsConfigurator>()
            .Use<MembershipQueryMappingsConfigurator>();

        For<IMembershipService>()
            .Use<MembershipService>();
    }
}

// In Services.Messaging project
public class MessagingRegistry : Registry
{
    public MessagingRegistry()
    {
        For<ICommandMappingsConfigurator>()
            .Use<MessagingCommandMappingsConfigurator>();

        For<IQueryMappingsConfigurator>()
            .Use<MessagingQueryMappingsConfigurator>();

        For<IMessagingService>()
            .Use<MessagingService>();
    }
}

Each service has a dependency on both IQueryMappingsConfigurator and ICommandMappingsConfigurator . 每个服务都依赖于IQueryMappingsConfiguratorICommandMappingsConfigurator

IMembershipService and IMessagingService are used by controllers in the MVC project: IMembershipServiceIMessagingService由MVC项目中的控制器使用:

public class MessageController : Controller
{
    public MessageController(IMessagingService service){ }
}

public class MembershipController : Controller
{
    public MembershipController(IMembershipService service){}
}

How can I configure the StructureMap container so that when a dependency for IMessagingService is required it will load the proper implementation for ICommandMappingsConfigurator and IQueryMappingsConfigurator ? 如何配置StructureMap容器​​,以便在需要IMessagingService依赖IMessagingService时,它将为ICommandMappingsConfiguratorIQueryMappingsConfigurator加载正确的实现?

I've tried using a custom registration convention like this: 我试过使用这样的自定义注册约定:

public class ServiceRegistrationConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (IsApplicationService(type))
        {
            registry.Scan(_ =>
            {
                _.AssemblyContainingType(type);
                _.LookForRegistries();
            });
        }
    }
}

However, when I try accessing an action method from MessageController I get the error 但是,当我尝试从MessageController访问操作方法时,出现错误

There is no configuration specified for IMessagingService 没有为IMessagingService指定配置

When I debug the application I can see the Process method being hit with the type of IMessagingService . 当我调试应用程序时,我可以看到Process方法被IMessagingService类型IMessagingService

The correct way to compose your application is to make a composition root . 编写应用程序的正确方法是使合成成为根 If you indeed have one (which isn't clear from your question), this step is done only 1 time per application start so there is no way to change the DI configuration's state during runtime (or at least you should assume there is not). 如果您确实有一个(这个问题尚不清楚),则此步骤在每个应用程序启动时仅执行1次,因此无法在运行时更改DI配置的状态(或者至少应该假定没有) 。 It doesn't matter if the dependencies are in different application layers, they will overlap if they use the same interface. 依赖关系是否位于不同的应用程序层中无关紧要,如果它们使用相同的接口,则它们将重叠。

Before you change your DI configuration, you should check whether you are violating the Liskov Substitution Principle . 在更改DI配置之前,应检查是否违反了Liskov替代原则 Unless you really need the ability to swap the MembershipCommandMappingsConfigurator and MessagingCommandMappingsConfigurator in your application, a simple solution is just to give each a different interface (in this case IMembershipCommandMappingsConfigurator and IMessagingCommandMappingsConfigurator ). 除非您真的需要在应用程序中交换MembershipCommandMappingsConfiguratorMessagingCommandMappingsConfigurator的能力,否则一个简单的解决方案就是给每个接口一个不同的接口(在本例中为IMembershipCommandMappingsConfiguratorIMessagingCommandMappingsConfigurator )。

If you are not violating the LSP, one option is to use generics to disambiguate the dependency chain. 如果您没有违反LSP,一种选择是使用泛型来消除依赖关系链的歧义。

public class MyRegistry : Registry
{
    public MyRegistry()
    {
        For(typeof(ICommandMappingsConfigurator<>))
            .Use(typeof(CommandMappingsConfigurator<>));

        For(typeof(IQueryMappingsConfigurator<>)
            .Use(typeof(QueryMappingsConfigurator<>));

        For<IMessagingService>()
            .Use<MessagingService>();

        For<IMembershipService>()
            .Use<MembershipService>();
    }
}

public class CommandMappingsConfigurator<MessagingService> : ICommandMappingsConfigurator<MessagingService>
{
    // ...
}

public class QueryMappingsConfigurator<MessagingService> : IQueryMappingsConfigurator<MessagingService>
{
    // ...
}

public class MessagingService
{
    public MessagingService(
        ICommandMappingsConfigurator<MessagingService> commandMappingsConfigurator,
        IQueryMappingsConfigurator<MessagingService> queryMappingsConfigurator)
    {
        // ...
    }
}

public class CommandMappingsConfigurator<MembershipService> : ICommandMappingsConfigurator<MembershipService>
{
    // ...
}

public class QueryMappingsConfigurator<MembershipService> : IQueryMappingsConfigurator<MembershipService>
{
    // ...
}

public class MembershipService
{
    public MembershipService(
        ICommandMappingsConfigurator<MembershipService> commandMappingsConfigurator,
        IQueryMappingsConfigurator<MembershipService> queryMappingsConfigurator)
    {
        // ...
    }
}

Another option - in StructureMap you can used smart instances in the configuration to specify exactly what instance goes where, so at runtime you can have different implementations of the same interface. 另一个选择-在StructureMap中,您可以在配置中使用智能实例来确切指定实例在哪里运行,因此在运行时,您可以对同一接口进行不同的实现。

public class MembershipRegistry : Registry
{
    public MembershipRegistry()
    {
        var commandMappingsConfigurator = For<ICommandMappingsConfigurator>()
            .Use<MembershipCommandMappingsConfigurator>();
        var queryMappingsConfigurator = For<IQueryMappingsConfigurator>()
            .Use<MembershipQueryMappingsConfigurator>();
        For<IMembershipService>()
            .Use<MembershipService>()
            .Ctor<ICommandMappingsConfigurator>().Is(commandMappingsConfigurator)
            .Ctor<IQueryMappingsConfigurator>().Is(queryMappingsConfigurator);
    }
}

public class MessagingRegistry : Registry
{
    public MessagingRegistry()
    {
        var commandMappingsConfigurator = For<ICommandMappingsConfigurator>()
            .Use<MessagingCommandMappingsConfigurator>();

        var queryMappingsConfigurator = For<IQueryMappingsConfigurator>()
            .Use<MessagingQueryMappingsConfigurator>();

        For<IMessagingService>()
            .Use<MessagingService>();
            .Ctor<ICommandMappingsConfigurator>().Is(commandMappingsConfigurator)
            .Ctor<IQueryMappingsConfigurator>().Is(queryMappingsConfigurator);
    }
}

You can also use named instances , but smart instances have compile-time type checking support which makes them easier to configure. 您也可以使用命名实例 ,但是智能实例具有编译时类型检查支持,这使它们更易于配置。

There is no reason to use .Scan (which uses Reflection) to configure the registries, unless your application has some kind of plugin architecture. 除非您的应用程序具有某种插件体系结构,否则没有理由使用.Scan (使用Reflection)来配置注册表。 For a normal application with multiple layers, you can configure them explicitly. 对于具有多层的普通应用程序,可以显式配置它们。

var container = new Container();

container.Configure(r => r.AddRegistry<MembershipRegistry>());
container.Configure(r => r.AddRegistry<MessagingRegistry>());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM