2016-02-17 18 views
0

我有一个抽象工厂,它创建一些服务,用IService接口表示。在工厂中,我有两种方法,因为在其中一个方法中,我允许消费者传递现有的IServiceLogger实例以供构造的服务树使用。将依赖项实例传递给工厂方法参数以使Ninject在分辨率范围内使用它

public interface IMyServiceFactory { 
    IMyService Create(IServiceLogger loggerInstance); 
    IMyService Create(); 
} 

因为一个IServiceLogger应该在服务树之间共享,我用的是InCallScope绑定到具体实施的时候。

我该如何使用Ninject实现这个场景?我尝试了以下方法。

1.手动创建一个工厂实现

internal class MyServiceFactory : IMyServiceFactory { 

    private IResolutionRoot _kernel; 

    public MyServiceFactory 

    public IMyService Create(IServiceLogger loggerInstance) { 
    // what should go here? how can I pass the existing instance to Ninject Get method and make Ninject to use it for the whole resolution tree, just as it were created by Ninject and used as InCallScope? 
    } 

    // this one is trivial... 
    pulbic IMyService Create() { 
    return _kernel.Get<IMyService>(); 
    } 
} 

UPDATE

其实我已经找到了这样的混乱和不太安全的方式。我可以通过GetBindings,然后RebindIServiceLoggerToConstant,然后Get,IMyService实例获得当前绑定,最后用AddBinding恢复原始绑定。我不喜欢它,它感觉很臭,更糟糕的是,它不是线程安全的,因为另一个线程可以在此代码中请求IMyService,因此使用本地临时绑定。

2.使用Ninject.Extensions.Factory

只需使用ToFactory约束力,但是这不工作,因为它只是尝试使用参数作为一个简单的构造函数的参数(如适用),而不是作为整个分辨率树的一个对象。

回答

0

重要更新

虽然我原来的答案给了我一个工作的解决方案,通过一个偶然的智能感知导航我只是发现有整整此问题的内置工具。我只需要使用内置的TypeMatchingArgumentInheritanceInstanceProvider就可以做到这一点,甚至更多,因为参数类型匹配不再需要命名约定。

这将是一个很好的关于这些选项的更详细的文档,或者只是我目前找不到它。


原来的答案

我尝试了一些方法,并结束了一个稍微不同的,一种利用Ninject的环境参数遗产公约为基础的方法。

约定用于通过依赖关系树命名的构造函数参数。例如,无论何时将一个IServiceLogger实例注入到服务类中,该参数应该被称为serviceLogger

考虑到上述约定,我测试了以下方法。首先,我为工厂扩展实现了一个自定义实例提供程序。此自定义提供程序将覆盖为上下文创建构造函数参数的机制,以便让开发人员指定应设置为继承的多个命名参数。这样,在获取操作期间,具有指定名称的所有参数将继承整个请求图。

public class ParameterInheritingInstanceProvider : StandardInstanceProvider 
{ 
    private readonly List<string> _parametersToInherit = new List<string>(); 

    public ParameterInheritingInstanceProvider(params string[] parametersToInherit) 
    { 
     _parametersToInherit.AddRange(parametersToInherit); 
    } 

    protected override IConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments) 
    { 
     var parameters = methodInfo.GetParameters(); 
     var constructorArgumentArray = new IConstructorArgument[parameters.Length]; 
     for (var i = 0; i < parameters.Length; ++i) 
      constructorArgumentArray[i] = new ConstructorArgument(parameters[i].Name, arguments[i], _parametersToInherit.Contains(parameters[i].Name)); 
     return constructorArgumentArray; 
    } 
} 

然后,在绑定配置后,我只是用相应的参数名称抛出它。

kernel.Bind<IMyServiceFactory>().ToFactory(() => new ParameterInheritingInstanceProvider("serviceLogger")); 

最后我回顾参数命名,并在所示例的工厂接口改变loggerInstanceserviceLogger公约相匹配。

该解决方案仍然不是最好的解决方案,因为它有几个限制。

  1. 这很容易出错。可以通过不遵守命名约定来制作难以跟踪的错误,因为如果约定不匹配,它现在会默默地失败。这可能会得到改善,我会稍后再考虑。
  2. 它只处理构造函数注入,但这不应该是一个大问题,因为这是建议的技术。例如,我几乎从不做其他类型的注射。
0

我会对Ninject的内核给予更多的控制,并且根本不会为工厂创建类。 并使用Func键在Ninject结合这样的:

Bind<Func<IMyService>>().ToMethod(s => CreateService); 

通过ILoggerService结合或不结合这一点,你可以控制好集中你是否有在你的服务记录或没有。(以尽量只把它注释掉)

这里实现引导程序的:

internal class MyStuff 
    { 
     private readonly Func<IMyService> _myServiceFactory; 

     public MyStuff(Func<IMyService> myServiceFactory) 
     { 
      _myServiceFactory = myServiceFactory; 

      _myServiceFactory.Invoke(); 
     } 
    } 

简单实现的MyService的:

internal class MyService 
     :IMyService 
    { 
     public MyService() 
     { 
      Console.WriteLine("with no parameters"); 
     } 

     public MyService(IServiceLogger logger) 
     { 
      Console.WriteLine("with logger parameters"); 
     } 
    } 

public class Bootstrapper 
    { 
     private IKernel _kernel = new StandardKernel(); 

     public Bootstrapper() 
     { 
      _kernel.Bind<MyStuff>().ToSelf(); 
      _kernel.Bind<IServiceLogger>().To<ServiceLogger>(); 
      _kernel.Bind<IMyService>().To<MyService>(); 

      _kernel.Bind<Func<IMyService>>().ToMethod(s => CreateService); 
     } 

     public IKernel Kernel 
     { 
      get 
      { 
       return _kernel; 
      } 
      set 
      { 
       _kernel = value; 
      } 
     } 

     private IMyService CreateService() 
     { 


      if(_kernel.GetBindings(typeof(IServiceLogger)).Any()) 
      { 
       return _kernel.Get<IMyService>(new ConstructorArgument("logger", _kernel.Get<IServiceLogger>())); 
      } 

      return _kernel.Get<IMyService>(); 
     } 
    } 

消费类工厂的实现

简单ServiceLogger

internal class ServiceLogger 
     :IServiceLogger 
    { 
     public ServiceLogger() 
     { 

     } 
    } 

    internal interface IServiceLogger 
    { 
    } 
+0

谢谢你的回答。你所描述的非常简单,甚至不需要IMO代码,因为就简单的一级构造函数而言,Factory扩展可以完成这项工作(正如我在文章中提到的那样)。当1:记录器MIGHT可选时,问题出现了2.记录器应该被完整的服务树使用,而不仅仅是在顶层。然而,我发现了一个解决方案,我很快就会发布。 –

+0

哦,好的。然后我会推荐使用条件绑定,这在ninject中是相当强大的。 –

相关问题