Initializing DBContext in Asp.Net Core - c#

I want to use Repository & Unit Of Work in my project.
But in ASP.NET MVC when we want use DBContext to use this code
MyDbContext db=new MyDbContext();
but in ASP.NET Core when write this code it want an argument
because use this code in DbContext Class
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
Error:
what is the problem?

You can initilize your DB context like this:
var optionBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionBuilder.UseSqlServer("Server=localhost;...");
var context = new MyDbContext(optionBuilder.Options);
Previous code is configuring the options to the connection and then creating a MyDbContext using those options.
If you want to use a InMemoryDatabase for unit testing for example you can change it to this:
var optionBuilder = new DbContextOptionsBuilder<MyDbContext>().UseInMemoryDatabase("testindDB")`;

public MyDbContext(DbContextOptions<MyDbContext> options)
You have not empty constructor in your MyDbContext class, So you should do pass parameter DbContextOptions<MyDbContext> options in constructor.
For example you can see it -> link1

You shouldn't be instantiating the DbContext, you should be requesting it as a constructor argument to your repository. Then your IOC container will provide the DbContext at runtime. This ensures that you can use the same DbContext throughout a given ASP.NET web request, which will prevent a host of problems you're otherwise likely to encounter.
You can see an example of a generic repository here:
http://deviq.com/repository-pattern/
You also typically don't need a separate Unit of Work in ASP.NET applications (but sometimes you do). This is because your requests should be very small and you should be able to do most of the work in a single controller or service, and then simply save through the repository. It's not that you never need UoW, but it's less necessary than in a thick client scenario (e.g. windows app or service).

You can try it: In your class UnitOfWork
private MyDBContext _context;
public UnitOfWork(MyDBContext context)
{
_context = context;
}
In your controller:
private UnitOfWork _unitOfWork;
public MoviesController(MyDBContext context)
{
_unitOfWork = new UnitOfWork(context);
}

Related

How to use Dependency Injection on a application class in .Net Core for an Entity Framework Pooled Context to achieve multi-tenant implementation?

I have implemented an application which makes use of .Net Core 3.1 and Entity Framework.
The application uses entity framework dbcontext pooling, utilizing the Pomelo mysql ef library.
services.AddDbContextPool<myDbContext>(
options => options
.UseMySql(Configuration.GetConnectionString("DefaultConnection"),
mysqlOptions =>
{
mysqlOptions.MaxBatchSize(MySqlConfig.EfBatchSize);
mysqlOptions.EnableRetryOnFailure();
if (MySqlConfig.EfRetryOnFailure > 0)
{
mysqlOptions.EnableRetryOnFailure(MySqlConfig.EfRetryOnFailure, TimeSpan.FromSeconds(5), null);
}
}
).UseLoggerFactory(consoleLoggerFactory));
What is important to note is the use of the AddDbContextPool
Please see here: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.entityframeworkservicecollectionextensions.adddbcontextpool?view=efcore-3.1
When using Context Pooling, your application is required to have a single constructor with DbContextOptions. ie: I cannot inject another object (in my case, I need a application service class for listing allowed tenants, and other claims related logic...) into this class, otherwise pooling cannot be used.
public myDbContext(DbContextOptions<myDbContext> options)
: base(options)
{
}
Another caveat is that I utilize the HttpContextAccessor to access the claims on the User which is authenticated, which include the Tenants allowed to be accessed by that user which of course is accessed via Dependency Injection as well.
The Authentication and Claims are retrieved via openid ultimately retrieving claims as Active Directory Groups, so I do not and would not have this info in the DB either as it originates from Active Directory...
The guts of this question is really, how can i access the HttpContext within my DbContext in order to implement a global filter for multi-tenant support in my application, and still use the DB Context Pooling.
I can achieve this by removing pooling, and make use of DI as normal via the constructor, but that is not what i'm after. I need to keep pooling, and implement the multi-tenant feature.
First, if your tenants are in different databases, DbContext pooling simply won't work. After a DbContext has been opened, it's connected to a single database. It's a minor performance feature, so it's not a great loss.
If you're setting query filters, you can probably make it work by simply not directly injecting a DbContext into your controllers, instead injecting a pre-configured bundle of services. And this also lets you centralize the code to rreconfigure the DbContext for the current tenant. EG:
public class ServiceContext
{
MyDbContext dbContext;
HttpContext httpContext;
IConfiguration config;
public ServiceContext(MyDbContext dbContext, IHttpContextAccessor httpContextAccessor, IConfiguration config)
{
this.dbContext = dbContext;
this.httpContext = httpContextAccessor.HttpContext;
this.config = config;
//use the httpContext to reconfigure the DbContext for single-tenant access
}
public HttpContext HttpContext { get => httpContext; set => throw new NotImplementedException(); }
public MyDbContext DbContext { get => dbContext; }
public IConfiguration Configuration { get => config; }
}

How can I add a dbContext after Startup in .Net Core?

I am using .Net Core, using the built-in dependency injection. In my login screen, I need the user to also choose a departmental database - we have different databases with the same structure to use the same application with different data. However, I can't figure out how to add/modify the dbContext that late. Startup.cs has the DI, but I don't know which connection string to read from the config until the user has chosen the department. It is a small database, and the company is not concerned about the management of the duplicate databases.
How can I add the service late
services.AddDbContext<my_accountingContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("CorrectDepartmentConfig")));
when I actually know what CorrectDepartmentConfig is?
Or, if that can't be done, how can I do a smelly change of the my_accountingContext after Startup.cs?
You can use an implementation factory overload of IServiceCollection in ConfigureServices method form Startup class:
//First register a custom made db context provider
services.AddTransient<ApplicationDbContextFactory>();
//Then use implementation factory to get the one you need
services.AddTransient(provider => provider.GetService<ApplicationDbContextFactory>().CreateApplicationDbContext());
The implementation of CreateApplicationDbContext depends on your specific needs, but a base implementation should look like the following:
public ApplicationDbContext CreateApplicationDbContext(){
//TODO Something clever to create correct ApplicationDbContext with ConnectionString you need.
}
After this implementation, you can inject the correct ApplicationDbContext in your controller, action...
public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public IActionResult([FromServices] ApplicationDbContext dbContext){
}
You can always set the connection string from inside the protected OnConfiguring method. You can get access to the IConfiguration instance from there (the DbContext class has a service locator, Instance property), retrieve the connection string, and then call UseMySql extension method with the appropriate connection.
Something like this:
protected virtual void OnConfiguring(DbContextOptionsBuilder builder)
{
var configuration = (this as IInfrastructure<IServiceProvider>).GetService<IConfiguration>();
var connectionString = configuration.GetConnectionString("<name>");
builder.UseMySql(connectionString);
base.OnConfiguring(builder);
}
For the strongly-typed version of GetService do not forget to reference namespace Microsoft.Extensions.DependencyInjection.

Resolve DBContext with implements another interface in Asp.Net Core

I have an EF Context that has it's signature
public class MyContext : DbContext, IDbContext
{
}
When I add it to services, I use it
services.AddDbContext<MyContext>(op =>
{
op.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
});
But it's causing problems when I'm injecting the IDbContext, like this
services.AddScoped(typeof(IDbContext), typeof(MyContext));
Because it's duplicating my DbContext, and It should be only one per request.
How can I resolve it?
In your case using the factory method should work fine.
services.AddScoped<IDbContext>(provider => provider.GetService(typeof(MyContext)));
This way you will resolve a new instance of MyDbContext (on first call) or return the already instantiated instance of it during a request on conclusive calls.
Please note, that if you are registering multiple different DbContexts, you will have to correctly define the constructors using the correct specific DbContextOptions. If you don't you run the risk of having the incorrect type being resolved by the DI.
For example, I was using an MyContext : DbContext, IDbContext, similar to OP, but I was also using a generic DbContext for temporary storage for OpenIddict:
services.AddDbContext<DbContext>(o =>
{
o.UseInMemoryDatabase(nameof(DbContext)); //tokens and stuff is stored in memory, not the actual users or passwords.
o.UseOpenIddict();
});
Crucially, my MyContext constructor was too generic - created using a Visual Studio Quick Action template :( ugh
Wrong:
public MyContext (DbContextOptions options) : base(options) { ... }
This results in the DI resolving the wrong DbContext at runtime:
Microsoft.EntityFrameworkCore.Infrastructure:Information: Entity Framework Core 2.1.4-rtm-31024 initialized 'MyContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContext
At some point I got an error which complained that I didn't have the right type of constructor for my context, so I changed the constructor to:
Correct:
public MyContext (DbContextOptions<MyContext> options) : base(options) { ... }
and now it works right.

ASP.NET Core Dependency Injection, inject with parameters

In an ASP.NET Core 1.0 project, using DI how can I pass parameters to constructor. For instance, how do I register the following service in Startup.cs. services.AddTransient(typeof(IStateService), new StateService()); does not work since StateService() requires an input parameter of type BlogingContext. Or, are there alternative way of building the following service with database involved? Here State is a table coming from SQL Server Db. App is using EntityFrameworkCore with Code First approach. I'm using latest release of ASP.NET Core 1.0 and VS2015-Update 3 released on June 27, 2016
I see a similar example here but not quite the same type of input parameter.
Service:
public interface IStateService
{
IEnumerable<State> List();
}
public class StateService : IStateService
{
private BloggingContext _context;
public StateService(BloggingContext context)
{
_context = context;
}
public IEnumerable<State> List()
{
return _context.States.ToList();
}
}
As documentation states here (Scroll a bit down) you should register the IStateService and BloggingContext like:
services.AddDbContext<BloggingContext>();
services.AddScoped<IStateService, StateService>();
Then DI will resolve the whole dependency tree for you. Note that you should use scoped lifetime on service, because the service should use same lifetime as DbContext and it uses scoped.

MVC Multitenant Application Instance Scope on Context using Autofac?

Hi everyone I am optimizing my Application and have a Question about it. I have a Design that looks like this:
ApplicationCore -> T4 from ApplicationDatabases Model Generator
ApplicationData -> Respositories which Access the Database Context in ApplicationCore
public class entityRepository<TEntity> : entityRepository<TEntity> where TEntity : class, IEntity
{
protected readonly DatabaseContext dbContext;
protected readonly IDbSet<TEntity> currentTableContext;
public entityRepository(DatabaseContext context)
{
this.DbContext = context;
currentTableContext = context.Set<TEntity>();
}
ApplicationDatabases -> Database created from Model Generator
ApplicationServices -> Services that creates a entityRepository
private readonly entityRepository<Users> _userRepository;
public userService(entityRepository<Users> userRepository)
{
_userRepository = userRepository;
}
What I would like to know if I am allowed to Register the entityRepository as InstancePerTenant in Autofac or should I use another Instance Scope.
builder.Register(context => new DatabaseContext()).InstancePerTenant();
builder.RegisterGeneric(typeof(entityRepository<>)).As(typeof(IentityRepository<>)).InstancePerTenant;
I am doing that at the moment and it works but I am not sure If I would get any Resources or similiar Problems later. I would also be happy if I get some suggestions.
IMHO, I have worked in a project that is multi-tenant, but does not use autofac. I have few points to put forth to you for a discussion
In case of a multi-tenant application and having the user and entity repositories loaded on memory for each tenant, seems like a bit of overhead
In case of a use case wherein a tenant wishes to view his child tenant's data, how can this be achieved
When there is an application load of 50 tenant's what happens to the parallelism and responsiveness to the load from the application point of view.
Kindly think through these use cases and the others that might come up after moving to production and share your thoughts.

Categories

Resources