Kiss Koppány Kiss Koppány - 2 months ago 21
C# Question

ASP.NET CORE DI in model

I'm trying to understand how DI works in the new ASP Core. With a tutorial, I made it work for controllers, but can't make it work for models. For example, I have an AuthController, and I injected the database context to it, but now, as I have more controllers, sharing the same model, which is

Authentication
, I'd like to inject the context to the model itself. Here's some piece of code I've:

From the
Startup.cs


public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<GameContext>(options => options.UseSqlServer(@"Data Source=DESKTOP-USER\SQLEXPRESS;Initial Catalog=Db7;Integrated Security=True;Connect Timeout=30;"));
}


And here's how I used it from controllers:

[Route("api/[controller]")]
public class AuthController : Controller
{
public GameContext db;
public AuthController(GameContext context)
{
db = context;
}

[HttpPost]
[Route("login")]
public LoginResponseModel Login([FromBody] LoginModel user) //public Models.VM.LoginModel Login([FromBody] Models.VM.LoginModel user)
{
//query user
var detectedUser = db.Users.FirstOrDefault(u => u.Email == user.Email && u.Password == HelperClass.Md5(user.Password);


If I remove the context part from the controller, and move it to the model, I won't be able to instatntiate it again as the constructor needs an argument (which would be injected automatically?)

public class Authentication
{
public GameContext db;

public Authentication(GameContext context)
{
db = context;
}
...


How could I reach the database from the model?

EDIT:

This is how my Authentication class would look like (constructor may look different based on the solution):

public class Authentication
{
public GameContext db;

public Authentication(GameContext context)
{
db = context;
}

public LoginResponseModel Login(LoginModel user)
{
//query user
var detectedUser = db.Users.FirstOrDefault(u => u.Email == user.Email && u.Password == HelperClass.Md5(user.Password));


And here's how I would like to use this model from controllers:

[Route("api/[controller]")]
public class AuthController : Controller
{

public AuthController(GameContext context)
{
}

// POST api/login
[HttpPost]
[Route("login")]
public LoginResponseModel Login([FromBody] LoginModel user) //public Models.VM.LoginModel Login([FromBody] Models.VM.LoginModel user)
{
Authentication auth = new Authentication(); //throws error since no parameter passed

return auth.Login(user);
}

Answer

You basically making a reusable service for other controllers.

start by creating an abstraction of the functionality you want.

public interface IAuthenticationService {
    LoginResponseModel Login(LoginModel user);
}

and have the implementation inherit from this interface

public class Authentication : IAuthenticationService {
    private readonly GameContext db;

    public Authentication(GameContext context) {
        db = context;
    }

    public LoginResponseModel Login(LoginModel user) {
        //query user
        var detectedUser = db.Users.FirstOrDefault(u => u.Email == user.Email && u.Password == HelperClass.Md5(user.Password));
        //...other code that creates and returns an instance of LoginResponseModel
    }
}

with that in place you need to register the interface with the service collection so that the DI framework is aware of what to inject whenever it sees that interface.

Source: Asp.Net Core Fundamentals: Dependency Injection

public void ConfigureServices(IServiceCollection services) {
    ...
    services.AddDbContext<GameContext>(options => options.UseSqlServer(@"Data Source=DESKTOP-USER\SQLEXPRESS;Initial Catalog=Db7;Integrated Security=True;Connect Timeout=30;"));
    // Add application services.
    services.AddTransient<IAuthenticationService, Authentication>();
}

and now any controller that needs access to the service can have it injected into its constructor

[Route("api/[controller]")]
public class AuthController : Controller {
    private readonly IAuthenticationService auth;

    public AuthController(IAuthenticationService auth) {
        this.auth = auth;
    }

    // POST api/login
    [HttpPost]
    [Route("login")]
    public LoginResponseModel Login([FromBody] LoginModel user) {
        return auth.Login(user);
    }
}

The DI framework will handle all the heavy lifting of creating and injecting the service. You now don't have to create the instances yourself.