为实现接口的所有类型注册通用工厂
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>
的依赖
我有通用工厂
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
byIMRPersons
这不是那么容易,因为无法知道使用哪个接口。您可以使用等同于 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>