Auto register Func for .net core dependecy injection












0














The default .net core dependency injection doesn't support building Func<> for injection automatically, see question. I wrote an extension method to go find all the Func in register types constructors and build the Func automatically, needs to be called at end of registrations.



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

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


I register the Funcs as Transient but I think it would have been safe to register them as singleton. Not sure which has more overhead.



For a simple test



public interface ITransient
{
Guid Id { get; }
}

public class Transient : ITransient
{
public Transient()
{
Id = Guid.NewGuid();
}
public Guid Id { get; }
}

public class SingleTon
{
private readonly Func<ITransient> _transientFunc;
public SingleTon(Func<ITransient> transientFunc)
{
_transientFunc = transientFunc;
}

public ITransient GetTransient()
{
return _transientFunc();
}
}


If the func isn't working correctly I would get the same object each time back on the transient class.



static void Main(string args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<SingleTon>();
serviceCollection.AddTransient<ITransient, Transient>();
serviceCollection.AddFactories();
var container = serviceCollection.BuildServiceProvider();

var singleton = container.GetService<SingleTon>();
if (object.ReferenceEquals(singleton.GetTransient(), singleton.GetTransient()))
{
throw new InvalidOperationException();
}

Console.WriteLine("Done");
Console.ReadLine();
container.Dispose();
}









share|improve this question






















  • Singletons should not depend on transients
    – Adrian Iftode
    8 mins ago










  • I understand that is the guideline. I also agree with that guideline but sometimes you do need to have other classes used and either you can inject a func which you can sub out during testing or you have to use the service locator pattern.
    – CharlesNRice
    1 min ago
















0














The default .net core dependency injection doesn't support building Func<> for injection automatically, see question. I wrote an extension method to go find all the Func in register types constructors and build the Func automatically, needs to be called at end of registrations.



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

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


I register the Funcs as Transient but I think it would have been safe to register them as singleton. Not sure which has more overhead.



For a simple test



public interface ITransient
{
Guid Id { get; }
}

public class Transient : ITransient
{
public Transient()
{
Id = Guid.NewGuid();
}
public Guid Id { get; }
}

public class SingleTon
{
private readonly Func<ITransient> _transientFunc;
public SingleTon(Func<ITransient> transientFunc)
{
_transientFunc = transientFunc;
}

public ITransient GetTransient()
{
return _transientFunc();
}
}


If the func isn't working correctly I would get the same object each time back on the transient class.



static void Main(string args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<SingleTon>();
serviceCollection.AddTransient<ITransient, Transient>();
serviceCollection.AddFactories();
var container = serviceCollection.BuildServiceProvider();

var singleton = container.GetService<SingleTon>();
if (object.ReferenceEquals(singleton.GetTransient(), singleton.GetTransient()))
{
throw new InvalidOperationException();
}

Console.WriteLine("Done");
Console.ReadLine();
container.Dispose();
}









share|improve this question






















  • Singletons should not depend on transients
    – Adrian Iftode
    8 mins ago










  • I understand that is the guideline. I also agree with that guideline but sometimes you do need to have other classes used and either you can inject a func which you can sub out during testing or you have to use the service locator pattern.
    – CharlesNRice
    1 min ago














0












0








0







The default .net core dependency injection doesn't support building Func<> for injection automatically, see question. I wrote an extension method to go find all the Func in register types constructors and build the Func automatically, needs to be called at end of registrations.



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

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


I register the Funcs as Transient but I think it would have been safe to register them as singleton. Not sure which has more overhead.



For a simple test



public interface ITransient
{
Guid Id { get; }
}

public class Transient : ITransient
{
public Transient()
{
Id = Guid.NewGuid();
}
public Guid Id { get; }
}

public class SingleTon
{
private readonly Func<ITransient> _transientFunc;
public SingleTon(Func<ITransient> transientFunc)
{
_transientFunc = transientFunc;
}

public ITransient GetTransient()
{
return _transientFunc();
}
}


If the func isn't working correctly I would get the same object each time back on the transient class.



static void Main(string args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<SingleTon>();
serviceCollection.AddTransient<ITransient, Transient>();
serviceCollection.AddFactories();
var container = serviceCollection.BuildServiceProvider();

var singleton = container.GetService<SingleTon>();
if (object.ReferenceEquals(singleton.GetTransient(), singleton.GetTransient()))
{
throw new InvalidOperationException();
}

Console.WriteLine("Done");
Console.ReadLine();
container.Dispose();
}









share|improve this question













The default .net core dependency injection doesn't support building Func<> for injection automatically, see question. I wrote an extension method to go find all the Func in register types constructors and build the Func automatically, needs to be called at end of registrations.



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

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


I register the Funcs as Transient but I think it would have been safe to register them as singleton. Not sure which has more overhead.



For a simple test



public interface ITransient
{
Guid Id { get; }
}

public class Transient : ITransient
{
public Transient()
{
Id = Guid.NewGuid();
}
public Guid Id { get; }
}

public class SingleTon
{
private readonly Func<ITransient> _transientFunc;
public SingleTon(Func<ITransient> transientFunc)
{
_transientFunc = transientFunc;
}

public ITransient GetTransient()
{
return _transientFunc();
}
}


If the func isn't working correctly I would get the same object each time back on the transient class.



static void Main(string args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<SingleTon>();
serviceCollection.AddTransient<ITransient, Transient>();
serviceCollection.AddFactories();
var container = serviceCollection.BuildServiceProvider();

var singleton = container.GetService<SingleTon>();
if (object.ReferenceEquals(singleton.GetTransient(), singleton.GetTransient()))
{
throw new InvalidOperationException();
}

Console.WriteLine("Done");
Console.ReadLine();
container.Dispose();
}






c# dependency-injection asp.net-core






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked 1 hour ago









CharlesNRice

1,849512




1,849512












  • Singletons should not depend on transients
    – Adrian Iftode
    8 mins ago










  • I understand that is the guideline. I also agree with that guideline but sometimes you do need to have other classes used and either you can inject a func which you can sub out during testing or you have to use the service locator pattern.
    – CharlesNRice
    1 min ago


















  • Singletons should not depend on transients
    – Adrian Iftode
    8 mins ago










  • I understand that is the guideline. I also agree with that guideline but sometimes you do need to have other classes used and either you can inject a func which you can sub out during testing or you have to use the service locator pattern.
    – CharlesNRice
    1 min ago
















Singletons should not depend on transients
– Adrian Iftode
8 mins ago




Singletons should not depend on transients
– Adrian Iftode
8 mins ago












I understand that is the guideline. I also agree with that guideline but sometimes you do need to have other classes used and either you can inject a func which you can sub out during testing or you have to use the service locator pattern.
– CharlesNRice
1 min ago




I understand that is the guideline. I also agree with that guideline but sometimes you do need to have other classes used and either you can inject a func which you can sub out during testing or you have to use the service locator pattern.
– CharlesNRice
1 min ago















active

oldest

votes











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210507%2fauto-register-funct-for-net-core-dependecy-injection%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210507%2fauto-register-funct-for-net-core-dependecy-injection%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

List directoties down one level, excluding some named directories and files

list processes belonging to a network namespace

List all connected SSH sessions?