使用asp.net 5
我希望我的控制器注入Func<T>
代替T
例如:
public HomeController(Func<Interfaces.IUnitOfWork> uow)
而不是
public HomeController(Interfaces.IUnitOfWork uow)
是否可以使用内置DI或我被迫移动到外部DI?
答案 0 :(得分:6)
Func<T>
没有注册或解决,但没有什么能阻止您自己注册。
e.g。
services.AddSingleton(provider =>
new Func<IUnitOfWork>(() => provider.GetService<IUnitOfWork>()));
请注意,您还需要以通常的方式注册IUnitOfWork。
答案 1 :(得分:4)
据我所知,使用ASP.NET Core中的当前默认IoC容器无法推迟这样的依赖。我还是无法让它工作!
要推迟这样的依赖关系的初始化,您需要实现一个现有的,功能更丰富的IoC容器。
答案 2 :(得分:2)
您可以注册Func<T>
或具有ServiceCollection
的代表。我建议使用一个委托,因为它可以让您区分具有相同签名的不同方法。
这是一个例子。
public interface IThingINeed {}
public class ThingINeed : IThingINeed { }
public delegate IThingINeed ThingINeedFactory();
public class DelegateRegistrationTests
{
[Test]
public void RegisterDelegateFromDependency()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IThingINeed, ThingINeed>();
serviceCollection.AddTransient<ThingINeedFactory>(
provider => provider.GetService<IThingINeed>);
var serviceProvider = serviceCollection.BuildServiceProvider();
var factoryMethod = serviceProvider.GetService<ThingINeedFactory>();
var output = factoryMethod();
Assert.IsInstanceOf<ThingINeed>(output);
}
}
这几乎看起来像是服务定位符,因为我们要解析的功能实际上是IServiceCollection.GetService<ThingINeedFactory>()
。但这隐藏在合成根目录中。注入此委托的类取决于委托,而不取决于实现。
如果要返回的方法属于容器必须解析的类,则可以使用相同的方法。
public interface IThingINeed
{
string SayHello();
}
public class ThingINeed : IThingINeed
{
private readonly string _greeting;
public ThingINeed(string greeting)
{
_greeting = greeting;
}
public string SayHello() => _greeting;
}
public class ThingINeedFactory
{
public IThingINeed Create(string input) => new ThingINeed(input);
}
public delegate IThingINeed ThingINeedFactoryMethod(string input);
public class DelegateRegistrationTests
{
[Test]
public void RegisterDelegateFromDependency()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IThingINeed, ThingINeed>();
serviceCollection.AddSingleton<ThingINeedFactory>();
serviceCollection.AddSingleton<ThingINeedFactoryMethod>(provider =>
provider.GetService<ThingINeedFactory>().Create);
var serviceProvider = serviceCollection.BuildServiceProvider();
var factoryMethod = serviceProvider.GetService<ThingINeedFactoryMethod>();
var created = factoryMethod("abc");
var greeting = created.SayHello();
Assert.AreEqual("abc", greeting);
}
}
这是一种扩展方法,可以(也许)使它更容易一些:
public static class ServiceCollectionExtensions
{
public static IServiceCollection RegisterDelegate<TSource, TDelegate>(
this IServiceCollection serviceCollection,
Func<TSource, TDelegate> getDelegateFromSource)
where TDelegate : class
{
return serviceCollection.AddSingleton(provider =>
getDelegateFromSource(provider.GetService<TSource>()));
}
}
serviceCollection
.RegisterDelegate<ThingINeedFactory, ThingINeedFactoryMethod>(
factory => factory.Create);
答案 3 :(得分:1)
您可以使用几种选择,第一种是您可以切换使用令人难以置信的Lamar(使用ASP.NET Core integration)。
在大多数情况下,切换到Lamar只需几行代码,您就可以整天解析Func<>
和Lazy<>
。
我已经在大型的基于微服务的平台上大规模使用了它一段时间,我们对它*感到完全满意。
如果您不想转移到Lamar,可以使用它来解决Lazy<>
(很抱歉,我已经尝试了很多次,但无法与{{1}一起使用) }:
Func<>
为了完整起见,这也是一个测试。
// Add to your Service Collection.
services.AddTransient(typeof(Lazy<>), typeof(LazyServiceFactory<>));
class LazyServiceFactory<T> : Lazy<T>
{
public LazyServiceFactory(IServiceProvider serviceProvider)
: base(() => serviceProvider.GetRequiredService<T>())
{
}
}
由于我现在非常专心地使用Lamar,因此我没有完全按照这个进度进行操作,但这对于较小的/一次性项目很方便。
*我唯一的小问题是它不支持// And some tests...
[TestMethod]
[DataTestMethod]
[DataRow(ServiceLifetime.Transient)]
[DataRow(ServiceLifetime.Scoped)]
[DataRow(ServiceLifetime.Singleton)]
public void Resolve_GivenLazyilyRegisteredService_CanResolve(ServiceLifetime serviceLifetime)
{
// Arrange
IServiceProvider serviceProvider = CreateServiceProvider(serviceLifetime);
using IServiceScope scope = serviceProvider.CreateScope();
// Act
Func<Lazy<ClassHello>> result = () => scope.ServiceProvider.GetRequiredService<Lazy<ClassHello>>();
// Assert
result
.Should()
.NotThrow()
.And
.Subject()
.Value
.Should()
.NotBeNull();
}
static IServiceProvider CreateServiceProvider(ServiceLifetime serviceLifetime)
{
IServiceCollection services = new ServiceCollection();
services.Add(new ServiceDescriptor(typeof(Lazy<>), typeof(LazyServiceFactory<>), serviceLifetime));
services.Add(new ServiceDescriptor(typeof(ClassHello), typeof(ClassHello), serviceLifetime));
return services.BuildServiceProvider(true);
}
yet。
答案 4 :(得分:0)
.net核心的默认依赖项注入中没有内置的Func构建支持,我们可以构建扩展方法以添加所有缺少的func。我们只需要确保在注册结束时调用它即可。
public static class ServiceCollectionExtensions
{
private static MethodInfo GetServiceMethod;
static ServiceCollectionExtensions()
{
Func<IServiceProvider, object> getServiceMethod = ServiceProviderServiceExtensions.GetService<object>;
GetServiceMethod = getServiceMethod.Method.GetGenericMethodDefinition();
}
/// <summary>
/// Registers all Funcs in constructors to the ServiceCollection - important to call after all registrations
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
public static IServiceCollection AddFactories(this IServiceCollection collection)
{
// Get a list of all Funcs used in constructors of regigstered types
var funcTypes = new HashSet<Type>(collection.Where(x => x.ImplementationType != null)
.Select(x => x.ImplementationType)
.SelectMany(x => x.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
.SelectMany(x => x.GetParameters())
.Select(x => x.ParameterType)
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>)));
// Get a list of already registered Func<> and remove them from the hashset
var registeredFuncs = collection.Select(x => x.ServiceType)
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>));
funcTypes.ExceptWith(registeredFuncs);
// Each func build the factory for it
foreach (var funcType in funcTypes)
{
var type = funcType.GetGenericArguments().First();
collection.AddTransient(funcType, FuncBuilder(type));
}
return collection;
}
/// <summary>
/// This build expression tree for a func that is equivalent to
/// Func<IServiceProvider, Func<TType>> factory = serviceProvider => new Func<TType>(serviceProvider.GetService<TType>);
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static Func<IServiceProvider, object> FuncBuilder(Type type)
{
var serviceProvider = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
var method = GetServiceMethod.MakeGenericMethod(type);
var call = Expression.Call(method, serviceProvider);
var returnType = typeof(Func<>).MakeGenericType(type);
var returnFunc = Expression.Lambda(returnType, call);
var func = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(IServiceProvider), returnType), returnFunc, serviceProvider);
var factory = func.Compile() as Func<IServiceProvider, object>;
return factory;
}
}
在AddFactories中,我们获得所有已注册的concreate类型的列表,然后检查其构造函数中是否有Func <>。从该列表中删除之前已注册的所有Func。使用一些expressiontrees,我们构建所需的Funcs。
codereview中的代码也结束了,减去了对已注册功能的检查。
答案 5 :(得分:0)
我写了一些扩展方法,用于注册服务和工厂(Func<T>
):
public static class IServiceCollectionExtension
{
public static IServiceCollection AddFactory<TService, TServiceImplementation>(this IServiceCollection serviceCollection)
where TService : class
where TServiceImplementation : class, TService
{
return serviceCollection
.AddTransient<TService, TServiceImplementation>();
.AddSingleton<Func<TService>>(sp => sp.GetRequiredService<TService>);
}
}
用法:
serviceCollection
.AddFactory<IMyInterface, MyImplementation>()