enricosebastian
enricosebastian

Reputation: 47

Dependency injection for open type generic interface and closed type class

I have a generic interface IViewModelService.cs

public interface IViewModelService<T> where T : class
{
    public Task<T> Get(); // returns a view model
}

Which is implemented by two different View Model Services that I assume are closed types:

public class HomeViewModelService : IViewModelService<HomeViewModel>
{
    public HomeViewModelService()
    {
        // Dependency injection for Db Context here
    }

    public async Task<HomeViewModel> Get()
    {
        //requires some async _dbContext logic
        return new HomeViewModel(); // for testing purposes
    }
}

and

public class ProfileViewModelService : IViewModelService<ProfileViewModel>
{
    public ProfileViewModelService()
    {
        // Dependency injection for Db Context here
    }

    public async Task<ProfileViewModel> Get()
    {
        //requires some async _dbContext logic
        return new ProfileViewModel(); // for testing purposes
    }
}

Been researching on dependency injection, and I wanted to know how would I be able to implement these services into Startup.cs. I tried these:

services.AddTransient(typeof(IViewModelService<>), typeof(HomeViewModelService)); 

// ERROR: Open generic service type 'IViewModelService`1[T]' requires registering an open generic implementation type. (Parameter 'descriptors')'
services.AddTransient(typeof(IViewModelService<HomeViewModelService>), typeof(HomeViewModelService)); 

// ERROR: Implementation type 'HomeViewModelService' can't be converted to service type 'IViewModelService`1[HomeViewModelService]'

services.AddScoped<IViewModelService<HomeViewModelService>, HomeViewModelService>(); 

// ERROR: This type cannot be used as a type parameter 'TImplementation' in the generic type or method. 
// There is no implicit reference conversion from HomeViewModelService to IViewModelService<HomeViewModelService>

Upvotes: 1

Views: 3401

Answers (2)

Ruikai Feng
Ruikai Feng

Reputation: 11546

I tried as below to accomplish dependency injection for open type generic interface.if you don't want to implement the intereface and regist the services too much times ,you could try this

public class HomeViewModel
    {
        public HomeViewModel()
        {
            id = 1;
            name = "name1";
        }
        public int id { get; set; }
        public string name { get; set; }
    }
    public class AnotherViewModel
    {
        public AnotherViewModel()
        {
            id = 2;
            name = "name2";
        }
        public int id { get; set; }
        public string name { get; set; }
    }

Modified the codes of service class:

public interface IViewModelService<T> where T : class
    {
        public T Get(); 
    }
    public class ViewModelService<T> : IViewModelService<T> where T : class
    {
        public ViewModelService()
        {
            
        }

        public  T Get()
        {
            Type t = typeof(T);
            if(t==typeof(HomeViewModel))
            {
                HomeViewModel homeviewmodel = new HomeViewModel();
                return (T)(object)homeviewmodel;
            }
            else if (t == typeof(AnotherViewModel))
            {
                AnotherViewModel anotherviewmodel = new AnotherViewModel();
                return (T)(object)anotherviewmodel;
            }           
            return default(T);
            
        }
    }

regist it in startup:

services.AddTransient(typeof(IViewModelService<>), typeof(ViewModelService<>));

in controller:

public IActionResult Test1([FromServices] IViewModelService<HomeViewModel> viewModelService)
        {
            var homeviewmodel = viewModelService.Get();
            return Ok();
        }
        public IActionResult Test2([FromServices] IViewModelService<AnotherViewModel> viewModelService)
        {
            var anotherviewmodel = viewModelService.Get();
            return Ok();
        }

Test Result: enter image description here

Upvotes: 2

Qing Guo
Qing Guo

Reputation: 9092

Try

 services.AddScoped<IViewModelService<HomeViewModel>, HomeViewModelService>();

Read this answer to know more

Upvotes: 3

Related Questions