One DbContext Instance spans multiple Repositories - c#

These are my AutoFac-DI definitions in my Web Api setup:
builder.RegisterType<MyContext>().As<MyContext>().InstancePerRequest();
builder.RegisterType<TestRepository>().InstancePerRequest();
builder.RegisterType<SchoolclassCodeRepository>().InstancePerRequest();
builder.RegisterType<TestService>().InstancePerRequest();
The TestService constructor accepts the TestRepository and SchoolclassCodeRepository. Both Repositories accepts the same Instance of the MyContext.
I agree with this: Is it wise to use same DbContext with multiple repositories?
There are other good reasons to share a context though, one of which (IMHO) is that the context has to track the state of an entity, if you're get an entity, dispose the context, make some modifications to the entity, and then attach to a new context this new context has to go hit the database so it can figure out the state of the entity. Likewise if you're working with graphs of entities (Invoices, and all their InvoiceItems), then the new context would have to fetch all the entities in the graph to determine their state.
But now I hit a one-way street with this architecture!
What if I have to do a transaction spanning multiple repositories?
With EF6 you do it like that without Repositories:
using(NorthwindEntities db = new NorthwindEntities())
{
DbContextTransaction transaction = db.Database.BeginTransaction();
try
{
//insert record 1
Customer obj1 = new Customer();
obj1.CustomerID = "ABCDE";
db.Customers.Add(obj1);
db.SaveChanges();
//insert record 2
Customer obj2 = new Customer();
obj2.CustomerID = "PQRST";
db.Customers.Add(obj2);
db.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
}
When I take now the above sample and try to do the same with my 2 repositories within the service, then I face a serious problem.
I have no DbContext available in my Service.
The DbContext is a DataProvider/Layer concern and should stay inside the repository.
How can I create then a transaction over multiple repositories without changing my repositories?
Sample what I want:
Inside my TestService I want to do roughly:
public void Save()
{
// Open Transaction
// testRepo.Insert();
// schoolclassCodeRepo.Delete();
// Commit Transaction
}
UPDATE
In my TestService I map all entities from the repos to DTO objects which are then enriched by data + links (Rest) in my web api controllers.
UPDATE 2
The Repository pattern makes data access methods reusable thats good.
But it makes a transaction over multiple repositories sharing same DbContext not possible.
WOULD it not be better to implement all Repository methods as extension methods of the DbContext, this way I could call the "Repo extension methods" in my Service directly on the ONE DbContext injected into my TestService?
UPDATE 3 Solution code
public async Task<bool> DeleteSchoolyearAsync(int id)
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
try
{
await testRepository.DeleteTestTypes(id);
await schoolyearRepository.DeleteAsync(id);
scope.Complete(); // Rollback is done due to using statement...
return true;
}
catch (System.Exception)
{
return false;
}
}
}
This code worked fine!

You don't change your repositories but you definitely lack the Unit of Work in your architecture. This is where you share a single context for multiple repositories.
Think of the UoW as the DbContext where repositories are DbSets.
UoW uow = new UoW( context );
uow.BeginTransaction();
uow.Repository1.... // query, insert, update, delete
uow.Repository2....
uow.Commit();
A typical implementation just exposes multiple repositories:
public class UoW {
public UoW( DbContext ctx ) {
this._ctx = ctx;
}
private Repository1 _repo1;
public Repository1 Repo1
{
get
{
if ( _repo1 == null )
_repo1 = new Repository1( this._ctx );
return _repo1;
}
...
If you need a good and complete tutorial on that, take a look here:
http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

You can do this by using transaction scope instead of a dbcontexttransaction:
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
// Your code
scope.Complete();
}
Note when you use this across databases it will use MSDTC.

Related

EntityFrameworkCore swap DbConnection (add Transaction)

I have large DB model (hundred of tables) split to multiple EntityFrameworkCore DbContexts. Is is a quite common use case when I modify multiple entities in two (or more) different DbContexts, but I need to commit this operations withing a single transaction.
I use a IReporitory pattern where I get injected into Controller an instance of ISomeRepository implementation which looks like:
[HttpPost]
public asycn Task DoSomeWorkAsync()
{
using (var transaction = this.IEmployeesRepository.BeginTransaction())
{
// do some work
await this.IEmployeesRepository.SaveChangesAsync();
// do another work
await this.IPayrollRepository.SaveChangesAsync();
}
}
An EmployeeDbContext implements an IEmployeeRepository interface, PayrollDbContexts implements IPayrollRepository.
I end up with error:
System.InvalidOperationException: The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.
There exists very handy documentation, which basically solves the problem.
Cool, but I am not able to create a new instance of EmployeeDbContext, as described in documentation, because I am working with abstraction - interface only. I am looking for some method how to change / swap / inject / replace a DbConnection in existing DbContext.
I was thinking of implementing Clone method like
[HttpPost]
public asycn Task DoSomeWorkAsync()
{
using (var transaction = this.IEmployeesRepository.BeginTransaction())
{
await this.IEmployeesRepository.SaveChangesAsync();
var payrollRepoClone = IPayrollRepository.Clone(transaction);
await payrollRepoClone.SaveChangesAsync();
}
}
and then I would do
public class PayrollDbContext : DbContext, IPayrollRepository
{
private readonly DbConnection dbConnection;
public PayrollDbContext Clone(DbTransaction tran)
{
return new PayrollDbContext(tran.GetDbTransaction.Connection);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(dbConnection);
}
}
but I am trying to avoid this kind of tight coupling with SQL Server, because currently UseNpgSql is called in IoC in Service container where I resolve IPayrolRepository instance. And UseInMemoryDatabase in unit tests. This would crash my tests (or at least will require some dirty if/else in OnConfiguring)
Do you have any hints how to inject transaction or dbConnection to existing DbContext?
Thanks

How to make Entity Framework DbContext generic for dynamic connection string?

This is my method in the business layer:
public async task<model> GetMemberList(CancellationToken cancelToken, string connString){
try
{
await Task.Run(() =>
{
using (var dbContext = DbContext.Create(connString))
{
Code Goes Here....
}
}, cancelToken);
}
catch
{
Throw New Exception();
}
}
we have different clients databases. we pass connString from mvc controller to business layer methods to initialize new instance of dbContext to connect relevant client database. You can see in my method we used DbContext.Create(connString) to create a connection with database always when this methods run.
My problem is I can't use moq Framework for unit testing because of this situation. Is there a way to generalize this for dynamic connection string and move dbContext initialization outside the method?

How to handle on-demand data fetching for Domain Models

Given the following scenario...
I am concerned about two things...
1) Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.
2) Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?
If this design is incorrect then someone please tell me how should business models be loaded.
//Business model.
interface IFolder
{
int Id { get; }
IEnumerable<IFolder> GetSubFolders();
}
class Folder : IFolder
{
private readonly int id_;
private readonly IFolderDataProvider provider_;
public Folder(int id, IFolderDataProvider provider)
{
id_ = id;
provider_ = provider;
}
public int Id { get; }
public IEnumerable<IFolder> GetSubFolders()
{
return provider_.GetSubFoldersByParentFolderId(id_);
}
}
interface IFolderDataProvider
{
IFolder GetById(int id);
IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id);
}
class SqlFolderDataProvider : IFolderDataProvider
{
private readonly DbContext context_;
public SqlFolderDataProvider(DbContext context)
{
context_ = context;
}
public IFolder GetById(int id)
{
//uses the context to fetch the required folder entity and translates it to the business object.
return new Folder(id, this);
}
public IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id)
{
//uses the context to fetch the required subfolders entities and translates it to the business objects.
}
}
Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.
Yes, how else would you be able to call the provider and get the data?
However, the suffix DataProvider is very confusing because it is used for the provider that you use to connect to the database. I recommend changing it to something else. Examples: Repository, Context.
Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?
It won't necessarily live on forever. You decide its life span in your ConfigureServices function when you're adding it as a service, so you can change its scope from Singleton to whatever you like. I personally set the scope of my DBContext service to Transient and I also initiate it there with the connection string:
services.AddTransient<IDbContext, DbContext>(options =>
new DbContext(Configuration.GetConnectionString("DefaultDB")));
I then open and close the database connection in every function in my data layer files (you call it provider). I open it inside a using() statement which then guarantees closing the connection under any condition (normal or exception). Something like this:
public async Task<Location> GetLocation(int id) {
string sql = "SELECT * FROM locations WHERE id = #p_Id;";
using (var con = _db.CreateConnection()) {
//get results
}
}
Is it okay to inject a provider into a business model object
Yes if you call it "business" provider :). Actually do not take too serious all this terminology "inject", "provider". Till you pass (to business model layer's method/constructor) interface that is declared on business model layer (and document abstraction leaks) - you are ok.
should I instantiate the context in the constructor?
This could be observed as an abstraction leak that should be documented. Reused context can be corrupted or can be shared with another thread and etc -- all this can bring side effects. So developers tend to do create one "heavy" object like dbContext per "user request" (that usually means per service call using(var context = new DbContext()), but not always, e.g. Sometimes I share it with Authentication Service Call - to check is the next operation allowed for this user). BTW, DbContext is quite quick to create so do not reuse it just for "optimization".

entity object cannot be referenced by multiple instances of IEntityChangeTracker. while adding related objects to entity in Entity Framework 4.1

I am trying to save Employee details, which has references with City. But everytime I try to save my contact, which is validated I get the exception "ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker"
I had read so many post but still not getting the exact idea of what to do...
my Save button click code is given below
protected void Button1_Click(object sender, EventArgs e)
{
EmployeeService es = new EmployeeService();
CityService cs = new CityService();
DateTime dt = new DateTime(2008, 12, 12);
Payroll.Entities.Employee e1 = new Payroll.Entities.Employee();
Payroll.Entities.City city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
e1.Name = "Archana";
e1.Title = "aaaa";
e1.BirthDate = dt;
e1.Gender = "F";
e1.HireDate = dt;
e1.MaritalStatus = "M";
e1.City = city1;
es.AddEmpoyee(e1,city1);
}
and Employeeservice Code
public string AddEmpoyee(Payroll.Entities.Employee e1, Payroll.Entities.City c1)
{
Payroll_DAO1 payrollDAO = new Payroll_DAO1();
payrollDAO.AddToEmployee(e1); //Here I am getting Error..
payrollDAO.SaveChanges();
return "SUCCESS";
}
Because these two lines ...
EmployeeService es = new EmployeeService();
CityService cs = new CityService();
... don't take a parameter in the constructor, I guess that you create a context within the classes. When you load the city1...
Payroll.Entities.City city1 = cs.SelectCity(...);
...you attach the city1 to the context in CityService. Later you add a city1 as a reference to the new Employee e1 and add e1 including this reference to city1 to the context in EmployeeService. As a result you have city1 attached to two different context which is what the exception complains about.
You can fix this by creating a context outside of the service classes and injecting and using it in both services:
EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance
Your service classes look a bit like repositories which are responsible for only a single entity type. In such a case you will always have trouble as soon as relationships between entities are involved when you use separate contexts for the services.
You can also create a single service which is responsible for a set of closely related entities, like an EmployeeCityService (which has a single context) and delegate the whole operation in your Button1_Click method to a method of this service.
Steps to reproduce can be simplified to this:
var contextOne = new EntityContext();
var contextTwo = new EntityContext();
var user = contextOne.Users.FirstOrDefault();
var group = new Group();
group.User = user;
contextTwo.Groups.Add(group);
contextTwo.SaveChanges();
Code without error:
var context = new EntityContext();
var user = context.Users.FirstOrDefault();
var group = new Group();
group.User = user; // Be careful when you set entity properties.
// Be sure that all objects came from the same context
context.Groups.Add(group);
context.SaveChanges();
Using only one EntityContext can solve this. Refer to other answers for other solutions.
This is an old thread, but another solution, which I prefer, is just update the cityId and not assign the hole model City to Employee... to do that Employee should look like:
public class Employee{
...
public int? CityId; //The ? is for allow City nullable
public virtual City City;
}
Then it's enough assigning:
e1.CityId=city1.ID;
Alternatively to injection and even worse Singleton, you can call Detach method before Add.
EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);
EntityFramework 4: cs.Detach(city1);
There is yet another way, in case you don't need first DBContext object. Just wrap it with using keyword:
Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}
I had the same problem but my issue with the #Slauma's solution (although great in certain instances) is that it recommends that I pass the context into the service which implies that the context is available from my controller. It also forces tight coupling between my controller and service layers.
I'm using Dependency Injection to inject the service/repository layers into the controller and as such do not have access to the context from the controller.
My solution was to have the service/repository layers use the same instance of the context - Singleton.
Context Singleton Class:
Reference: http://msdn.microsoft.com/en-us/library/ff650316.aspx
and http://csharpindepth.com/Articles/General/Singleton.aspx
public sealed class MyModelDbContextSingleton
{
private static readonly MyModelDbContext instance = new MyModelDbContext();
static MyModelDbContextSingleton() { }
private MyModelDbContextSingleton() { }
public static MyModelDbContext Instance
{
get
{
return instance;
}
}
}
Repository Class:
public class ProjectRepository : IProjectRepository
{
MyModelDbContext context = MyModelDbContextSingleton.Instance;
[...]
Other solutions do exist such as instantiating the context once and passing it into the constructors of your service/repository layers or another I read about which is implementing the Unit of Work pattern. I'm sure there are more...
In my case, I was using the ASP.NET Identity Framework. I had used the built in UserManager.FindByNameAsync method to retrieve an ApplicationUser entity. I then tried to reference this entity on a newly created entity on a different DbContext. This resulted in the exception you originally saw.
I solved this by creating a new ApplicationUser entity with only the Id from the UserManager method and referencing that new entity.
I hit this same problem after implementing IoC for a project (ASP.Net MVC EF6.2).
Usually I would initialise a data context in the constructor of a controller and use the same context to initialise all my repositories.
However using IoC to instantiate the repositories caused them all to have separate contexts and I started getting this error.
For now I've gone back to just newing up the repositories with a common context while I think of a better way.
This is how I encountered this issue. First I need to save my Order which needs a reference to my ApplicationUser table:
ApplicationUser user = new ApplicationUser();
user = UserManager.FindById(User.Identity.GetUserId());
Order entOrder = new Order();
entOrder.ApplicationUser = user; //I need this user before saving to my database using EF
The problem is that I am initializing a new ApplicationDbContext to save my new Order entity:
ApplicationDbContext db = new ApplicationDbContext();
db.Entry(entOrder).State = EntityState.Added;
db.SaveChanges();
So in order to solve the problem, I used the same ApplicationDbContext instead of using the built-in UserManager of ASP.NET MVC.
Instead of this:
user = UserManager.FindById(User.Identity.GetUserId());
I used my existing ApplicationDbContext instance:
//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId());
I had the same problem and I could solve making a new instance of the object that I was trying to Update. Then I passed that object to my reposotory.
In this case, it turns out the error is very clear: Entity Framework cannot track an entity using multiple instances of IEntityChangeTracker or typically, multiple instances of DbContext. The solutions are: use one instance of DbContext; access all needed entities through a single repository (depending on one instance of DbContext); or turning off tracking for all entities accessed via a repository other than the one throwing this particular exception.
When following an inversion of control pattern in .Net Core Web API, I frequently find that I have controllers with dependencies such as:
private readonly IMyEntityRepository myEntityRepo; // depends on MyDbContext
private readonly IFooRepository fooRepo; // depends on MyDbContext
private readonly IBarRepository barRepo; // depends on MyDbContext
public MyController(
IMyEntityRepository myEntityRepo,
IFooRepository fooRepo,
IBarRepository barRepo)
{
this.fooRepo = fooRepo;
this.barRepo = barRepo;
this.myEntityRepo = myEntityRepo;
}
and usage like
...
myEntity.Foo = await this.fooRepository.GetFoos().SingleOrDefaultAsync(f => f.Id == model.FooId);
if (model.BarId.HasValue)
{
myEntity.Foo.Bar = await this.barRepository.GetBars().SingleOrDefaultAsync(b => b.Id == model.BarId.Value);
}
...
await this.myEntityRepo.UpdateAsync(myEntity); // this throws an error!
Since all three repositories depend on different DbContext instances per request, I have two options to avoid the problem and maintain separate repositories: change the injection of the DbContext to create a new instance only once per call:
// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!
or, if the child entity is being used in a read-only manner, turning off tracking on that instance:
myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);
Use the same DBContext object throughout the transaction.
For my scenario we have a solution with several applications referencing the same context. I had to update the unity.config file adding lifetime type to the context.
<lifetime type="PerResolveLifetimeManager" />
Error source:
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.Name);
ApplicationDbContext db = new ApplicationDbContent();
db.Users.Uploads.Add(new MyUpload{FileName="newfile.png"});
await db.SavechangesAsync();/ZZZZZZZ
Hope someone saves some precious time

TransactionScope functions

Working on Transactions in .net. Had a question on flowing transactions through sub functions. Do I need to use dependent transactions if the object context is common across the sub - methods?
For example, in the following code - I declare the object context in the constructor of my class (not sure if this is best practice)
public class EmployeeRepository
{
private EmployeeContext ec;
public EmployeeRepository()
{
objectContext = new EmployeeContext();
}
public InitTransaction(EmployeeEntity emp1)
{
using (TransactionScope transaction = new TransactionScope())
{
try
{ ec.employees.AddObject(emp1);
SubFunction1();
ec.SaveChanges();
}
catch
{
//catch
}
}
//commit the transaction here
ec.AcceptAllChanges();
}
public SubFunction1()
{
//some processing
//using same object context
ec.someother.AddObject(someobject);
ec.SaveChanges();
}
}
I want the subfunctions to be a part of the transactions as well?
In this case should I use a dependent transaction within SubFunction1 even though I am using the same object context? Or Should I add a
using (TransactionScope transaction = new TransactionScope());
within SubFunction1. Pointers in the right direction would be greatly appreciated.
Transaction Scopes can be nested (they work similar to the SQL ##TRANCOUNT mechanism), so you could in theory use TransactionScopes in your Repository, e.g. to keep parent : child table relationships ACID, but also in your Business / Service layers as well (e.g. to have a Distributed Transaction across multiple entities, possible across multiple Databases, and even across other resources such as Message Queues and Transactional file systems.
Note that the default isolation level of TransactionScope is Read Serializable - this can lead to locking / deadlocks.
You can consider using Dependency Injection to pass around the same ObjectContext so you can avoid the TransactionScope.
Instead of creating Context inside the Repository inject it through constructor.
public class EmployeeRepository
{
private EmployeeContext ec;
public EmployeeRepository(EmployeeContext objectContext)
{
ec = objectContext;
}
}
Take a look at this answer

Categories

Resources