Home > Net >  Pass constructor with parameter as generic on .NET Core
Pass constructor with parameter as generic on .NET Core

Time:08-26

I have a method to update the user status

public async Task UpdateStatus<TService, T>(int id, int status)
    where TService : StatusService<T>, new()
{
    await new TService().UpdateStatus(id, status);
}

My UserService have constructor

public class UserService : StatusService<User>
{
    public UserService(MyContext context) : base(context)
    {
    }

    ....
}

Action to update user status:

public async Task UpdateUserStatus(int id, int status)
{
    await UpdateStatus<UserService, User>(id, status);
}

Then my code have the error:

'UserService' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TService' in the generic type or method

How do I pass the constructor with parameter as generic type?

CodePudding user response:

Your fundamental issue is that you need an instance of MyContext at the point where you want to create the TService, as long as you are using a UserService object.

One way to solve this is to pass in a Func<TService> to the UpdateStatus() method instead of specifying the new restraint:

public async Task UpdateStatus<TService, T>(int id, int status, Func<TService> serviceCreator)
    where TService : StatusService<T>
{
    await serviceCreator().UpdateStatus(id, status);
}

Then the implementation of UpdateUserStatus() would look something like this:

public async Task UpdateUserStatus(int id, int status)
{
    var context = new MyContext(); // However you obtain this.
    await UpdateStatus<UserService, User>(id, status, () => new UserService(context));
}

The issue you then face is how to obtain the MyContext instance that you need for creating the UserService object. You can't avoid the need for the MyContext instance because the UserService requires it when you create it.

Here's a runnable example on DotNetFiddle.

You can take a further step of injecting a delegate with which to create the MyContext object:

public async Task UpdateUserStatus(int id, int status, Func<MyContext> contextProvider)
{
    await UpdateStatus<UserService, User>(id, status, () => new UserService(contextProvider()));
}

Now we've pushed the MyContext creation to the outer level. The outer level must still be able to obtain or create a MyContext, of course.

Here's a runnable example with those changes on DotNetFiddle.

CodePudding user response:

Looks like you're set up to use DI in your project, so you should use it.

In your Program.cs, register your classes that implement your base class (which could be better as an interface), eg:

builder.Services.AddTransient<StatusService<User>, UserService>();

Now instead of having a separate task to update the status, just pass your expected service into your constructor via DI which will automatically include the context:

public class WhatIsYourClassCalled
{
    private readonly StatusService<User> _statusService;

    public WhatIsYourClassCalled(StatusService<User> statusService)
    {
        _statusService = statusService;
    }

    public async Task UpdateUserStatus(int id, int status)
    {
        await _statusService.UpdateStatus(id, status);
    }
}
  • Related