这个问题更多的是“我该怎么做?”,而不是“我做错了什么?”。我有一个名为 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();
}
我的作品根很合适。我正在使用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 Root的主内核分开。这些子内核将被创建和处理,其生命周期与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 :(得分: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
时使用的模式。