Injecting DbContext into service layer

asp.net-core-mvc c# entity-framework

Question

How am I supposed to inject my MyDbContext into my database service layer MyService?

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
    services.AddMvc();
}

MyDbContext.cs

public partial class MyDbContext : DbContext
{
    public virtual DbSet<User> User { get; set; }

    public MyDbContext(DbContextOptions<MyDbContext> options)
    :base(options)
    {
    }
}

appsettings.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=MyServer;Database=MyDatabase;user id=MyUser;password=MyPassword;"
  }
}

MyService.cs

public class MyService
{
    public User GetUser(string username)
    {
        // Should i remove this call and get the reference from the injection somehow?
        MyDbContext db_context = new MyDbContext(optionsBuilder.Options);
        using (db_context)
        {
            var users = from u in db_context.User where u.WindowsLogin == username select u;
            if (users.Count() == 1)
            {
                return users.First();
            }
            return null;
        }
    }
}

In my GetUser method, I know I am supposed to be using my injected MyDbContext here but I am not quite sure how to fetch it. Which piece of the puzzle am I missing?

1
17
12/9/2016 10:21:43 AM

Accepted Answer

You don't have to include the dbcontext yourself, ASP.NET core dependency injection service will do this for you.

You have just to declare your services and your database context in your startup class, and put the dbcontext you need in your service's constructor :

Startup.cs (your have to choose the service lifetime you want, here it's a scoped service, once per request):

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
    services.AddMvc();
    services.AddScoped<IMyService, MyService>();
}

Your service class:

public class MyService : IMyService
{
    private readonly MyDbContext _context;

    public MyService(MyDbContext ctx){
         _context = ctx;
    }

    public User GetUser(string username)
    {
        var users = from u in _context.User where u.WindowsLogin == username select u;
        if (users.Count() == 1)
        {
            return users.First();
        }
        return null;
    }
}

public interface IMyService
{
    User GetUser(string username);
}

In your controller, you have to declare the services (or the database context) you need to use in the same way :

public class TestController : Controller
{
     private readonly IMyService _myService;

      public TestController(IMyService serv)
      {
           _myService = serv;
      }

      public IActionResult Test()
      {
          return _myService.MyMethod(); // No need to instanciate your service here
      }
}

Note about the controller : you don't have to add them in your startup class like you do with your database context or your services. Just implement their constructor.

If you need more information about the .NET Core depencency injection, the official documentation is clear and very complete : https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection


NB: In the startup.cs, the AddScoped line is an option. Your can choose the lifetime you want for your service. There is the different lifetimes you can choose :

Transient

Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.

Scoped

Scoped lifetime services are created once per request.

Singleton

Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.

Above taken from: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection


Note: It's not the question here but your GetUser data query looks like a little strange to me. If your count()==1 goal is to check the unicity of the user, the good way is to add a unicity constraint in your database. If count()==1 goal is to check you have data to avoid a object null reference exception, you can use .FirstOrDefault(), it will manage this for you. You can simplify this method :

public User GetUser(string username) => (from u in _context.User where u.WindowsLogin == username select u).FirstOrDefault();
38
12/9/2016 11:19:44 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow