multiple connection string same context - entity-framework-core

I need Help configuring an asp.net core 2.0 angular project with entity-framework 2.0, I need help to implement in the controller when user call an API like ChangeDatabase(string databaseConexionString), then all the request made for that user will use the new connection string instead.
[Produces("application/json")]
[Route("api/admin")]
public class AdminController : Controller
{
private readonly DatabaseContext _context;
private readonly IMapper _mapper;
public AdminController(DatabaseContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
[HttpGet("[action]")]
public IActionResult ChangeDatabase(string databaseConexionString)
{
//change conexion string...
return Ok("changed!");
}
}
My startup.cs file, the configure services is the default
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
I just want to change between databases and keep the same context class because the tables are the same, the only difference is the data.

Related

Pass DbContext to class library

I am developing an API to get data from the database using the entity framework. I have a class library to handle my generic tasks, including Repository, UnitOfWork etc. My UnitOfWork class is as follows.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using AppPermission.Data.DataContext;
using AppPermission.Data.Models;
using AppPermission.Data.Repositories;
namespace AppPermission.Common.UnitOfWork
{
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext dbContext;
public UnitOfWork(DbContext context)
{
dbContext = context;
}
public int SaveChanges()
{
return dbContext.SaveChanges();
}
public async Task<bool> SaveChangesAsync()
{
return await dbContext.SaveChangesAsync() > 0;
}
public void Dispose()
{
dbContext.Dispose();
GC.SuppressFinalize(this);
}
}
}
My API's ConfigureServices is as below
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>();
services.AddSession();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddScoped<IUnitOfWork, UnitOfWork>();
}
I want to pass AppDbContext registered in the API startup to UnitOfWork in the class library. There were a couple of solutions in StackOverflow using(services.BuildServiceProvider), but the connection is disposed of after the first API call (GetAll). Is there any way of doing it? If I place my UnitOfWork in the API project itself and changing the constructor in UnitOfWork to accept AppDbContext, it works fine?
It's a good idea to use Generic UnitOfWork :
public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext
class constructor :
public UnitOfWork(TContext context, ILogger<UnitOfWork<TContext>> logger){}
Then add it to IServiceCollection like this :
services.AddScoped<IUnitOfWork, UnitOfWork<MonitoringDbContext>>();

Inheriting logging services from base class

I have a .Net Core 3.1 API controller with a constructor that looks like this:
public class MachineListsController : ControllerBase
{
private readonly jiWeb_ProdContext _context;
private readonly ILogger _logger;
private readonly ILoggingMessageService _loggingMessage;
public MachineListsController(jiWeb_ProdContext context, ILogger<MachineListsController> logger, ILoggingMessageService loggingMessage)
{
_context = context;
_logger = logger;
_loggingMessage = loggingMessage;
}
public string Message { get; set; }
...
}
You can see that I am injecting a .Net Core logging service and the database context into it.
Then I use the logging like this in my controller methods:
[HttpGet("FactoryMachines/{factoryId}")]
public async Task<ActionResult<IEnumerable<MachineList>>> GetMachinesForFactory(Guid factoryId)
{
var machineList = await _context.MachineList.Where(n => n.FactoryId == factoryId).ToListAsync();
Message = _loggingMessage.GetLogSuccess(this.GetType().Name.ToString(), ControllerActions.GetAction, "FactoryMachines", factoryId.ToString());
_logger.LogInformation(Message);
return machineList;
}
The logging is working great, but I'm realizing that I should create a base class that handles logging so I don't have to add or change it inside of every controller I write.
So I started to write this base controller:
[ApiController]
public class MyBaseController : ControllerBase
{
readonly jiWeb_ProdContext _context;
readonly ILogger _logger;
readonly ILoggingMessageService _loggingMessage;
public BaseController(jiWeb_ProdContext context, ILogger<BaseController> logger, ILoggingMessageService loggingMessage)
{
_context = context;
_logger = logger;
_loggingMessage = loggingMessage;
}
}
Then I changed my controller to inherit from it like this:
public class MachineListsController : MyBaseController
{
[HttpGet("FactoryMachines/{factoryId}")]
public async Task<ActionResult<IEnumerable<MachineList>>> GetMachinesForFactory(Guid factoryId)
{
var machineList = await _context.MachineList.Where(n => n.FactoryId == factoryId).ToListAsync();
return machineList;
}
}
But I'm getting error and I'm unsure of what to do on the next step.
Here's the error:
There is no argument given that corresponds to the required formal parameter 'context' of 'BaseController.BaseController(jiWeb_ProdContext, ILogger<BaseController>, ILoggingMessageService)'
Specifically, how do I set up my controllers so that they can just use the base class for logging so I don't have to write logging code for every new controller action I create?
Thanks!
As far as I know, if the base class constructor method contains value, we should pass it in the subclass constructor method and also you should follow Nkosi comment to modify the property to protected.
More details, you could refer to below codes:
[ApiController]
public class MyBaseController : ControllerBase
{
protected readonly ILogger _logger;
public MyBaseController(ILogger<MyBaseController> logger)
{
_logger = logger;
}
}
[Route("api/[controller]")]
public class MachineListsController : MyBaseController
{
public MachineListsController(ILogger<MyBaseController> logger) :base(logger)
{
}
[HttpGet]
public IActionResult Get() {
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace,"aaa" );
return Ok();
}
}
I am wondering, would there be a way to do the logging in the base class? Like where you call _logger.Log in the MachineListsController class, could that be moved to base?
As far as I know, we could only add logs before the MachineListsController's action executed or after the MachineListsController's action executed.
If this match your requirement, you could try to use action filter.
You could add iactionfilter interface to the basecontroller and overried the OnActionExecuted and OnActionExecuting method.
More details, you could refer to below codes:
[Route("api/[controller]")]
[ApiController]
public class MyBaseController : ControllerBase, IActionFilter
{
protected readonly ILogger _logger;
public MyBaseController(ILogger<MyBaseController> logger)
{
_logger = logger;
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace, "aaa");
int i = 0;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace, "bbb");
int i = 0;
}
}
Result:

Passing connection string to Entity framework at runt time for each call

My Entity framework context is as following
public partial class MyContext : DbContext, IMyContext
{
static MyContext()
{
System.Data.Entity.Database.SetInitializer<MyContext>(null);
}
public MyContext()
: base("Name=MyContext")
{
}
I am resolving it through autofac in the following way
builder.RegisterType(typeof(MainContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType<MainContext>().As<IMainContext>().InstancePerRequest();
This db context gets called in repository layer
#region Fields
private readonly IMyContext _context;
#endregion
#region Constructors and Destructors
public EmployeeRepository(IMyContext context)
{
_context = context;
}
#endregion
public void Create(Employee emp)
{
this._context.Employee.Add(emp);
}
Now my issue is , I want to set the connection string dynamically per call. The connection string will be passed through a webapi which i want to pass on to this context. Can anyone help me how can i do that? I am confused about autofac here. Secondly how can i make sure each call sets connection string and does not cache it.
You can use a factory that will build the context and set the connectionstring for you.
public interface IContextFactory
{
IContext GetInstance();
}
public class MyContextFactory : IContextFactory
{
public IContext GetInstance()
{
String connectionString = this.GetConnectionString(HttpContext.Current);
return new MyContext(connectionString);
}
private String GetConnectionString(HttpContext context)
{
// do what you want
}
}
builder.RegisterType<MyContextFactory>()
.As<IContextFactory>()
.InstancePerRequest();
builder.Register(c => c.Resolve<IContextFactory>().GetInstance())
.As<IContext>()
.InstancePerRequest();
If you can't get connectionstring based on HttpContext, you can change contextFactory implementation to expect initialization by WebAPI before creating the instance. For example :
public interface IContextFactory
{
IContext GetInstance();
void Initialize(String connectionString);
}
public class MyContextFactory : IContextFactory
{
private String _connectionString;
public void Initialize(String connectionString)
{
this._connectionString = connectionString;
}
public IContext GetInstance()
{
if (this._connectionString == null)
{
throw new Exception("connectionString not initialized");
}
return new MyContext(this._connectionString);
}
}
At the beginning of your web API call (through attribute for example), you can call the Initialize method. Because the factory is InstancePerRequest you will have one instance for the duration of the request.
By the way, I'm not sure to understand this registration
builder.RegisterType(typeof(MainContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType<MainContext>().As<IMainContext>().InstancePerRequest();
It looks buggy because you will have 2 different registration of the same type and not for the same scope, is it intended ? Furthermore, it doesn't sound a good idea to register a DbContext, do you need this registration ?
The following registration looks better :
builder.RegisterType<MainContext>()
.As<IMainContext>()
.As<DbContext>()
.InstancePerRequest();

Dependecy Injection works with Unity but doesn't with Ninject

I've been working on EF Repository pattern with Unit of Work and DI. I was following this example: link In example is used Unity as DI container, but I want to use Ninject.
(Note: with Unity everything works just fine).
So I've set up my Ninject Controller Factory like this: link.
When i go to my controller and try to list messages, it works fine but when i try to create message nothing happens, values are passed to controller and to repository, but data isn't saved to database for some reason.
Here is controller constructor:
private readonly IUserRepository _userRepository;
private readonly IMessageThreadRepository _messageThreadRepository;
public TetsController(IMessageThreadRepository messageThreadRepository, IUserRepository userRepository)
{
_messageThreadRepository = messageThreadRepository;
_userRepository = userRepository;
}
And here is code for messageThreadRepository which is being called by controller
public class MessageThreadRepository : RepositoryBase<MessageThread>, IMessageThreadRepository
{
private readonly RepositoryBase<MessageThread> _messageThreadRepository;
private readonly RepositoryBase<Message> _messageRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly UserRepository _userRepository;
public MessageThreadRepository(IUnitOfWork unitOfWork, RepositoryBase<MessageThread> messageThreadRepository,
IDatabaseContextFactory databaseContextFactory, UserRepository userRepository, RepositoryBase<Message> messageRepository)
: base(databaseContextFactory)
{
_unitOfWork = unitOfWork;
_messageThreadRepository = messageThreadRepository;
_userRepository = userRepository;
_messageRepository = messageRepository;
}
//some code here
// part with adding to database
Message message = messageFactory.CreateMessage(messageBody, userSender, messageThread);
_messageRepository.Add(message);
if (!doesThreadExist)
{
_messageThreadRepository.Add(messageThread);
}
_unitOfWork.Commit();
}
EDIT
I've tried to add User to database, but with no luck. Again I manage to fetch all users but I can't add them, here is User repository:
public class UserRepository : RepositoryBase<User>, IUserRepository
{
private readonly IUnitOfWork _unitOfWork;
private readonly RepositoryBase<User> _repository;
public UserRepository(IUnitOfWork unitOfWork, IDatabaseContextFactory databaseContextFactory, RepositoryBase<User> repository) : base (databaseContextFactory)
{
_unitOfWork = unitOfWork;
_repository = repository;
}
public User GetUserById(int id)
{
return _repository.GetSingleByCriteria(u => u.UserId.Equals(id));
}
public User GetUserByEmail(string email)
{
return _repository.GetSingleByCriteria(u => u.Email.Equals(email));
}
public User GetUserByUserName(string name)
{
return _repository.GetSingleByCriteria(u => u.UserName.Equals(name));
}
public void AddUser(User user)
{
_repository.Add(user);
_unitOfWork.Commit();
}
public IEnumerable<User> GetAllUsers()
{
return _repository.GetAll();
}
}
I assume you're using MVC since you reference an article about using it with MVC.
Did you do all the steps mentioned here?
http://bubblogging.wordpress.com/2012/06/04/mvc-controller-factory-ninject/
Why are you using a custom controller factory instead of using the built-in dependency injection support of MVC, using Ninject.MVC3 package?

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;
}
// ...
}