Castle Windsor 3 Interceptor 没有发布由类型化工厂创建的组件,但 2.5.4 发布了。为什么?

发布于 2024-12-27 18:12:36 字数 4014 浏览 4 评论 0原文

这与其他地方所述的模式类似,并详细在此博文中。我使用 Windsor 2.5.4 进行此工作,与博文中所述非常相似,但决定切换到使用 Windsor 3。当我这样做时,我注意到应用程序的内存使用量随着时间的推移而增加 - 我猜这将是组件没有被释放。

博文中的代码进行了一些修改,这可能导致行为有所不同。

这是我的 AutoRelease 拦截器(直接从博客文章中取出,这里是为了方便和懒惰;))

[Transient]
public class AutoReleaseHandlerInterceptor : IInterceptor
{
    private static readonly MethodInfo Execute = typeof(IDocumentHandler).GetMethod("Process");
    private readonly IKernel _kernel;
    public AutoReleaseHandlerInterceptor(IKernel kernel)
    {
        _kernel = kernel;
    }

    public void Intercept(IInvocation invocation)
    {
        if (invocation.Method != Execute)
        {
            invocation.Proceed();
            return;
        }
        try
        {
            invocation.Proceed();
        }
        finally
        {
            _kernel.ReleaseComponent(invocation.Proxy);
        }
    }
}

我与博客文章的偏差之一是类型化工厂使用的选择器:-

public class ProcessorSelector : DefaultTypedFactoryComponentSelector
{
    protected override Func<IKernelInternal, IReleasePolicy, object> BuildFactoryComponent(MethodInfo method,
                                                                                           string componentName,
                                                                                           Type componentType,
                                                                                           IDictionary additionalArguments)
    {
        return new MyDocumentHandlerResolver(componentName,
            componentType,
            additionalArguments,
            FallbackToResolveByTypeIfNameNotFound,
            GetType()).Resolve;
    }
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return null;
    }
    protected override Type GetComponentType(MethodInfo method, object[] arguments)
    {
        var message = arguments[0];
        var handlerType = typeof(IDocumentHandler<>).MakeGenericType(message.GetType());
        return handlerType;
    }
}

可能值得注意的是我不使用默认解析器。 (这也许就是问题所在......)。

public class MyDocumentHandlerResolver : TypedFactoryComponentResolver
{

    public override object Resolve(IKernelInternal kernel, IReleasePolicy scope)
    {
        return kernel.Resolve(componentType, additionalArguments, scope);
    }
}

(为了简洁起见,我省略了 ctor - 那里没有发生什么特别的事情,它只是调用基本 ctor)。

我这样做的原因是因为默认解析器会尝试按名称而不是按类型解析并失败。在这种情况下,我知道我只需要按类型解析,因此我只是覆盖了 Resolve 方法。

最后一块拼图将是安装程序。

container.AddFacility<TypedFactoryFacility>()
         .Register(
          Component.For<AutoReleaseHandlerInterceptor>(),
          Component.For<ProcessorSelector>().ImplementedBy<ProcessorSelector>(),
          Classes.FromAssemblyContaining<MessageHandler>()
                 .BasedOn(typeof(IDocumentHandler<>))
                 .WithService.Base()
                 .Configure(c => c.LifeStyle.Is(LifestyleType.Transient)),
          Component.For<IDocumentHandlerFactory>()
                   .AsFactory(c => c.SelectedWith<ProcessorSelector>()));

单步执行代码,拦截器被调用并执行finally子句(例如,我没有弄错方法名称)。然而,该组件似乎没有被释放(使用性能计数器显示了这一点。每次调用工厂的 create 方法都会使计数器增加 1)。

到目前为止,我的解决方法是向我的工厂接口添加一个 void Release(IDocumentHandler handler) 方法,然后在执行 handler.Process() 方法后,它显式释放处理程序实例,这似乎完成了工作-性能计数器上升,处理完成后下降)。

这是工厂:

public interface IDocumentHandlerFactory
{
    IDocumentHandler GetHandlerForDocument(IDocument document);
    void Release(IDocumentHandler handler);
}

这是我使用它的方式:

IDocumentHandlerFactory handler = _documentHandlerFactory.GetHandlerForDocument(document);
handler.Process();
_documentHandlerFactory.Release(handler);

因此显式执行发布就不需要拦截器,但我真正的问题是为什么这种行为在发布之间有所不同?

This is a similar pattern to ones stated elsewhere and detailed in this blog post. I have this working using Windsor 2.5.4 pretty much as stated in the blogpost, but decided to switch to using Windsor 3. When I did this I noticed that the memory usage of the application go up over time - I guessed this would be that components were'nt being released.

There were a couple of modifications to the code in the blogpost, which may have caused the behaviour to differ.

Here is my AutoRelease interceptor (straight out of the blogpost, here for convenience and the lazy ;) )

[Transient]
public class AutoReleaseHandlerInterceptor : IInterceptor
{
    private static readonly MethodInfo Execute = typeof(IDocumentHandler).GetMethod("Process");
    private readonly IKernel _kernel;
    public AutoReleaseHandlerInterceptor(IKernel kernel)
    {
        _kernel = kernel;
    }

    public void Intercept(IInvocation invocation)
    {
        if (invocation.Method != Execute)
        {
            invocation.Proceed();
            return;
        }
        try
        {
            invocation.Proceed();
        }
        finally
        {
            _kernel.ReleaseComponent(invocation.Proxy);
        }
    }
}

One of my deviations from the blog post is the selector that the typed factory uses:-

public class ProcessorSelector : DefaultTypedFactoryComponentSelector
{
    protected override Func<IKernelInternal, IReleasePolicy, object> BuildFactoryComponent(MethodInfo method,
                                                                                           string componentName,
                                                                                           Type componentType,
                                                                                           IDictionary additionalArguments)
    {
        return new MyDocumentHandlerResolver(componentName,
            componentType,
            additionalArguments,
            FallbackToResolveByTypeIfNameNotFound,
            GetType()).Resolve;
    }
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return null;
    }
    protected override Type GetComponentType(MethodInfo method, object[] arguments)
    {
        var message = arguments[0];
        var handlerType = typeof(IDocumentHandler<>).MakeGenericType(message.GetType());
        return handlerType;
    }
}

What might be noticeable is that I do not use the default resolver. (This is where, perhaps, the problem lies...).

public class MyDocumentHandlerResolver : TypedFactoryComponentResolver
{

    public override object Resolve(IKernelInternal kernel, IReleasePolicy scope)
    {
        return kernel.Resolve(componentType, additionalArguments, scope);
    }
}

(I omitted the ctor for brevity- nothing special happens there, it just calls the base ctor).

The reason I did this was because the default resolver would try to resolve by name and not by Type- and fail. In this case, I know I only ever need to resolve by type, so I just overrode the Resolve method.

The final piece of the puzzle will be the installer.

container.AddFacility<TypedFactoryFacility>()
         .Register(
          Component.For<AutoReleaseHandlerInterceptor>(),
          Component.For<ProcessorSelector>().ImplementedBy<ProcessorSelector>(),
          Classes.FromAssemblyContaining<MessageHandler>()
                 .BasedOn(typeof(IDocumentHandler<>))
                 .WithService.Base()
                 .Configure(c => c.LifeStyle.Is(LifestyleType.Transient)),
          Component.For<IDocumentHandlerFactory>()
                   .AsFactory(c => c.SelectedWith<ProcessorSelector>()));

Stepping through the code, the interceptor is called and the finally clause is executed (e.g. I didn't get the method name wrong). However, the component does not seem to be released (using the performance counter shows this. Each invocation of the factory's create method increases the counter by one).

So far, my workaround has been to add a void Release(IDocumentHandler handler) method to my factory interface, and then after it executes the handler.Process() method, it explicitly releases the handler instance, and this seems to do the job- the performance counter goes up, and as the processing is done, it goes down).

Here is the factory:

public interface IDocumentHandlerFactory
{
    IDocumentHandler GetHandlerForDocument(IDocument document);
    void Release(IDocumentHandler handler);
}

And here is how I use it:

IDocumentHandlerFactory handler = _documentHandlerFactory.GetHandlerForDocument(document);
handler.Process();
_documentHandlerFactory.Release(handler);

Doing the Release explicitly therefore negates the need for the interceptor, but my real question is why this behaviour differs between the releases?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

白鸥掠海 2025-01-03 18:12:36

自我提醒:- RTFM。或者事实上,阅读 Breakingchanges.txt 文件。

这是影响此行为的更改(重点是我的):-

更改 - IReleasePolicy 接口有一个新方法:IReleasePolicy
创建子策略();子策略的使用改变了工厂的类型
处理组件的带外发布(参见描述)

影响 - 中等可修复性 - 简单

描述 - 添加此内容是为了尝试实现更细粒度的
生命周期范围(现在主要针对每个类型的工厂,但在
未来也说 - 客户端应用程序中的每个窗口)。作为其副作用
(以及上述发布策略行为的更改)这不是
更可能释放通过类型化工厂解析的对象,使用
容器.释放
。由于现在仅在范围内跟踪对象
只有致电工厂后,他们才会被释放
发布方法时,或者工厂本身发布时。

修复 - 方法应返回公开相同行为的新对象
作为“父级”,通常最好返回相同的对象
类型(如内置发布策略所做的那样)。

我没有发现修复建议在我的实例中非常有帮助,但是我在问题中的解决方案是你实际应该做什么(使用工厂发布)。如果其他人遇到这个(非)问题,我会保留它。

Note to self:- RTFM. Or in fact, read the Breakingchanges.txt file.

Here's the change that affects this behaviour (emphasis is mine):-

change - IReleasePolicy interface has a new method: IReleasePolicy
CreateSubPolicy(); usage of sub-policies changes how typed factories
handle out-of-band-release of components (see description)

impact - medium fixability - easy

description - This was added as an attempt to enable more fine grained
lifetime scoping (mostly for per-typed-factory right now, but in the
future also say - per-window in client app). As a side-effect of that
(and change to release policy behavior described above) it is no
longer possible to release objects resolved via typed factories, using
container.Release
. As the objects are now tracked only in the scope
of the factory they will be released only if a call to factory
releasing method is made, or when the factory itself is released.

fix - Method should return new object that exposes the same behavior
as the 'parent' usually it is just best to return object of the same
type (as the built-in release policies do).

I didn't find the fix suggestion terribly helpful in my instance, however my solution in the question is what you should actually do (release using the factory). I'll leave it up in case anyone else has this (non) issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文