引入工厂扩展和处理内存泄漏

本文关键字:处理 内存 泄漏 扩展 工厂 | 更新日期: 2023-09-27 18:08:46

这个问题更像是一个"我该怎么做? ",而不是"我做错了什么?"。我有一个类,它被称为QueryProcessor处理查询(认为CQRS)。这个物体被注入到我的演示器中。QueryProcessor需要使用内核解析绑定。直接或通过工厂注入内核都很容易。这样做而不引起内存泄漏是关键。

我已经使用内存分析器验证了我的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();
}

我的composition根相当简单。我正在使用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不引起泄漏?

感谢

更新-新问题

我试图通过创建一个新模块的新内核来解决这个问题,与Composition根的主内核分开。这些子内核将被创建和处理,它们的生命周期与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.    

引入工厂扩展和处理内存泄漏

由于创建实例的是应用程序,而不是DI容器,因此它也负责处置实例。这个场景可以通过使用Register, Resolve, and Release模式来处理。

如果注入了内核,那么就有效地实现了服务定位器反模式。这意味着你的应用程序显式地依赖于你的DI框架。

比起注入内核,你应该使用一个抽象工厂来处理处理程序实例的创建和释放。

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

在你的组合根中,你只需要实现handler方法,并将它作为一个参数注册到你的工厂。

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

当然,如果异步处理处理程序,则必须保持实例活动直到请求结束,而不是在handler.Handle()方法返回时立即处理它。如果是这种情况,我建议您分析WebApi的源代码,以找出在处理System.Web.Http.ApiController时使用的模式。