How to use Func in built-in dependency injection

前端 未结 6 685
梦毁少年i
梦毁少年i 2021-02-14 11:17

Using asp.net 5 I\'d like my controller to be injected with a Funcinstead of T

For example:



        
6条回答
  •  爱一瞬间的悲伤
    2021-02-14 11:50

    While there is no built in Func building support in the default dependency injection for .net core we can build an extension method to add in all the missing funcs. We just need to make sure we call it at the end of registration.

    public static class ServiceCollectionExtensions
    {
        private static MethodInfo GetServiceMethod;
    
        static ServiceCollectionExtensions()
        {
            Func getServiceMethod = ServiceProviderServiceExtensions.GetService;
            GetServiceMethod = getServiceMethod.Method.GetGenericMethodDefinition();
        }
    
        /// 
        /// Registers all Funcs in constructors to the ServiceCollection - important to call after all registrations
        /// 
        /// 
        /// 
        public static IServiceCollection AddFactories(this IServiceCollection collection)
        {
    
            // Get a list of all Funcs used in constructors of regigstered types
            var funcTypes = new HashSet(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;
        }
    
        /// 
        /// This build expression tree for a func that is equivalent to 
        ///     Func> factory = serviceProvider => new Func(serviceProvider.GetService);
        /// 
        /// 
        /// 
        private static Func 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;
            return factory;
        }
    }
    
    
    

    In AddFactories we get a list of all the concreate types that are registered then check their constructors for any Func<>. From that list remove any Func that has been registered before. Using some expressiontrees we build the needed Funcs.

    The code is also over in codereview, minus the check for already registered funcs.

    提交回复
    热议问题