Creating Repository Pattern that provides multiple DbContexts with Entity Framework in .Net Core Web API - entity-framework-core

I want to develop a structure that will support generic DbContexts in the .Net Core Web API project and can be used in the repository pattern. Mysql and PostreSql databases are sufficient for now. Can you help with this?

Create a new .Net Core Web API project.
Add a new folder in the project called DataAccess and create a new class called BaseDbContext that inherits from DbContext. This class will contain the common properties and methods for all your DbContexts.
public class BaseDbContext : DbContext
{
public BaseDbContext(DbContextOptions options) : base(options) { }
//...
}
Create a new class called MySqlDbContext that inherits from BaseDbContext. This class will contain the properties and methods specific to the MySQL database.
public class MySqlDbContext : BaseDbContext
{
public MySqlDbContext(DbContextOptions<MySqlDbContext> options) : base(options) { }
//...
}
Create a new class called PostgreSqlDbContext that inherits from BaseDbContext. This class will contain the properties and methods specific to the PostgreSQL database.
public class PostgreSqlDbContext : BaseDbContext
{
public PostgreSqlDbContext(DbContextOptions<PostgreSqlDbContext> options) :
base(options) { }
//...
}
Create a new folder in the project called Repositories and create a new class called BaseRepository that will contain the common methods for all your repositories.
public class BaseRepository<T> where T : class
{
protected readonly DbContext _context;
public BaseRepository(DbContext context)
{
_context = context;
}
//...
}
Create new classes for each repository that inherits from BaseRepository and pass the appropriate DbContext to the base constructor.
public class MySqlRepository : BaseRepository<MySqlDbContext>
{
public MySqlRepository(MySqlDbContext context) : base(context) { }
//...
}
and
public class PostgreSqlRepository : BaseRepository<PostgreSqlDbContext>
{
public PostgreSqlRepository(PostgreSqlDbContext context) : base(context) { }
//...
}
In your controllers you can now inject the appropriate repository and use it to interact with the database.
You can also use dependency injection to inject the appropriate DbContext based on the configuration.
Additional:
Here is an example of how you can do this:
In your appsettings.json file, add a section for the database connection information, such as:
{
"ConnectionStrings": {
"MySqlConnection": "Server=localhost;Database=mydb;User=user;Password=password;",
"PostgreSqlConnection": "Host=localhost;Database=mydb;Username=user;Password=password;"
},
"DatabaseProvider": "MySql"
}
Here the DatabaseProvider field indicate the database that user wants to use.
2. In your Startup.cs file, create a new method called ConfigureDbContext that will configure the DbContext based on the configuration in the appsettings file
public void ConfigureDbContext(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("MySqlConnection");
var provider = Configuration.GetValue<string>("DatabaseProvider");
if(provider == "MySql")
{
services.AddDbContext<MySqlDbContext>(options => options.UseMySql(connectionString));
}
else if (provider == "PostgreSql")
{
services.AddDbContext<PostgreSqlDbContext>(options => options.UseNpgsql(connectionString));
}
}
In the ConfigureServices method in Startup.cs, call the ConfigureDbContext method to configure the DbContext.
public void ConfigureServices(IServiceCollection services)
{
ConfigureDbContext(services);
//...
}
In your controllers, you can now inject the appropriate DbContext using dependency injection.
public class MyController : Controller
{
private readonly IDbContext _context;
public MyController(IDbContext context)
{
_context = context;
}
//...
}

Related

Can i Pass DbContext as interface or delegate

I am having two dbcontext so want to pass it though some function so I can switch between the two dbcontext or I have to change in all the api controller
public class AccountClassesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public AccountClassesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/AccountClasses
[HttpGet]
public async Task<ActionResult<IEnumerable<AccountClass>>> GetAccountClass()
{
return await _context.AccountClass.ToListAsync();
}
don't want to call the ApplicationDbContext from controller call it through some function or something
I have implemented database connection for postgresql and sqlserver now for them each one creates a different type of migration in code first, so had to create two dbcontext, now I want to be able to switch between dbcontext when I using postgresql or sql server
Yes, you can create interfaces and modify your ApplicationDbContext to implement.
interface
public interface IAccountClass {
Task<IEnumerable<AccountClass>> GetAccountClass();
}
public class AppDbContext: DbContext, IAccount {
/* implementing interface
*/
public Task<IEnumerable<AccountClass>> GetAccountClass() {
return this.AccountClass.ToListAsync();
}
}
Inject DbContext instance casting as interface in your controller:
public AccountClassesController(IAccountClass accountClass)
{
_accountClass = accountClass;
}
// GET: api/AccountClasses
[HttpGet]
public async Task<ActionResult<IEnumerable<AccountClass>>> GetAccountClass()
{
return await this._accountClass.GetAccountClass();
}
You must configure you dependency injection framework to inject a new ApplicationDbContext as IAccountClass.
You can check this answer here is it a good practice to pass an EF DbContext as delegate in services to attach all changes on it.
But if you application don't have any performance issues is fine even if you instance new context due EF is incredible fast managing and building it up.

Set provider name manually in DbContext constructor

I need to store the connection string somewhere else other than web.config. Currency I am using a .json file to store it and a static class to read from it.
Here is my data context -
public partial class MyDatabaseContext : DbContext
{
public MyDatabaseContext() : base()
{
Database.Connection.ConnectionString = GlobalConfig.ConnectionString;
}
}
This is GlobalConfig class
public static class GlobalConfig
{
public static string ConnectionString
{
get
{
return "Server=MyServer; Database=MyDb; Integrated Security=SSPI;";
}
}
}
I need to assign provider name System.Data.SqlClient to the context. How can I do that here?
Its possible to load DbConfiguration class at the DbContext Constructor
Define a class that implement DbConfiguration (Oracle in this case)
public class AppDbConfiguration : DbConfiguration
{
public AppDbConfiguration()
{
// use code based configuration
SetDefaultConnectionFactory(new OracleConnectionFactory());
SetProviderServices("Oracle.ManagedDataAccess.Client",EFOracleProviderServices.Instance);
SetProviderFactory("Oracle.ManagedDataAccess.Client", new OracleClientFactory());
}
}
Use it in your context
public AppDbContext(string connString) : base(connString)
{
DbConfiguration.SetConfiguration(new AppDbConfiguration());
}
Don't forget to install the currect Package for your provider (in this example its ODP.net from nuget package manager)

Architectural decision regarding Repository pattern (it is using Automapper)

I have:
Domain models (Model)
Database entities (Entity)
Repository: that accepts the model and converts it to the database entity (using automapper) and saves it to the database. In some cases returns back a Model object.
Example:
public class BaseRepository<T, U> : IRepository<T, U>
{
public void Insert(T model)
{
U entity= Mapper.Map<T, U>(model);
dbContext.Set<U>().Add(entity);
dbContext.SaveChanges();
}
}
Now when creating repository objects from a business layer i would instantiate the repository as:
new BaseRepository<Model, Entity>()
PROBLEM
Now this requires that the business layer have access to both the model and database entity projects. I want to avoid the reference of database entity to the business layer. My business layer should be able to instantiate repositories using only the domain model.
new BaseRepository<Model>()
for which i need a repository as
public class BaseRepository<T> : IRepository<T>
But then i cant find a way to handle the mapping between model and entity(automapper).
Is what i am asking valid? OR is my requirement absurd?
Note: I think my business layer should not have a reference to database entities because i do not want anyone using database entities directly. They should be working only with the model classes.
So the answer is simple. Bit of a silly question to begin with. The declaration for IRep was wrong.
BaseRep<T,U>:IRep<T> instead of IRep<T, U>.
The rep interface should be:
public interface IRepository<T>
where T : class
{
void Insert(T model);
IEnumerable<T> GetAll();
}
The base repository should be:
public class BaseRepository<T, U> : IRepository<T>
where T : class
where U : class
{
protected readonly IPARS_ADOEntities dbContext;
public BaseRepository()
: this(new IPARS_ADOEntities())
{
}
public BaseRepository(IPARS_ADOEntities dbContext)
{
this.dbContext = dbContext;
}
public void Insert(T model)
{
U entity = Mapper.Map<T, U>(model);
dbContext.Set<U>().Add(entity);
dbContext.SaveChanges();
}
public IEnumerable<T> GetAll()
{
IEnumerable<U> entities = dbContext.Set<U>();
return Mapper.Map<IEnumerable<U>, IEnumerable<T>>(entities);
}
}
In the business layer:
public class BizLayer
{
public List<EmployeeModel> GetEmployee(IRepository<EmployeeModel> rep)
{
return rep.GetAll();
}
}
From the presentation or application root you inject the dependency:
bizlayer.GetEmployees(new IRepository<EmployeeModel, EmployeeEntity>())

Injecting DbContext into Repository class library

The projects in my solution are set up like this:
App.Data
App.Models
App.Web
In App.Data, I'm using Entity Framework to access my data with a bunch of Repositories to abstract interaction with it. For obvious reasons, I would like my App.Web to reference only the App.Data project and not Entity Framework.
I'm using Constructor Injection to give my Controllers a reference to a Repository container that looks like this:
public interface IDataRepository
{
IUserRepository User { get; set; }
IProductRepository Product { get; set; }
// ...
}
public class DataRepository : IDataRepository
{
private readonly AppContext _context;
public DataRepository(AppContext context)
{
_context = context;
}
// ...
}
DataRepository will have a AppContext object (which inherits from Entity Framework's DbContext) that all the child Repositories will use to access the database.
So finally we come to my problem: how do I use Constructor Injection on DataRepository considering it's a code library and has no entry-point? I can't bootstrap AppContext in App.Web because then I have to reference Entity Framework from that project.
Or am I just doing something stupid?
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. This is a simplified example ...
public class RepositoryConnection
{
private readonly AppContext _context;
public RepositoryConnection()
{
_context = new AppContext();
}
public AppContext AppContext { get { return _context; } }
}
public class DataRepository : IDataRepository
{
private readonly AppContext _context;
public DataRepository(RepositoryConnection connection)
{
_context = connection.AppContext;
}
// ...
}

Dispose Context in EF Generic Repository

I have the following generic repository:
public class EFRepository<TEntity, TContext> : IRepository<TEntity, TContext>, IDisposable
where TEntity : class
where TContext : ObjectContext
{
protected TContext context;
public EFRepository(TContext context)
{
this.context = context;
}
//CRUD methods...
public void Dispose()
{
if (null != context)
{
context.Dispose();
}
}
}
This is a class from the Business layer
public class UserBLL : BaseBLL<User>
{
EFRepository<User, MyEntities> userRepo = null;
public UserBLL() : base ()
{
//Context is created in the consructor of the base class and passed to repository
userRepo = new EFRepository<User, MyEntities>(Context);
}
}
Here is the base business class:
public class BaseBLL <TEntity>
where TEntity : class
{
protected MyEntities Context { get; set; }
public BaseBLL()
{
this.Context = DataAccessHelper.Context;
this.Context.MetadataWorkspace.LoadFromAssembly(typeof(TEntity).Assembly);
}
}
In this design, since I'm creating an instance of the repository in the business class constructor rather than inside a using clause, the dispose method of the repository is not getting called by default. My main question is how to make sure the context/repository is disposed.
I know I can create the repository in a using clause inside each method rather than in the constructor, but I wonder if there's a more elegant way.
Feel free to comment about the design in general as well.
Wrap Dbcontext with UnitOfWork and inside of UnitOfWork implement dispose method.
Reference : http://elegantcode.com/2009/12/15/entity-framework-ef4-generic-repository-and-unit-of-work-prototype/
It is completely wrong. You are creating context outside of the repository so repository cannot be responsible for the disposal. The layer where the repository is constructed for the disposal = BaseBLL must be disposable and upper layer must dispose it correctly when it doesn't need it any more.