我认为这属于上下文绑定的概念,但是Ninject文档虽然非常彻底,但没有任何示例与我目前的情况相近。真的很确定我还是很困惑。
我基本上有代表查询参数结构的类。比如..
class CurrentUser {
string Email { get; set; }
}
然后是一个代表其数据库检索的接口(在数据层中)
class CurrentUserQuery : IQueryFor<CurrentUser> {
public CurrentUserQuery(ISession session) {
this.session = session;
}
public Member ExecuteQuery(CurrentUser parameters) {
var member = session.Query<Member>().Where(n => n.Email == CurrentUser.Email);
// validation logic
return member;
}
}
现在,我想要做的是建立一个可以获取给定对象的简单类,从中获取IQueryFor<T>
类,从我的Ninject.IKernel
(构造函数参数)构造它,并且对它执行ExecuteQuery
方法,通过给定的对象。
我能做到这一点的唯一方法就是基本上做到以下几点......
Bind<IQueryFor<CurrentUser>>().To<CurrentUserQuery>();
这解决了一个查询的问题。但我预计会有大量的查询...所以这种方法不仅会变得单调乏味,而且非常容易出现冗余。
我想知道在Ninject中是否存在继承方式以包含这种行为。 : -
最后,我(理想)使用它的方式是......
class HomeController : Controller {
public HomeController(ITransit transit) {
// injection of the transit service
}
public ActionResult CurrentMember() {
var member = transit.Send(new CurrentUser{ Email = User.Identity.Name });
}
}
显然这不会正常工作,因为Send
方法无法知道返回类型。
我一直在广泛地解析Rhino Service Bus
并投射Alexandria
以尝试制作轻巧,轻便的实施方案。
我已经能够使用.NET 4.0 dynamic
对象获得相当理想的结果,例如以下内容......
dynamic Send<T>(object message);
然后宣布我的界面......
public interface IQueryFor<T,K>
{
K Execute(T message);
}
然后使用......
public class TestCurrentMember
{
public string Email { get; set; }
}
public class TestCurrentMemberQuery : IConsumerFor<TestCurrentMember, Member>
{
private readonly ISession session;
public TestCurrentMemberQuery(ISession session) {
this.session = session;
}
public Member Execute(TestCurrentMember user)
{
// query the session for the current member
var member = session.Query<Member>()
.Where(n => n.Email == user.Email).SingleOrDefault();
return member;
}
}
然后在我的控制器中......
var member = Transit.Send<TestCurrentMemberQuery>(
new TestCurrentMember {
Email = User.Identity.Name
}
);
有效地使用<T>
作为我的'嘿,这就是实现查询参数!'。 确实工作,但我觉得很不舒服。这是否适用于.NET 4.0的dynamic
功能?或者这就是为什么它首先存在的原因?
为了保持一致性并保持这篇文章相对于最初的问题,我为动态问题开辟了一个不同的问题。
答案 0 :(得分:3)
是的,你应该能够使用Ninject Conventions处理这个问题。我刚学习Ninject的公约部分,文档很少;但是,Conventions扩展的源代码非常简单,易于阅读/导航,Remo Gloor在这里和邮件列表上都非常有用。
我要尝试的第一件事是GenericBindingGenerator(根据应用程序的需要更改过滤器和范围):
internal class YourModule : NinjectModule
{
public override void Load()
{
Kernel.Scan(a => {
a.From(System.Reflection.Assembly.GetExecutingAssembly());
a.InTransientScope();
a.BindWith(new GenericBindingGenerator(typeof(IQueryFor<>)));
});
}
}
任何BindingGenerator的核心都是这个界面:
public interface IBindingGenerator
{
void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel);
}
默认绑定生成器只检查类的名称是否与接口名称匹配:
public void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel)
{
if (!type.IsInterface && !type.IsAbstract)
{
Type service = type.GetInterface("I" + type.Name, false);
if (service != null)
{
kernel.Bind(service).To(type).InScope(scopeCallback);
}
}
}
GenericBindingGenerator将类型作为构造函数参数,并检查扫描的类上的接口,以查看这些接口的通用定义是否与传递给构造函数的类型匹配:
public GenericBindingGenerator(Type contractType)
{
if (!contractType.IsGenericType && !contractType.ContainsGenericParameters)
{
throw new ArgumentException("The contract must be an open generic type.", "contractType");
}
this._contractType = contractType;
}
public void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel)
{
Type service = this.ResolveClosingInterface(type);
if (service != null)
{
kernel.Bind(service).To(type).InScope(scopeCallback);
}
}
public Type ResolveClosingInterface(Type targetType)
{
if (!targetType.IsInterface && !targetType.IsAbstract)
{
do
{
foreach (Type type in targetType.GetInterfaces())
{
if (type.IsGenericType && (type.GetGenericTypeDefinition() == this._contractType))
{
return type;
}
}
targetType = targetType.BaseType;
}
while (targetType != TypeOfObject);
}
return null;
}
因此,当约定扩展程序扫描类CurrentUserQuery
时,它将看到界面IQueryFor<CurrentUser>
。该接口的通用定义是IQueryFor<>
,因此它将匹配,并且该类型应该为该接口注册。
最后,有一个RegexBindingGenerator
。它尝试将扫描的类的接口与作为构造函数参数给出的Regex
进行匹配。如果您想查看其运行方式的详细信息,您现在应该可以仔细阅读它的源代码。
此外,您应该能够编写您可能需要的IBindingGenerator
的任何实现,因为合同非常简单。