为实现接口的所有类型注册通用工厂

Register generic factory for all types which implements an interface

我有通用工厂

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));
    }
}

我可以用它来解析我的 window class:

中的视图模型工厂
public WRPersons(IViewModelFactory<MRPersons> viewModelFactory)
{
    var viewModel = viewModelFactory.Create(new MRPersonsUseCaseParams { Filter = 2 });
    ...
}

ViewModel通过以下代码实现

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

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

在我的组合根目录中注册如下:

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

如您所见,对于每个新的 ViewModel(现在它是唯一的 MRPerson),我需要在我的组合根目录中创建两个条目。因此对于 MRCar 它将是:

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

我想以某种方式使这些注册自动化。我尝试了 RegisterAssemblyTypes/AsClosedTypesOf 但没有成功。有人可以帮助我吗?

编辑:

基于答案代码行

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

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

全自动注册看起来像:

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

为了获得更好的可测试解决方案,甚至可以用 IMRPersons 替换 MRPersons:

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

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

public interface IMRPersons 
{
}

因此在组合根中的注册看起来像(需要更正)

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

这将允许我通过以下方式将工厂传递给构造函数:

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

编辑2: 在与 Cyril Durand 聊天期间,他为 ViewModelFactory 提供了解决方案,但没有参考 ILifetimeScope。这是一个代码:

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);
    } 
}

我的原始工厂也可以,因为它出现在组合根中,可以使用对 DI 容器的强引用。

您想将ViewModelFactory<>注册为IViewModelFactory<>,您可以使用RegisterGeneric方法。

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

那么您将能够解析 IViewModelFactory<MRCar> 而无需任何其他注册。

有关详细信息,请参阅 Registration Concepts - Open Generic Components


对于问题的第二部分:

For better testable solution it would be fine to even replace MRPersons by IMRPersons

这不是那么容易,因为无法知道使用哪个接口。您可以使用等同于 As<IMRPersons>().As<IViewModel>()AsImplementedInterfaces,但如果您有很多已实现的接口,则可能会出现问题。

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

或者您可以使用将所有 X 注册为 IX 的约定,但我不是这种注册的忠实拥护者。

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

顺便说一下,经过聊天,我们发现您根本不需要 IViewModelFactory<>,而只需要对 Func<TParam, T>

的依赖