2015-08-19 102 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