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>>();
Related
Entity framework
I have to insert the data from code to Db and I have to create POST method. For this I have made the Employee controller but I am getting some error in code. This is my code:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using VENUS.HRMS.DATA.Models;
namespace VENUS.HRMS.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
[HttpPost]
public TblEmployee InsertTblEmployee(TblEmployee _tblemployee)
{
using (TblEmployeesEntities entities = new TblEmployeesEntities())
{
entities.TblEmployees.Add(_tblemployee);
entities.SaveChanges();
}
return _tblemployee;
}
}
}
I am getting error on TblEmployeesEntities entities = new TblEmployeesEntities.
Please help me out.
I guess this sample code could help you. Write your DBcontext like below:
public partial class TblEmployeesEntities : DbContext
{
public TblEmployeesEntities ()
{ }
public TblEmployeesEntities (DbContextOptions<TblEmployeesEntities> options)
: base(options)
{ }
public virtual DbSet<TblEmployee> TblEmployees{ get; set; }
}
Then it's time to inject dbcontext in the startup if your DBMS is SQL server.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TblEmployeesEntities >(options =>
options.UseSqlServer("ConnectionString"));
}
Now just create a constructor in the controller and use DbContext:
private readonly TblEmployeesEntities _context;
public EmployeeController (TblEmployeesEntities context)
{
_context = context;
}
[HttpPost]
public TblEmployee InsertTblEmployee(TblEmployee _tblemployee)
{
_context.TblEmployees.Add(_tblemployee);
_context.SaveChanges();
return _tblemployee;
}
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.
I am using ASP.NET Core 2.0
At Startup.cs I have
services.AddDbContext<MailDBServicesContext>(optionsSqLite =>
{
optionsSqLite.UseSqlite("Data Source=Mail.db");
});
I have created a model and a DbContext where DbContext is:
public class MailDBServicesContext : DbContext
{
public MailDBServicesContext(DbContextOptions<MailDBServicesContext> options)
: base(options)
{
}
public DbSet<MailCountSentErrorMails> DbSetMailCountSentErrorMails { get; set; }
}
from a Class helper I need to pass DbContextOptions and my question is how can I tell to use the options from the Startup.cs ConfigureServices method
using (var db = new MailDBServicesContext())
{
}
It should be enough to simply inject MailDBServicesContext into your controller or a service class, for example.
public class SomeDataService
{
private readonly MailDBServicesContext _dbContext;
public SomeDataService(MailDBServicesContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task AddMailCounts()
{
_dbContext.DbSetMailCountSentErrorMails
.Add(new MailCountSentErrorMails { CountSentMails = 55 });
await _dbContext.SaveChangesAsync();
}
}
Other DB context configuration options are defined in Configuring a DbContext on MSDN.
Update
Make sure to register your service in DI, i.e. ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeDataService, SomeDataService>();
services.AddDbContext<MailDBServicesContext>(optionsSqLite =>
{
optionsSqLite.UseSqlite("Data Source=Mail.db");
});
services.AddMvc();
}
Then make a call to AddMailCounts() in your controller.
public class HomeController : Controller
{
private readonly ISomeDataService _dataService;
public HomeController(ISomeDataService dataService)
{
_dataService = dataService ?? throw new ArgumentNullException(nameof(dataService));
}
public IActionResult Index()
{
_dataService.AddMailCounts();
return View();
}
}
Now every time you load homepage, a record is inserted into DbSetMailCountSentErrorMails table.
You can find working solution on my GitHub.
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();
Using a combination provided from this example and this implementation I am trying to create a solution that decouples the UnitOfWork class from the individual repositories, as they violate the Open-Closed Principle, since every time you added a new repository you would have to modify the UnitOfWork class. I am using Unity as the IoC container to wire up dependencies.
The problem I have is that in automatically wiring up the UnitOfWork, IDbContext and the repositories (IEmployeeRepository and ICustomerRepository) using Unity, the repositories will be injected with separate instances of the UnitOfWork, which, of course, defeats the purpose. I need to share the context across the repositories, and it seems I am missing a piece to this puzzle - at the moment (see Service layer) the UnitOfWork instantiated will be different to the UnitOfWork for each of repositories.
How do inject the IUnitOfWork into the service layer and pass this instantiated shared UnitOfWork class to the respective repositories, using Unity and dependency injection?
Here's my proposed (fabricated) solution:
Repositories
public interface IRepository<TEntity> where TEntity : class
{
TEntity Create();
// omitted for brevity
}
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
private readonly DbContext _context;
public Repository(IUnitOfWork uow)
{
_context = uow.Context;
}
public virtual TEntity Create(TEntity entity)
{
return _context.Set<TEntity>().Add(entity);
}
// omitted for brevity
}
public interface IEmployeeRepository : IRepository<Employee>
{
}
public interface ICustomerRepository : IRepository<Customer>
{
}
public class EmployeeRepository : Repository<Employee>
{
public EmployeeRepository(IUnitOfWork uow)
: base(uow)
{
}
}
public class CustomerRepository : Repository<Customer>
{
public CustomerRepository(IUnitOfWork uow)
: base(uow)
{
}
}
DbContext Factory
public interface IDbContextFactory
{
DbContext GetContext();
}
public class DbContextFactory : IDbContextFactory
{
private readonly DbContext _context;
public DbContextFactory()
{
_context = new MyDbContext("ConnectionStringName");
}
public DbContext GetContext()
{
return _context;
}
}
Unit Of Work
public interface IUnitOfWork
{
void SaveChanges();
DbContext Context { get; }
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
private readonly DbContext _context;
private bool disposed = false;
public UnitOfWork(IDbContextFactory contextFactory)
{
_context = contextFactory.GetContext();
}
public void SaveChanges()
{
if (_context != null)
{
_context.SaveChanges();
}
}
public DbContext Context
{
get { return _context; }
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
_context.Dispose();
}
}
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Service
public class CompanyService
{
private readonly IUnitOfWork _uow;
private readonly IEmployeeRepository _employeeRepository;
private readonly ICustomerRepository _customerRepository;
public CompanyService(IUnitOfWork uow, IEmployeeRepository employeeRepository, ICustomerRepository customerRepository)
{
_uow = uow;
_employeeRepository = employeeRepository;
_customerRepository = customerRepository;
}
// over-simplified example method
public void AddEmployeeAndCustomer()
{
_employeeRepository.Create(new Employee {Id = 1, Name = "Test Employee"});
_customerRepository.Create(new Customer { Id = 2, Name = "Test Customer" });
_uow.SaveChanges();
}
}
I think what you are looking for is a per request lifetime manager so that you only get one UnitOfWork instance and one DbContext instance for the duration of a request. Unity 3 has the Unity bootstrapper for ASP.NET MVC which has a PerRequestLifetimeManager which lets you do this.
If you are not using ASP.NET then you could probably use a PerResolveLifetimeManager. Another approach I've seen is a HierarchicalLifetimeManager combined with a child container (which makes the registrations a singleton within the child container).