question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Delegate factory with Dependency.OnComponent and TypedFactoryFacility

See original GitHub issue

Hello, i have been doing some refactoring of our container mess and it seems i found something weird. When using delegate factory and Dependecny.OnComponent at once the factory gets wrong component resolved, i boiled it down to this code:

public interface IInterface {}
public class C1 : IInterface {}
public class C2 : IInterface {}
public  class Root
{
  private IInterface _I;
  public Root(Func<IInterface> factory)
    {
	  //here i expect to get instance of C2 but get C1
      _I = factory();
    }
}

public void Main()
{
  _container = new WindsorContainer();
  _container.AddFacility<TypedFactoryFacility>();
  
  _container.Register(
  	Component.For<IInterface>().ImplementedBy<C1>(),
  	Component.For<IInterface>().ImplementedBy<C2>(),
  	Component.For<Root>().DependsOn(Dependency.OnComponent<IInterface, C2>())
  );
  
  var t = _container.Resolve<Root>();
}

I used a workaround with DependencyOnValue and created the delegate factory myself, but thats ugly:

Component.For<Root>().DependsOn(Dependency.OnValue<Func<IInterface>>((Func<IInterface>)(_ => new C2())

So am i doing something wrong ? Or is it a bug ? Or maybe its by design ? Is there a better way than the Dependency.OnValue ? Note: this example is verry simplified and sadly refactoring the classes is out of the question. Note2: the delgate (Func<IInterface>) in my case takes arguments, i just removed them because they are not neccesary for the example

version 4.0.30319 (i know its not the newest, but i work with what i have and if neccesary i can upgrade )

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:21

github_iconTop GitHub Comments

1reaction
ghostcommented, Jun 30, 2018

I still think the whole problem starts here:

Component.For<IInterface>().ImplementedBy<C1>(), //ordering is important
Component.For<IInterface>().ImplementedBy<C2>(), //ordering is important

According to the docs here:

In Windsor first one wins: In Castle, the default implementation for a service is the first registered implementation. This is different from AutoFac for example, where the default is the last registered implementation (http://code.google.com/p/autofac/wiki/ComponentCreation).

It is just a recommendation of mine that the code above throws an exception because the container is arbitrarily picking the first component registration(which is not necessarily what the user always means, and we really want clean code. Why have registrations that you probably don’t need because they are ignored?). This is default behaviour which as you correctly identified is not making it’s way through to invocations from the TypedFactoryInterceptor when you use DependendsOn. I would however expect the following registrations would not throw an exception:

Component.For<IInterface>().ImplementedBy<C1>(), 
Component.For<IInterface>().ImplementedBy<C2>().IsDefault(), 
Component.For<IInterface>().ImplementedBy<C1>().Named("first"), 
Component.For<IInterface>().ImplementedBy<C2>(), 
Component.For<IInterface>().ImplementedBy<C1>().IsFallback(),
Component.For<IInterface>().ImplementedBy<C2>(), 

What Worked

With the above in mind, I played around with a few scenario’s to see what does work.

Re-ordering worked:

[Test]
public void Should_pick_correct_registration()
{
	var _container = new WindsorContainer();

	_container.Register(
		Component.For<IInterface>().ImplementedBy<C2>(), // First passed the post, wins
		Component.For<IInterface>().ImplementedBy<C1>(),
		Component.For<Root>()
	);

	var t = _container.Resolve<Root>();
}

Fallbacks worked:

[Test]
public void Should_pick_correct_registration()
{
	var _container = new WindsorContainer();

	_container.Register(
		Component.For<IInterface>().ImplementedBy<C1>().IsFallback(),
		Component.For<IInterface>().ImplementedBy<C2>(),
		Component.For<Root>()
	);

	var t = _container.Resolve<Root>();
}

Using factory method with named registrations worked:

[Test]
public void Should_pick_correct_registration()
{
	var _container = new WindsorContainer();

	_container.Register(
		Component.For<IInterface>().ImplementedBy<C1>(),
		Component.For<IInterface>().ImplementedBy<C2>().Named("foo"),
		Component.For<Root>().UsingFactoryMethod(() => new Root(() => _container.Resolve<IInterface>("foo")))
	);

	var t = _container.Resolve<Root>();
}

Explicit factory registration with default worked:

[Test]
public void Should_pick_correct_registration()
{
	var _container = new WindsorContainer();

	_container.Register(
		Component.For<IInterface>().ImplementedBy<C1>(),
		Component.For<IInterface>().ImplementedBy<C2>().IsDefault(),
		Component.For<Func<IInterface>>().Instance(() => _container.Resolve<IInterface>()),
		Component.For<Root>()
	);

	var t = _container.Resolve<Root>();
}

Conclusion

I just question the time invested in fixing this API. There are already so many good ways you could do this. I am leaning towards deprecating the API altogether in favour of a slightly more explicit registration model. My two cents. Feel free to tell me what you think.

1reaction
ghostcommented, Jun 30, 2018
Component.For<IInterface>().ImplementedBy<C2>(),

Should throw an exception to make it more deterministic unless we are dealing with overrides or list resolvers. No?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Can Windsor's TypedFactoryFacility's implicit delegate ...
If you really want to you can remove the DelegateFactory component from the container, which is what creates the delegate-based factories.
Read more >
Delegate Factories — Autofac 7.0.0 documentation
The first step in setting up a delegate factory is to create a delegate that will be used to dynamically resolve values from...
Read more >
10 Advanced Windsor Tricks – 1A. A Delegate Factory ...
Here's part 1A of my series: 10 Advanced Windsor Tricks. Yes, I'm getting totally side-tracked already. After I published my first trick ...
Read more >
Castle.Windsor.xml 0.0.145
Represents a property and the respective dependency. ... Initializes a new instance of the <see cref = "T:Castle.Core.PropertySet" /> class. ... Gets the...
Read more >
Typed Factory
Typed Factory Facility provides automatically generated Abstract Factories that you can use to create components in your code, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found