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
Related
In my asp mvc Framework project, using EF, I have some objects (from classes) whose fields store data coming from my database.
My question is :
How to populate these fields, or manage methods of these objects using a dbcontext variable ?
Sol 1: Is it better to use each time I need a connection with db in my classes with the instruction (using (resource), see below ) ?
Sol 2: Is it betterI to code a singleton class to use one instance of the context ?
Sol 3: Or should I use another way for the links beetween my classes and the database ?
What is the best method considering performances and code quality.
Thanks for your attention .
Solution 1
public class Test
{
private T1 a;
private T2 b;
public Test()
{}
public void CreateFrom (int id)
{
using (var db=new WebApplicationMVCTest.Models.dbCtx())
{
a=db.T1s.Find(id);
b= db.T2s.Find(a.id2);
}
}
Solution 2:
public class DbSingleton
{
private static dbCtx instance;
private int foo;
private DbSingleton ()
{}
public static dbCtx Current
{
get
{
if (instance == null)
{
instance = new dbCtx();
}
return instance;
}
}
public static void Set (dbCtx x)
{
if (instance==null)
{
instance = x;
}
}
}
For a web project, never use a static DbContext. EF DbContexts are not thread safe so handling multiple requests will lead to exceptions.
A DbContext's lifespan should only be as long as it is needed. Outside of the first time setup cost when a DbContext is used for the first time, instantiating DbContexts is fast.
My advice is to start simple:
public ActionResult Create(/* details */)
{
using (var context = new AppDbContext())
{
// do stuff.
}
}
When you progress to a point where you learn about, and want to start implementing dependency injection applications then the DbContext can be injected into your controller / service constructors. Again, from the IoC container managing the DbContext, the lifetime scope of the Context should be set to PerWebRequest or equivalent.
private readonly AppDbContext _context;
public MyController(AppDbContext context)
{
_context = context ?? throw new ArgumentNullException("context");
}
public ActionResult Create(/* details */)
{
// do stuff with _context.
}
The gold standard for enabling unit testing would be injecting a Unit of Work pattern and considering something like the Repository pattern to make your dependencies easier to unit test.
The best advice I can give you starting out with EF and MVC is to avoid the temptation to pass Entities between the controller (server) and the UI. (views) You will come across dozens of examples doing just this, but it is a poor choice for performance, it also hides a LOT of land mines and booby traps for both performance, exceptions, and data security issues. The most important detail is that when the UI calls the controller passing what you expect will be an entity, you are not actually getting an entity, but a de-serialized JSON object cast to an entity. It is not an entity that is tracked by the DbContext handling the request. Instead, get accustomed to passing view models (serializable data containers with the data the view needs or can provide) and IDs + values where the controller will re-load entities to update the data only as needed.
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
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".
I was looking at this kind of code and wondering, is there anything that can be improved if you approach from functional programming perspective?
You don't have to strictly re-implement my example to answer, if you have different example involving Transactions, that would be great.
using (var unitOfWork = _uowManager.Begin())
{
_paymentRepository
.InsertOrUpdate(payment); // Returns payment instance
// Being executed to get Payment.Id
_uowManager
.SaveChanges();
_otherRepository
.OtherMethod(payment.Id); // Could be changed as necessary
unitOfWork
.Complete()
}
Code above is based on ASP.NET Boilerplate and Entity Framework if it helps.
I think that creating transactional monad is a great way to handle this. Doesn't matter how much FP is in C#, this creates a lot of benefits:
single, global, unified way to handle transactions
don't repeat yourself, write this class once and don't recreate transaction logic n times for each controller function
you can create this class as interface and mock it in integration tests, for example, to rollback transaction, so your tests don't impact the database state
I use something like this:
public class Tx
{
public DbConnectionExtra _connection { get; set; } // class which has DbTransaction and DbConnection combined
public T UseTx<T>(Func<T> fnToCall)
{
try
{
_connection.BeginTransaction();
var result = fnToCall();
_connection._transaction.Commit();
_connection._transaction.Dispose();
return result;
}
catch
{
_connection._transaction.Rollback();
_connection._transaction.Dispose();
throw;
}
}
}
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.