2015-08-19 101 views
0

这个问题是更多的是“我该怎么办?”,而不是“我在做什么错?”。我有一个名为的查询处理器,它处理查询(认为CQRS)。该对象被注入到我的演示者中。 QueryProcessor需要使用内核来解析绑定。直接或通过工厂注入内核很容易。这样做不会导致内存泄漏是诀窍。Ninject工厂扩展和处理内存泄漏

我已经使用内存分析器验证过我的QueryProcessor对象没有被垃圾收集。这个类看起来是这样的:

public sealed class QueryProcessor : IQueryProcessor, IDisposable 
{ 
    private readonly IKernelFactory _container; 
    private bool _disposed; 

    public QueryProcessor(IKernelFactory container) 
    { 
     _container = container; 
    } 

    //[DebuggerStepThrough] 
    public TResult Process<TResult>(IQuery<TResult> query) 
    { 
     var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); 

     dynamic handler = _container.RetrieveKernel().Get(handlerType); 

     return handler.Handle((dynamic)query); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool disposing) 
    { 
     if (disposing && !_disposed) 
     { 
      // dispose of stuff here 
      _disposed = true; 
     }    
    } 
} 

public interface IKernelFactory 
{ 
    IKernel RetrieveKernel(); 
} 

我的作文根是相当简单的。我正在使用Ninject的工厂扩展。

public void OnLoad(IKernel kernel) 
{ 
    // Auto-Register all the validators which are stored in the Service assembly. 
    AssemblyScanner.FindValidatorsInAssembly(_serviceAssembly).ForEach(
      result => kernel.Bind(result.InterfaceType, result.ValidatorType) 
     ); 

    ManualRegistrations(kernel); 

    kernel.Bind<IKernelFactory>().ToFactory(); 

    AutoRegisterType(kernel, typeof(IQueryHandler<,>)); 
    AutoRegisterType(kernel, typeof(ICommandHandler<>)); 
} 

如上所述,注入正在工作,但它留下了内存泄漏。我该如何让Ninject内核在我的QueryProcessor中解析内容而不会导致泄漏?

感谢

更新 - 新问题

我试图通过创建一个新的模块,新的内核,从根组成的主内核单独解决这个问题。这些子内核将被创建并处理,其生命周期与QueryProcessors的生命周期相关联。我把它挂在主模块上:

kernel.Bind<IQueryProcessor>().ToMethod(ctx => new QueryProcessor(new StandardKernel(new ProcessorModule(_serviceAssembly)))).InTransientScope(); 

在内核第一次被处理之前它工作正常。但在那之后,我得到了以下错误消息:

Error loading Ninject component ICache 
No such component has been registered in the kernel's component container. 

Suggestions: 
    1) If you have created a custom subclass for KernelBase, ensure that you have properly 
    implemented the AddComponents() method. 
    2) Ensure that you have not removed the component from the container via a call to RemoveAll(). 
    3) Ensure you have not accidentally created more than one kernel.  

该死如果我这样做,该死如果我不...

+0

你如何绑定QueryProcessor?谁在处理查询处理器? – BatteryBackupUnit

+0

另请注意,如果没有内存压力,对于垃圾收集器*不收集对象*是完全合法的。等待,直到你没有对对象的引用,然后把它当作内存泄漏是*无效*。那么你有什么证据证明实际上存在内存泄漏?由于您使用了内存分析器,因此请向我们展示所有到“QueryProcessor”的GC根目录(应该收集但不是)的路径。 – BatteryBackupUnit

+0

@BatteryBackupUnit QueryProcessor与kernel.bind .To ()绑定,它由Presenters在其各自的Dispose方法中处理。但是,我不能处理内核,因为它在其他地方是需要的。请注意,这不是一个Web项目,所以内核需要比HTTP请求/响应寿命更长。我会尝试从今晚的探查器获取一些数据。但它确实有一个强制GC的按钮,并且该按钮对所有其他第1代对象有效,这些对象在GC之后会从下一个内存快照中消失。使用此配置的 – onefootswill

回答

0

由于您的应用程序,而不是DI容器,是创建实例它也负责处理实例。这种情况可以通过使用Register,Resolve和Release模式来处理。

如果你注入内核,那么你已经有效地实现了service locator anti-pattern。这意味着您的应用程序明确依赖于您的DI框架。

而不是注入内核,您应该使用后DI Friendly Framework中提到的抽象工厂来处理创建和释放处理程序实例。

public interface IHandlerFactory 
{ 
    dynamic Create(Type handlerType); 

    void Release(dynamic handler); 
} 

public interface HandlerFactory 
{ 
    private readonly Func<Type, dynamic> handlerMethod; 

    public HandlerFactory(Func<Type, dynamic> handlerMethod) 
    { 
     if (handlerMethod == null) 
      throw new ArgumentNullException("handlerMethod"); 

     this.handlerMethod = handlerMethod; 
    } 

    public dynamic Create(Type handlerType) 
    { 
     return handlerMethod(handlerType); 
    } 

    public void Release(dynamic handler) 
    { 
     IDisposable disposable = handler as IDisposable; 
     if (disposable != null) 
     { 
      disposable.Dispose(); 
     } 
    } 
} 

使用

public sealed class QueryProcessor : IQueryProcessor 
{ 
    private readonly IHandlerFactory handlerFactory; 

    public QueryProcessor(IHandlerFactory handlerFactory) 
    { 
     if (handlerFactory == null) 
      throw new ArgumentNullException("handlerFactory"); 

     this.handlerFactory = handlerFactory; 
    } 

    //[DebuggerStepThrough] 
    public TResult Process<TResult>(IQuery<TResult> query) 
    { 
     var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); 

     dynamic handler = this.handlerFactory.Create(handlerType); 
     try 
     { 
      return handler.Handle((dynamic)query); 
     } 
     finally 
     { 
      this.handlerFactory.Release(handler); 
     } 
    } 
} 

请注意,如果您使用这种方法,你不需要每次处理程序来实现IDisposable,也没有你的QueryProcessor不必要需要实现IDisposable

在您的作文根中,您只需实现处理程序方法并将其注册为工厂的参数。

Func<Type, dynamic> handlerMethod = type => (dynamic)kernel.Resolve(type); 
kernel.Bind<IHandlerFactory>().To<HandlerFactory>() 
    .WithConstructorArgument("handlerMethod", handlerMethod); 

当然,如果你正在处理您的异步处理程序,你必须保持实例的生命,直到请求结束,而不是只要handler.Handle()方法返回处置它。如果是这样的话,我建议你分析一下source code for WebApi,找出在处理System.Web.Http.ApiController时用什么模式。

+0

我应该提到,这是一个Winforms应用程序,而不是网络。所以,生活时间有点棘手。我正在使用工厂扩展,您可以在我的代码中看到。我不知道如何在不杀死内核的情况下关闭QueryProcessor。我得到我可以给QueryProcessor一个单身生命。但这可能不是一个ThreadSafe选项。 – onefootswill