如何将Ninject配置为始终停用池化引用?

时间:2013-06-11 15:16:53

标签: c# .net ninject servicestack ninject.web.mvc

我们正在使用一个使用池化对象的库(ServiceStack.Redis的{​​{1}})。创建对象并将其重用于多个Web请求。但是,每次使用后都应将PooledRedisClientManager称为,以将对象释放回池中。

默认情况下,如果对象引用具有not been deactivated before,则Ninject仅停用该对象引用。

实际上,池会实例化一个对象并将其标记为活动状态。然后Ninject运行激活管道。在请求结束时(Web请求),Ninject运行调用Dispose的停用管道(因此池将对象标记为非活动状态)。下一个请求:使用第一个池化实例,池将其标记为活动。但是,在请求结束时,Ninject不会运行其停用管道,因为Dispose已将此实例标记为已停用(this is in the Pipeline)。

以下是我们在新的MVC项目中添加的一个简单示例,用于演示此问题:

ActivationCache

我们迄今为止提出的解决方案是:

  1. 将这些对象标记为public interface IFooFactory { IFooClient GetClient(); void DisposeClient(FooClient client); } public class PooledFooClientFactory : IFooFactory { private readonly List<FooClient> pool = new List<FooClient>(); public IFooClient GetClient() { lock (pool) { var client = pool.SingleOrDefault(c => !c.Active); if (client == null) { client = new FooClient(pool.Count + 1); client.Factory = this; pool.Add(client); } client.Active = true; return client; } } public void DisposeClient(FooClient client) { client.Active = false; } } public interface IFooClient { void Use(); } public class FooClient : IFooClient, IDisposable { internal IFooFactory Factory { get; set; } internal bool Active { get; set; } internal int Id { get; private set; } public FooClient(int id) { this.Id = id; } public void Dispose() { if (Factory != null) { Factory.DisposeClient(this); } } public void Use() { Console.WriteLine("Using..."); } } public class HomeController : Controller { private IFooClient foo; public HomeController(IFooClient foo) { this.foo = foo; } public ActionResult Index() { foo.Use(); return View(); } public ActionResult About() { return View(); } } // In the Ninject configuration (NinjectWebCommon.cs) private static void RegisterServices(IKernel kernel) { kernel.Bind<IFooFactory>() .To<PooledFooClientFactory>() .InSingletonScope(); kernel.Bind<IFooClient>() .ToMethod(ctx => ctx.Kernel.Get<IFooFactory>().GetClient()) .InRequestScope(); } 并使用其他停用机制(如MVC InTransientScope()在每次请求后处理对象)。我们失去了Ninject停用过程的好处,需要间接处理对象。

  2. 编写一个自定义ActionFilter,用于检查池是否处于活动状态。这是我到目前为止所写的内容,但我希望其他人看到它的强大程度:

    IActivationCache
  3. 特别针对public class PooledFooClientActivationCache : DisposableObject, IActivationCache, INinjectComponent, IDisposable, IPruneable { private readonly ActivationCache realCache; public PooledFooClientActivationCache(ICachePruner cachePruner) { realCache = new ActivationCache(cachePruner); } public void AddActivatedInstance(object instance) { realCache.AddActivatedInstance(instance); } public void AddDeactivatedInstance(object instance) { realCache.AddDeactivatedInstance(instance); } public void Clear() { realCache.Clear(); } public bool IsActivated(object instance) { lock (realCache) { var fooClient = instance as FooClient; if (fooClient != null) return fooClient.Active; return realCache.IsActivated(instance); } } public bool IsDeactivated(object instance) { lock (realCache) { var fooClient = instance as FooClient; if (fooClient != null) return !fooClient.Active; return realCache.IsDeactivated(instance); } } public Ninject.INinjectSettings Settings { get { return realCache.Settings; } set { realCache.Settings = value; } } public void Prune() { realCache.Prune(); } } // Wire it up: kernel.Components.RemoveAll<IActivationCache>(); kernel.Components.Add<IActivationCache, PooledFooClientActivationCache>(); :使用ServiceStack.Redis包装器,这样我们总能得到一个新的对象实例。然后让客户端对象从the wrapper takes care of disposing it开始变为瞬态。这种方法没有使用Ninject解决池化对象的更广泛概念,只修复了ServiceStack.Redis。

    PooledRedisClientManager.DisposablePooledClient<RedisClient>
  4. 这些方法中的一种比另一种方法更合适吗?

1 个答案:

答案 0 :(得分:1)

到目前为止我还没有使用Redis,所以我不能告诉你如何正确地做到这一点。但我可以给你一些意见:

Disposing不是ActivationPipeline唯一完成的事情。 (例如,它还执行属性/方法注入和激活激活/取消激活操作。)通过使用返回false的自定义激活缓存,即使它已被激活,也会导致再次执行这些其他操作(例如,再次执行属性注入) 。)