在我的MVC应用程序中,我定义了两个接口:IQueryMappingsConfigurator
和ICommandMappingsConfigurator
。这两个接口用于为查询和命令上下文提供EntityFramework
映射。
在同一解决方案中,我有两项服务:IMembershipService
和IMessagingService
;对于每个服务,都有Registry
指定ICommandMappingsConfigurator
和IQueryMappingsConfigurator
的实施:
// 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>();
}
}
每项服务都依赖于IQueryMappingsConfigurator
和ICommandMappingsConfigurator
。
IMembershipService
和IMessagingService
:
public class MessageController : Controller
{
public MessageController(IMessagingService service){ }
}
public class MembershipController : Controller
{
public MembershipController(IMembershipService service){}
}
如何配置StructureMap容器,以便在需要IMessagingService
的依赖项时,它会加载ICommandMappingsConfigurator
和IQueryMappingsConfigurator
的正确实现?
我尝试过使用这样的自定义注册约定:
public class ServiceRegistrationConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (IsApplicationService(type))
{
registry.Scan(_ =>
{
_.AssemblyContainingType(type);
_.LookForRegistries();
});
}
}
}
但是,当我尝试从MessageController
访问操作方法时,我收到错误
没有为IMessagingService指定配置
当我调试应用程序时,我可以看到使用Process
类型命中的IMessagingService
方法。
答案 0 :(得分:1)
撰写应用程序的正确方法是制作composition root。如果你确实有一个(你的问题没有清楚),这个步骤每个应用程序启动只执行一次,所以在运行时没有办法改变DI配置的状态(或者至少你应该假设没有)。如果依赖项位于不同的应用程序层中并不重要,如果它们使用相同的接口,它们将重叠。
在更改DI配置之前,您应该检查是否违反了Liskov Substitution Principle。除非您确实需要在应用程序中交换MembershipCommandMappingsConfigurator
和MessagingCommandMappingsConfigurator
,否则一个简单的解决方案就是为每个接口提供不同的接口(在这种情况下为IMembershipCommandMappingsConfigurator
和IMessagingCommandMappingsConfigurator
)。
如果您没有违反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)
{
// ...
}
}
另一个选项 - 在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);
}
}
您也可以使用命名实例,但智能实例具有编译时类型检查支持,这使得它们更易于配置。
除非您的应用程序具有某种插件体系结构,否则没有理由使用.Scan
(使用Reflection)来配置注册表。对于具有多个图层的普通应用程序,您可以显式配置它们。
var container = new Container();
container.Configure(r => r.AddRegistry<MembershipRegistry>());
container.Configure(r => r.AddRegistry<MessagingRegistry>());