Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting DbContext into service layer

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?

like image 307
Danny Cullen Avatar asked Dec 09 '16 10:12

Danny Cullen


People also ask

How do you inject DbContext into Repository?

You can define a RepositoryConnection class in App. Data that acts as a wrapper to the Context and removes the need to reference EF in App. Web . If you are using an IoC Container you can control the lifetime of the RepositoryConnection class to ensure that all instances of Repository get the same Context.

Which method is used for adding DbContext class as service?

The AddDbContext extension method registers DbContext types with a scoped lifetime by default.

Should DbContext be in a using?

EF and EF Core DbContext types implement IDisposable . As such, best practice programming suggests that you should wrap them in a using() block (or new C# 8 using statement). Unfortunately, doing this, at least in web apps, is generally a bad idea.

How to include the dbcontext in a service?

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 :

How to set up dependency injection for dbcontext?

Create the DBContext class: Set up dependency injection by adding options.UseSqlServer () to ConfigureServices () in Startup.cs: Make DBContext accessible from within each controller by adding it to the controller class and setting it through the constructor:

How can I abstract the database access layer in dependency injection?

Microsoft recommends using the Repository pattern to further abstract the database access layer, in which case we would use the interface IProductsDb in the Dependency Injection instead of the concrete class ProductsDb.

How do I make dbcontext accessible from within the controller?

Make DBContext accessible from within each controller by adding it to the controller class and setting it through the constructor:


1 Answers

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(); 
like image 147
AdrienTorris Avatar answered Sep 18 '22 15:09

AdrienTorris