【发布时间】:2014-12-03 15:07:17
【问题描述】:
我正在使用命令处理程序模式并与 ninject.extensions.Conventions 绑定,当我的实际 IQueryHandler 接口实现与单个具体类型匹配时,这非常有效。这是我正在使用的:
kernel.Bind(x => x
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom(typeof(IQueryHandler<,>))
.BindSingleInterface()
.Configure(b => b.WhenInjectedInto(typeof(ValidationHandlerDecorator<,>)).InRequestScope()));
kernel.Bind(typeof(IQueryHandler<,>)).To(typeof(PerformanceHandlerDecorator<,>)).InRequestScope();
但是我遇到了一个场景,我需要在运行时根据自定义路由值覆盖默认的具体类型。以下工作没有问题:
kernel.Bind<IQueryHandler<query1, result1>>().ToMethod(
context => HttpContext.Current.Request.RequestContext.RouteData.Values["type"].ToString().ToLower() == "api"
? (IQueryHandler<query1, result1>)new apiHandler()
: (IQueryHandler<query1, result1>)new defaultHandler()
)
上面的问题是我需要为我的每一个 IQueryHandler 泛型类型编写这段代码。此外,对于我想全局应用的每个装饰器(如顶部示例),我必须修改每个绑定并添加它,将代码加倍或加倍。
我希望完成的是使用以下内容。我已经实现了一个类/接口来返回自定义路由数据值。这会运行,但会引发异常,因为在运行时 HttpContext.Current 为空。我在想,因为它没有在运行时解决每个请求。
kernel.Bind<IMyContext>().To<MyContext>().InRequestScope();
kernel.Bind(x => x
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom(typeof(IQueryHandler<,>))
.StartingWith(kernel.Get<IMyContext>().customRouteValue) // this isn't valid...
.BindSingleInterface()
.Configure(b => b.InRequestScope())
);
有没有办法使用“ToMethod”或工厂/提供者机制来移动匹配运行时特定值的逻辑并根据命名约定返回具体类型?或者有什么其他想法可以做到这一点?
更新:我使用以下模式进行数据库访问:https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92
所以我对我的数据库的每种类型的查询都有一个 IQueryHandler 的实现。
IQueryHandler<GetDocInfo, DocInfo>
IQueryHandler<GetFileInfo, FileInfo>
IQueryHandler<GetOrderInfo, OrderInfo>
IQueryHandler<GetMessageInfo, MessageInfo>
我的确切问题是跨客户端的某些表有不同的架构,因此我必须根据 url 中的路由配置覆盖某些客户端的实现。
public class defaultschemaGetMessageQueryHandler : IQueryHandler<GetMessageInfo, MessageInfo>
public class client1schemaGetMessageQueryHandler : IQueryHandler<GetMessageInfo, MessageInfo>
public class client2schemaGetMessageQueryHandler : IQueryHandler<GetMessageInfo, MessageInfo>
我有兴趣使用它的另一个地方是覆盖特定的查询实现以从不同的数据存储中提取:API 或 NoSQL。
更新 2 最后更新。所以我采用了下面的代码并进行了修改,以从命名方案转移到基于属性,因为我不希望每个 IQueryable 为每个不同的默认类型命名为“QueryHandler”。
改变了这个:
string route = serviceType.Name.Substring(0, indexOfSuffix);
到这里:
string route = System.ComponentModel.TypeDescriptor
.GetAttributes(serviceType)
.OfType<QueryImplementation>()
.Single()
.Id;
并添加了我用来装饰我的 IQueryHandlers 的以下属性
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct)
]
public class QueryImplementation : System.Attribute
{
public string Id { get { return id; } }
private string id;
public QueryImplementation(string id)
{
this.id = id;
}
}
这样使用:
[QueryImplementation("Custom")]
public class CustomDocQueryHandler : IQueryHandler<GetDocInfo, DocInfo>
然后只需要为我的“默认”做同样的事情,以通过属性而不是名称来获取。
【问题讨论】:
-
所有 IQueryHandler 都有多个实现吗?能有多少?总是有默认值吗?仅考虑静态信息,有没有办法区分默认处理程序和特定处理程序? (命名约定?命名空间约定?属性?,...)
-
并非所有人都有多个,但很多人都会。大多数只有 2-3 种不同的实现(SQL DB、NoSQL DB、API)。总会有一个默认值。默认实现的命名约定将与接口的名称匹配。自定义将有一个标准化的前缀。但如果需要,我可以将其移至属性或命名空间约定。但主要问题是尝试根据在运行时解析的每个请求值来解析具体类型。
-
我将为您提供一些示例代码,说明如何做到这一点。但是,您应该注意,它在性能方面“相当”昂贵。如果你没有任何性能问题,它可能会没问题。如果每一点都疼,你可能无论如何都必须切换容器。
标签: c# .net dependency-injection ninject