Is there an easy way to register static closures with Castle Windsor?

I experimented using named delegates instead of one-dimensional interfaces. This has some advantages for the size of the code, since we can go over (some of them have been removed so as not to exaggerate the case):

public interface IProductSource { IEnumerable<Product> GetProducts(); } public class DataContextProductSource : IProductSource { private readonly DataContext _DataContext; public DataContextProductSource(DataContext dataContext) { if (dataContext == null) throw new ArgumentNullException("dataContext"); _DataContext = dataContext; } public IEnumerable<Product> GetProducts() { return _DataContext.Products.AsEnumerable(); } } 

in

 public delegate IEnumerable<Product> DGetProducts(); public static class DataContextFunctions { public DGetProducts GetProducts(DataContext dataContext) { if (dataContext == null) throw new ArgumentNullException("dataContext"); return () => dataContext.Products.AsEnumerable(); } } 

This basically takes advantage of the fact that when you go far enough with dependency injection, many classes get a little bigger than closing. These classes can be replaced with functions that return lambda. Entire sets of related functions (which should not encapsulate any kind of mutable state, but would be expressed using classes in the “standard” dependency injection), can then be collapsed into a static class (or “module” in VB).

This is all good and good, but it's hard for me to find a better way to register these static methods with Castle Windsor. Methods without dependencies are easy:

 Component.For<DGetIntegers>().Instance(Integers.GetOneToTen) 

But our DataContextFunctions.GetProducts on top have some dependencies. The best way to find this:

 Component.For<DGetProducts>().UsingFactoryMethod( kernel => DataContextFunctions.GetProducts(kernel.Resolve<DataContext>()) 

This can be quite verbose, and obviously you need to ask the kernel directly for each type of dependency that hits the point a bit. It seems to me that all the static information that the container needs is available, so this should be possible.

The question is, does Windsor Castle (or any other container) have an easy way to do this that I missed, or there are technical problems that arise, or is it too niche to use a use case

+6
source share
3 answers

This is an interesting approach. I think you could easily do this with a special activator.

+4
source

Short answer

You are trying to make a DI container (Castle Windsor) perform the composition function, but it is actually aimed at composing the object. It just gives you a lot of friction. I assume that you will get the same experience with other containers.

DI containers are designed around object-oriented concepts, in particular SOLID. They work great with these principles because they were designed with an innate understanding of things like constructor injection and automatic wiring.

There is nothing wrong with a more functional approach, but I have not yet seen a DI container built around a functional composition rather than an object composition.

Long answer

Using delegates as a general principle for DI tends to be problematic in statically typed languages ​​(at least in .NET) for several reasons. Conceptually, there is nothing wrong with this approach, as a delegate can be seen as an anonymous role interface . However, it tends to become bulky due to the ambiguity of the type.

The most common approach that I usually see is to use BCL built-in delegates like Func<T> , Action<T> , etc. However, you may have many different consumers who rely on Func<string> , in which case things become ambiguous - just because the consumer requires Func<string> does not mean that they need to be delegated in the same role. Although it is mechanically possible to use DI with delegates, what happens is that delegates hide application roles . Roles disappear, leaving only the mechanics.

Then you could, as suggested by the OP, define user delegates for each role, as this delegate suggested:

 public delegate IEnumerable<Product> DGetProducts(); 

However, if you take this route, nothing happens. For each role, a "delegate role" must be defined. Compare this to defining a similar interface, and it should be clear that the only save is a pair of angle brackets and an explicit method definition:

 public interface IProductSource { IEnumerable<Product> GetProducts(); } 

This is not too much overhead (if any).

You can also take a look at this discussion: http://thorstenlorenz.wordpress.com/2011/07/23/dependency-injection-is-dead-long-live-verbs/

+10
source

Based on Krzysztof pointers, I was able to write a custom activator to handle this. The source is on github .

Register static delegates like this (following the example in the question):

 container.Register(Component. For<DGetProducts>(). ImplementedBy(typeof(DataContextFunctions)). Named("GetProducts"). Activator<StaticDelegateActivator>()); 

The code is basically rewritten by DefaultComponentActivator . I also needed to copy-paste DependencyTrackingScope from the Windsor source code, as it is internal.

The only "test" in the Tests project covers only one use case. I do not suspect that it will work for more complex scenarios such as proxies.

I accepted Krzysztof’s answer, as his leadership led to a decision.

+1
source

Source: https://habr.com/ru/post/895482/


All Articles