Register a common factory for all types that implement the interface

I have a generic factory

public interface IViewModelFactory<T> where T : IViewModel
{
    T Create<TU>(TU par);
}

public class ViewModelFactory<T> : IViewModelFactory<T> where T : IViewModel
{
    private readonly ILifetimeScope _scope;

    public ViewModelFactory(ILifetimeScope scope)
    {
        _scope = scope;
    }

    public T Create<TU>(TU par)
    {
        return _scope.Resolve<T>(new TypedParameter(typeof(TU), par));
    }
}

      

which I can use to resolve the viewmodel factory in my window class:

public WRPersons(IViewModelFactory<MRPersons> viewModelFactory)
{
    var viewModel = viewModelFactory.Create(new MRPersonsUseCaseParams { Filter = 2 });
    ...
}

      

ViewModel is implemented by the following code

public class MRPersons : IViewModel
{
    public MRPersons(MRPersonsUseCaseParams par)
    {
        _filter = par.Filter;
    }
}

public class MRPersonsUseCaseParams
{
    public int Filter { get; set; }
}

      

The registration at the root of my composition looks like this:

var builder = new ContainerBuilder();
builder.RegisterType<ViewModelFactory<MRPersons>>().As<IViewModelFactory<MRPersons>>();
builder.RegisterType<MRPersons>();

      

As you can see for every new ViewModel (now the only MRPerson), I will need to create two entries in my root directory. So for MRCar it would be:

builder.RegisterType<ViewModelFactory<MRCar>>().As<IViewModelFactory<MRCar>>();
builder.RegisterType<MRCar>();

      

I would like to automate this registration somehow. I've experimented with RegisterAssemblyTypes / AsClosedTypesOf with no success. Can anyone help me?

EDIT:

Based on response code line

builder.RegisterType<ViewModelFactory<MRPersons>>().As<IViewModelFactory<MRPersons>>();

      

is replaced by

builder.RegisterGeneric(typeof(ViewModelFactory<>)).As(typeof(IViewModelFactory<>)); 

      

Full automatic registration looks like this:

builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass).AsSelf();
builder.RegisterGeneric(typeof(ViewModelFactory<>)).As(typeof(IViewModelFactory<>));

      

For a better testable solution, it would be nice to even replace MRPersons with IMRPersons:

public class MRPersons : IViewModel, IMRPersons
{
    public MRPersons(MRPersonsUseCaseParams par)
    {
        _filter = par.Filter;
    }
}

public class MRPersonsUseCaseParams
{
    public int Filter { get; set; }
}

public interface IMRPersons 
{
}

      

Thus, registration at the root of the composition will look like this (NECESSARY CORRECTION)

builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass).As<??????>.AsSelf();

      

This would allow me to pass the factory to the constructor like this:

public WRPersons(IViewModelFactory<IMRPersons> viewModelFactory)
{
    var viewModel = viewModelFactory.Create(new MRPersonsUseCaseParams { Filter = 2 });
    ...
}

      

EDIT2: While chatting with Kirill Durant, he provided a solution for the ViewModelFactory without linking to the ILifetimeScope. Here is the code:

public interface IViewModelFactory2<T, TU> where T : IViewModel
{
    T Create(TU par);
} 
public class ViewModelFactory2<T, TU> : IViewModelFactory2<T, TU> where T : IViewModel
{
    private readonly Func<TU, T> _factory;

    public ViewModelFactory2(Func<TU, T> factory)
    {
        _factory = factory;
    }

    public T Create(TU par)
    {
        return _factory(par);
    } 
}

      

My original factory is fine too, as it is represented at the root of the composition where strong DI container references can be used.

+3


source to share


1 answer


You want to register ViewModelFactory<>

as IViewModelFactory<>

, you can do it with the method RegisterGeneric

.

builder.RegisterGeneric(typeof(ViewModelFactory<>)).As(typeof(IViewModelFactory<>)); 

      

Then you will be able to resolve IViewModelFactory<MRCar>

without any other registration.

See Registration Concepts - Open Shared Components for details.


For the second part of the question:

For a better testable solution, it would be nice to replace MRPersons

withIMRPersons



It's not that easy because there is no way to know which interface to use. You can use AsImplementedInterfaces

that is equivalent As<IMRPersons>().As<IViewModel>()

, but this can be a problem if you have a lot of interface implemented.

builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly())
       .Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass)
       .AsImplementedInterfaces();

      

Or you can use an agreement that will register everything X

as IX

, but I'm not a big fan of this kind of registration.

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
        .Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass)
        .As(t => t.GetInterfaces().Where(i => i.Name.EndsWith(t.Name)));

      


By the way, after communication, we found out that you do not need at all IViewModelFactory<>

, but you only need dependence onFunc<TParam, T>

+1


source







All Articles