I have some factory interfaces.
Some factories don't need any implementor, so I can bind them as follows.
IKernel kernel = new StandardKernel();
kernel.Bind(services => services
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.FullName.Contains("MyProject")
&& !a.FullName.Contains("Tests")))
.SelectAllInterfaces()
.EndwingWith("Factory")
.BindToFactory());
Which works flawlessly as long I don't need to provide arguments to the constructor, which should be provided through Method Injection.
Besides, using this code, I have bound my ICustomerManagementPresenterFactory
as well, and it is not bound to its implementer.
ICustomerManagementPresenterFactory
public interface ICustomerManagementPresenterFactory {
CustomerManagementPresenter Create();
}
CustomerManagementPresenterFactory
public class CustomerManagementPresenterFactory : ICustomerManagementPresenterFactory {
public CustomerManagementPresenterFactory(ICustomerManagementView view
, ICustomerDetailPresenterFactory factory) {
this.factory = factory;
this.view = view;
}
public CustomerManagementPresenter Create() {
return new CustomerManagementPresenter(view, factory);
}
private readonly ICustomerDetailPresenterfactory factory;
private readonly ICustomerManagementView view;
}
So, because the constructor of CustomerManagementPresenter takes two arguments, I wish to implement a factory which won't need to be method injected the dependencies of the class it creates, and I keep using Constructor Injection.
So, I would like to benefit from the Convention Binding, and still bind the two differently.
How might I go about this?
Sadly enough you can't retrieve the list of types "selected" by the convention. That means you'll have to work around it someway.
The syntax offers the Where(Func<Type, bool> selector)
and the Excluding(IEnumerable<Type> types)
methods. So you'd need to get the interface of all implemented factories before binding the interface factories with .ToFactory()
. For example:
IKernel kernel = new StandardKernel();
IList<Type> implementedFactoryInterfaces = new List<Type>();
kernel.Bind(services => services
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.FullName.Contains("MyProject")
&& !a.FullName.Contains("Tests")))
.SelectAllClasses()
.EndingWith("Factory")
.Where(classFactoryType =>
{
implementedFactoryInterfaces.Add(classFactoryType.GetInterfaces().Single());
return true;
})
.BindDefaultInterface());
kernel.Bind(services => services
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.FullName.Contains("MyProject")
&& !a.FullName.Contains("Tests")))
.SelectAllInterfaces()
.EndingWith("Factory")
.Excluding(implementedFactoryInterfaces)
.BindToFactory());
An alternative would be to implement a binding generator which check whether there's an implementation for the passed interface-Type
and create the binding accordingly:
public class InterfaceAndClassFactoryBindingGenerator : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (!type.IsInterface)
{
throw new ArgumentOutOfRangeException("type", type, "is not an interface, but only interfaces are supported");
}
Type classImplementingTheFactoryInterface = type.Assembly.GetTypes()
.Where(t => t.IsClass)
.SingleOrDefault(type.IsAssignableFrom);
if (classImplementingTheFactoryInterface == null)
{
bindingRoot.Bind(type).ToFactory();
}
else
{
bindingRoot.Bind(type).To(classImplementingTheFactoryInterface);
}
}
}
IKernel kernel = new StandardKernel();
kernel.Bind(services => services
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.FullName.Contains("MyProject")
&& !a.FullName.Contains("Tests")))
.SelectAllInterfaces()
.EndingWith("Factory")
.BindWith<InterfaceAndClassFactoryBindingGenerator>());