54

I have an asp.net core web app with multiple parameters in appSettings.json file.

I didnt' want to have services having IOptions<MyObject> in the constructor.

I wanted MyObject in the constructor. So I found the following article: https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited which is very interesting.

But I want to go further. I would like to create an extension method to generate the injection.

Here is what I would like to do:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Common.WebTools.Extensions
{
    public static class IServiceCollectionExtensions
    {
        public static IServiceCollection AddSingletonConfigurationObject<T>(this IServiceCollection services, 
            IConfiguration configuration,
            string appSettingsKey) where T:new()
        {   
            var obj2 = new T();
            configuration.Bind(appSettingsKey, obj);
            services.AddSingleton(obj2); //compilation failed
            return services;
        }
    }
}

And then in my ConfigureServices method I can call

services.AddSingletonConfigurationObject<Common.Tools.Configuration.GoogleAnalyticsConfiguration>(Configuration, "GoogleAnalytics");

But I Have a compliation error on this line:

services.AddSingleton(obj2); 

Does somebody know how could I correct the error?

5
  • And what does the error says? Commented May 23, 2019 at 9:18
  • impossible cast from 'T' to 'System.Type' ->it doesn't use the good constructor Commented May 23, 2019 at 9:43
  • 1
    I am not sure but maybe it can't guess the type. Alternatively you can try serices.AddSingleton<T>(obj2) or services.AddSingleton(typeof(T), obj2) Commented May 23, 2019 at 9:49
  • 4
    And T shoud be defined as class: where T : class, new() Commented May 23, 2019 at 9:59
  • 1
    Thank you @Rabban where T : class, new() is the key :) Commented May 23, 2019 at 12:53

3 Answers 3

106

You can use services.AddScoped to use only 1 instance in the scope request. So in general improvement compare to AddTransient

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));

So my interface and class will look like this

public interface IGenericRepository<T> where T : class
public class GenericRepository<T> : IGenericRepository<T> where T : class
Sign up to request clarification or add additional context in comments.

4 Comments

So how would the target for the injected instance actually look?
Is it even possible with constructor injecton since it requires a type argument? private readonly IGenericRepository<???> _repository;
private readonly IGenericRepository<T> GenericRepository; ....... in the constructor parameters use ....IGenericRepository<T> genericRepository; and in body of constructor set the property .... GenericRepository = genericRepository. you have to use it in generic class, with same T inheritance.
Any idea why this idea can't work with AddTransient?
3

As stated in the comments you should set the generic constraint to where T: class in order to satisfy the constraint of the AddSingleton call.

Reference AddSingleton(IServiceCollection, TService)

Additionally you can bind the object graph using ConfigurationBinder.Get<T>

Reference Configuration in ASP.NET Core : Bind to an object graph

ConfigurationBinder.Get<T> binds and returns the specified type. Get<T> is more convenient than using Bind.

like in the following example

public static class IServiceCollectionExtensions {
    public static IServiceCollection AddSingletonConfigurationObject<TService>(
        this IServiceCollection services, 
        IConfiguration configuration,
        string appSettingsKey) where TService: class
    {   

        var setting = configuration.GetSection(appSettingsKey).Get<TService>();
        services.AddSingleton(setting);
        return services;
    }
}

1 Comment

"You should set the generic constraint to where T: class in order to satisfy the constraint of the AddSingleton call." Why? The constraint of AddSingleton is just where TService : class where TImplementation : class, TService, which doesn't limit the type parameter of TService and TImplementation, right?
0

I was able to use this style to encapsulate (as an extension method)

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace MyProject.DependencyInjection
{
    public static class MyEncapsulatedIocRegistration
    {
        public static IServiceCollection AddTypeT<T>(this IServiceCollection services) where T : class
        {
            services.AddSingleton<IMyThingInterface<T>, MyThingConcrete<T>>();

            return services;
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.