Reputation: 3129
I am new to ASP.Net Core and I am trying to implement ASP.NET Core DI.
I configured like below in ConfigureServices Method in Startup.cs
services.AddScoped<DbContext, AutomationDbContext>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IUserService, UserService>();
In UserService Constructor, I am trying to use DI. I think below is NOT the right way to implement this.
public UserService(IHttpContextAccessor httpContextAccessor, AutomationDbContext automationDbContext, IConfiguration configuration)
{
this.configuration = configuration;
this.optionsBuilder = new DbContextOptionsBuilder<AutomationDbContext>();
var connectionString = this.configuration.GetConnectionString("Automation");
this.optionsBuilder.UseSqlServer(connectionString);
this.automationDbContext = new AutomationDbContext(this.optionsBuilder.Options);
this.httpContext = httpContextAccessor.HttpContext;
}
I don't like building optionsbuilder in constructor and get connectionstring. What would be the better place to build these optionsBuilder and pass in constructor.
Upvotes: 1
Views: 1694
Reputation: 239220
You need to use services.AddDbContext<TContext>
instead:
services.AddDbContext<AutomationDbContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Automation")));
Then, just inject your context:
public UserService(IHttpContextAccessor httpContextAccessor, AutomationDbContext automationDbContext)
As for IHttpContextAccessor
, you should simply use:
services.AddHttpContextAccessor();
However, I would encourage you to strongly consider whether you actually need this in your service or not. If you need something like the current user's id, that should be passed into the method that needs it, not retrieved from within your service.
UPDATE
Since it was brought up, let me elucidate the reasons why adding your context in the way you currently are is incorrect, since it will shed a little light on how DI works in general.
First, you're binding DbContext
directly to AutomationDbContext
, which means you can then only use that one context. Maybe you don't need more than one context... now. That could change later. Second, when you register a service in that way, you can only inject the abstract type, i.e. DbContext
here. The service registration literally means "when you see DbContext
, inject an instance of AutomationDbContext
". If you try to inject AutomationDbContext
directly, as you're currently doing in your controller, that will actually throw an exception because that type is not actually registered as service: DbContext
is. Third, AddScoped
provides no real ability to configure the context, which is of course the part your were missing. There's ways to work around this such as using the factory overload of AddScoped
or defining OnConfiguring
on your context, but both of those are substandard to just using the right method in the first place: AddDbContext<TContext>
For what it's worth, there's also somewhat of a fourth reason, in that you can opt to use AddDbContextPool<TContext>
instead of AddDbContext<TContext>
, for connection pooling. There's no other way to set that up, so if you did want/need connection pooling, you'll never get there with AddScoped
.
Upvotes: 4