patterncsharpMinor
Helper extension to release Windsor component; not sure if it's over-kill
Viewed 0 times
killhelperextensionsurereleasewindsorcomponentnotover
Problem
One of the tenets of Windsor IoC (probably applies to all IoC containers too) is to "release what you explicitly resolve", which admittedly should occur rarely. But we have a fair few
However, remembering to call
```
namespace Castle.Windsor {
using System;
using Castle.MicroKernel;
public static class KernelExtensions {
public static ResolvedResult ResolveDispose(this IKernel kernel) {
return new ResolvedKernelResult(kernel);
}
public static ResolvedResult ResolveDispose(this IWindsorContainer container) {
return new ResolvedContainerResult(container);
}
public abstract class ResolvedResult : IDisposable {
public T Instance { get; private set; }
protected ResolvedResult(Func resolver, Action releaser) {
this.Instance = resolver();
this._Releaser = releaser;
}
#region IDisposable Members
public void Dispose() {
if (null == _Releaser) {
throw new ObjectDisposedException(this.GetType().Name);
}
//Dis-associate the resolved instance, can't null assign as T isn't typed
Instance = default(T);
_Releaser(Instance);
_Releaser = null;
}
#endregion
private Action _Releaser;
public static implicit operator T(ResolvedResult res) {
return res.Instance;
}
}
private sealed class ResolvedKernelResult : Re
UsingFactoryMethod setups in our installers, which are resolving directly (which appears to be a valid case for explicit resolution).However, remembering to call
Dispose() on those resolved things is something I doubt a lot of people will remember, so I came up with a helper method in order to be able to wrap that logic away (always a good thing, right?). However, I'm not sure if it's just a bit overkill in this case.```
namespace Castle.Windsor {
using System;
using Castle.MicroKernel;
public static class KernelExtensions {
public static ResolvedResult ResolveDispose(this IKernel kernel) {
return new ResolvedKernelResult(kernel);
}
public static ResolvedResult ResolveDispose(this IWindsorContainer container) {
return new ResolvedContainerResult(container);
}
public abstract class ResolvedResult : IDisposable {
public T Instance { get; private set; }
protected ResolvedResult(Func resolver, Action releaser) {
this.Instance = resolver();
this._Releaser = releaser;
}
#region IDisposable Members
public void Dispose() {
if (null == _Releaser) {
throw new ObjectDisposedException(this.GetType().Name);
}
//Dis-associate the resolved instance, can't null assign as T isn't typed
Instance = default(T);
_Releaser(Instance);
_Releaser = null;
}
#endregion
private Action _Releaser;
public static implicit operator T(ResolvedResult res) {
return res.Instance;
}
}
private sealed class ResolvedKernelResult : Re
Solution
Style-wise everything looks good. The only note I'd make is that in C#, generally the convention is to start brackets on a new line rather than in-line, although obviously this is ultimately up to personal or team preference. XML comments on public members may be a good idea too, as it's not trivially obvious what the purpose of this class is unless you're very familiar with the container.
As to the actual usefulness of the helper, though, I'm doubtful:
One alternative would be instead of providing a helper method to use inside
A bit of a mouthful, but then using it would look like:
This reduces the amount of code to write a bit more, and it helps remind people to use it by being on their intellisense. I'd say it's somewhat a matter of judgement whether this is more or less readable than the helper method version. It is also less flexible if, for example, you need multiple different explicitly resolved resources inside a single factory method. But, as far as I can think, this kind of approach is likely to be the only fruitful way to achieve what you want.
UPDATE
Another alternative using the decorator pattern, which allows you to deal with multiple explictly resolved services by decorating the kernel:
Here
This doesn't actually have to be a decorator, you could create your own class which only has a subset of the interface of
So using this, your example would look like:
Definitely shorter and more readable. In terms of people remembering to use it, it will appear on the intellisense, but with a different name to
As to the actual usefulness of the helper, though, I'm doubtful:
- It doesn't significantly reduce the amount of code you have to write
- It is just as reliant on people remembering to use it
- It does not improve readability, and if anything, slightly degrades it.
One alternative would be instead of providing a helper method to use inside
UsingFactoryMethod, you provide an alternative version of that method. You'd probably need to tweak it, but a rough version might look like:public static ComponentRegistration UsingFactoryMethod(
this ComponentRegistration component, Func resourceResolver,
Func factoryMethod)
where TService : class
where TImpl : TService
{
return component.UsingFactoryMethod(
(kernel) =>
{
var resource = resourceResolver(kernel);
var result = factoryMethod(kernel, resource);
kernel.ReleaseComponent(resource);
return result;
});
}A bit of a mouthful, but then using it would look like:
Component.For()
.UsingFactoryMethod(
k => k.Resolve(),
(k, alt) => (new ImplementingComponentType(alt.RandomProperty)));This reduces the amount of code to write a bit more, and it helps remind people to use it by being on their intellisense. I'd say it's somewhat a matter of judgement whether this is more or less readable than the helper method version. It is also less flexible if, for example, you need multiple different explicitly resolved resources inside a single factory method. But, as far as I can think, this kind of approach is likely to be the only fruitful way to achieve what you want.
UPDATE
Another alternative using the decorator pattern, which allows you to deal with multiple explictly resolved services by decorating the kernel:
public static ComponentRegistration UsingReleasingFactoryMethod(
this ComponentRegistration component, Func factoryMethod)
where TService : class
where TImpl : TService
{
Converter safeMethod = (kernel) =>
{
var releasingKernel = new ReleasingKernel(kernel);
var result = factoryMethod(kernel);
releasingKernel.ReleaseAll();
return result;
};
return component.UsingFactoryMethod(safeMethod);
}Here
ReleasingKernel is a decorator for Kernel which keeps track of all services resolved, and otherwise passes all calls through to the kernel. It also has a method which I'm calling ReleaseAll which calls to the contained kernel to release all services it has recorded.This doesn't actually have to be a decorator, you could create your own class which only has a subset of the interface of
IKernel available, if not all methods are needed.So using this, your example would look like:
Component.For()
.UsingReleasingFactoryMethod(k => {
var altComponent = k.Resolve();
return new ImplementingComponentType(altComponent.RandomProperty);
})Definitely shorter and more readable. In terms of people remembering to use it, it will appear on the intellisense, but with a different name to
UsingFactoryMethod.Code Snippets
public static ComponentRegistration<TService> UsingFactoryMethod<TService, TResource, TImpl>(
this ComponentRegistration<TService> component, Func<IKernel, TResource> resourceResolver,
Func<IKernel, TResource, TImpl> factoryMethod)
where TService : class
where TImpl : TService
{
return component.UsingFactoryMethod(
(kernel) =>
{
var resource = resourceResolver(kernel);
var result = factoryMethod(kernel, resource);
kernel.ReleaseComponent(resource);
return result;
});
}Component.For<MyComponentType>()
.UsingFactoryMethod(
k => k.Resolve<SomeAlternativeComponent>(),
(k, alt) => (new ImplementingComponentType(alt.RandomProperty)));public static ComponentRegistration<TService> UsingReleasingFactoryMethod<TService, TImpl>(
this ComponentRegistration<TService> component, Func<IKernel, TImpl> factoryMethod)
where TService : class
where TImpl : TService
{
Converter<IKernel, TImpl> safeMethod = (kernel) =>
{
var releasingKernel = new ReleasingKernel(kernel);
var result = factoryMethod(kernel);
releasingKernel.ReleaseAll();
return result;
};
return component.UsingFactoryMethod(safeMethod);
}Component.For<MyComponentType>()
.UsingReleasingFactoryMethod(k => {
var altComponent = k.Resolve<SomeAlternativeComponent>();
return new ImplementingComponentType(altComponent.RandomProperty);
})Context
StackExchange Code Review Q#57378, answer score: 3
Revisions (0)
No revisions yet.