我有一个
ServiceCollection
,我想创建一个可以传入一组参数的方法,并让 ServiceCollection
在请求匹配类型时返回这些服务。我正在使用 Autofixture 创建我的服务。当我调用 fixture.Create<IMyService>
时,它会返回 Mock<IMyService>
。例如
public static IServiceProvider CreateServiceProvider(params object[] services)
{
var serviceCollection = new ServiceCollection();
foreach (object service in services)
{
serviceCollection.AddTransient(factory => service);
}
return serviceCollection.BuildServiceProvider();
}
然后我想这样使用它
[Fact]
public void TestSomething()
{
// This causes myFirstService to be of type of type IMyFirstService,
// but have a value of type Mock<IMyFirstService> (same for
// mySecondService)
var fixture = new Fixture();
IMyFirstService myFirstService = fixture.Create<IMyFirstService>();
IMySecondService mySecondService = fixture.Create<IMySecondService>();
var serviceProvider = CreateServiceProvider(myFirstService, mySecondService);
// I want the returned objects to be myFirstService and mySecondService
// that I created above
var resolvedFirstService = serviceProvider.GetService<IMyFirstService>();
var resolvedSecondService = serviceProvider.GetService<IMySecondService>();
}
问题是
myFirstService
和 mySecondService
不会返回,因为当调用 object
时,服务会被强制转换为 CreateServiceProvider
,因此它们会在 ServiceCollection
中注册为 object
类型,而不是正确的接口类型。我尝试使用 serviceProvider.GetService(service.GetType(), factory => service)
但问题是 service.GetType()
返回 Castle.Proxies.IMyFirstServiceProxy
。
我知道我可以通过执行下面的代码来完成这项工作,但我希望它通过让
CreateServiceProvider
方法注册这些服务来工作。
[Fact]
public void TestSomething()
{
var fixture = new Fixture();
IMyFirstService myFirstService = fixture.Create<IMyFirstService>();
IMySecondService mySecondService = fixture.Create<IMySecondService>();
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient(factory => myFirstService);
serviceCollection.AddTransient(factory => mySecondService);
var serviceProvider = serviceCollection.BuildServiceProvider();
var resolvedFirstService = serviceProvider.GetService<IMyFirstService>();
var resolvedSecondService = serviceProvider.GetService<IMySecondService>();
}
我不知道实现这一目标的简单方法,但如果你坚持走这条路,你可以采用相当脆弱的反射路径并弄乱实现细节:
static IServiceProvider CreateServiceProvider(params object[] services)
{
var serviceCollection = new ServiceCollection();
foreach (object service in services)
{
var m = service as IMocked;
var value = typeof(Mock).GetProperty("MockedType", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(m.Mock) as Type;
serviceCollection.AddTransient(value, _ => service);
}
return serviceCollection.BuildServiceProvider();
}
但这可能非常脆弱,所以我建议重新考虑该方法并使用
Type
作为参数:
static IServiceProvider CreateServiceProvider(params Type[] services)
{
var serviceCollection = new ServiceCollection();
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var context = new SpecimenContext(fixture);
foreach (var serviceType in services)
{
var o1 = fixture.Create(serviceType, context);
serviceCollection.AddTransient(serviceType, _ => o1);
}
return serviceCollection.BuildServiceProvider();
}
通过这样的电话:
var serviceProvider = CreateServiceProvider(typeof(IMyFirstService), typeof(IMySecondService));