Auto register Func for .net core dependecy injection
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
add a comment |
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
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
add a comment |
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
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
c# dependency-injection asp.net-core
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
add a comment |
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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