Manage Transactions on Business Layer - enterprise-library

I want to use TransactionScope class in my business layer to manage database operation in data access layer.
Here is my sample code. When i execute it, it tries to enable the dtc. I want to do the operation without enable dtc.
I already checked https://entlib.codeplex.com/discussions/32592 article. It didn't work for me. I read many articles on this subject but none of them really touch enterprise library or i didn't see.
by the way, i am able to use TransactionScope using dotnet sql client and it works pretty well.
what would be the inside of SampleInsert() method?
Thanks,
Business Layer method:
public void SampleInsert()
{
using (TransactionScope scope = new TransactionScope())
{
Sample1DAL dal1 = new Sample1DAL(null);
Sample2DAL dal2 = new Sample2DAL(null);
Sample3DAL dal3 = new Sample3DAL(null);
dal1.SampleInsert();
dal2.SampleInsert();
dal3.SampleInsert();
scope.Complete();
}
}
Data Access Layer method:
//sampleInsert method structurally same for each 3 dal
public void SampleInsert()
{
Database database = DatabaseFactory.CreateDatabase(Utility.DATABASE_INFO); ;
using (DbConnection conn = database.CreateConnection())
{
conn.Open();
DbCommand cmd = database.GetStoredProcCommand("P_TEST_INS", "some value3");
database.ExecuteNonQuery(cmd);
}
}

Hi yes this will enable dtc because you are creating 3 DB connections within one TransactionScope . When more than one DB connection is created within same TransactionScope the local transaction escalate to Distributed Transaction and hence dtc will be enabled to manage Distributed Trnsactions.You will have to do it in a way that only one DB connection is created for entire TransactionScope. I hope this will give you an idea.

After research and waching query analyzer, I changed the SampleInsert() body as follows and it worked. The problem was as ethicallogics mentioned opening new connection each time i access the database.
public void SampleInsert()
{
Database database = DatabaseFactory.CreateDatabase(Utility.DATABASE_INFO);
using (DbCommand cmd = database.GetStoredProcCommand("P_TEST_INS", "some value1"))
{
database.ExecuteNonQuery(cmd);
}
}

Related

The model backing the 'DataContext' context has changed since the database was created

I am trying to use Code First with Migrations. Even though there are no current changes to my model, I'm getting an exception. When I add a migration, the up and down are empty, but I get a runtime error with the message as follows:
An exception of type 'System.InvalidOperationException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: The model backing the 'MyDataContext' context
has changed since the database was created. Consider using Code First
Migrations to update the database (http://go.microsoft.com/fwlink/?
My architecture is as follows:
DataAccess project that includes the context, fluid configurations and migrations code
Model project that contains the poco classes
Web API and MVC projects that each contain the connections string in their respective web.config files.
Additionally I have the following code:
DbInitializer
public static MyDataContext Create()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDataAccess.MyDataContext, MyDataAccess.Migrations.Configuration>());
return new MyDataContext(ConfigurationManager.ConnectionStrings["MyDataContext"].ConnectionString, null);
}
I started with AutomaticMigrationsEnabled = false; in the migration Configuration constructor, as it was my understanding that this would allow (and require) me to have more control over when migrations were applied. I have also tried setting this to true but with the same result.
I added a new migration upon receiving this error, and the Up method was empty. I updated the database to this new migration, and a record was created in the _migrationHistory table, but I still receive the error when I attempt to run the application. Also, the seed data was not added to the database.
protected override void Seed(MyDataAccess.MyDataContext context)
{
IdentityResult ir;
var appDbContext = new ApplicationDbContext();
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(appDbContext));
ir = roleManager.Create(new IdentityRole("Admin"));
ir = roleManager.Create(new IdentityRole("Active"));
ir = roleManager.Create(new IdentityRole("InActive"));
var userNamager = new UserManager<User>(new UserStore<User>(appDbContext));
// assign default admin
var admin = new User { UserName = "administrator", Email = "myAdmin#gmail.com" };
ir = userNamager.Create(admin, "myp#55word");
ir = userNamager.AddToRole(admin.Id, "Admin");
}
where
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext()
: base("MyDataContext", throwIfV1Schema: false)
{
}
...
The question: If Add-Migration isn't seeing any change in the model, why do I get this error when I run? Why isn't the seed code being hit? How do I fix this, or if that can't be determined, how do I further determine the root cause?
I am not sure if you found the answer to your problem, but this other answer I found here actually did it for me:
Entity Framework model change error
I actually ended up deleting the __MigrationHistory table in SQL Server which I didn't know it was being created automatically.
The article also talks about the option to not generate it I think by using this instruction: Database.SetInitializer<MyDbContext>(null); but I have not used it, so I am not sure if it works like that
This worked for me.
Go to Package Manager Console and Run - Update-Database -force
I bet your data context is not hooking up the connection string.
Check if it's not initialized with a localdb (something like (localdb)\v11.0) and not working with that when you might think it's set to something else.
My issue ended up being a conflict between Automatic Migrations being enabled and the initializer MigrateDatabaseToLatestVersion as described here.

Transaction Scope Isolation level and EF6

I am testing the EF6 isolation level but the test fails with:
Assert.AreEqual failed. Expected:<ReadUncommitted>. Actual:<Unspecified>.
The test:
public void TestIsolationLevelReadUncommitted()
{
// Arrange
using (
new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {IsolationLevel = IsolationLevel.ReadUncommitted}))
{
using (var context = new BoligEntities())
{
// Act
context.GetDbConnection().Open();
var isolationLevel = context.GetDbConnection().GetIsolationLevel();
// Assert
Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, isolationLevel);
}
}
}
The test doesn't make much sense but I am wondering why it fails.
there is plenty of posts around transaction scope and EF.
Actually add uncommitted read and nolock to your searches.
Good basic explanation and example
FROM EF 6 on...
EF transaction scope docu
Generally you dont need it. ( there are exceptions)
and
i hope i dont have to support the system that uses uncommitted reads. ;-) Filthy...
good luck
Transaction is not the same as database. Looks like you are checking Isolation level for open connection but not for running transaction.
Generally speaking - you can open connection and run multiple transaction on this connection with different Isolation level.
using (var context = new MyEntities())
{
using (var tran = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, tran.UnderlyingTransaction.IsolationLevel);

Entity Framework DefaultConnectionFactory being ignored

I'm using Entity Framework 5 with Code First. I've written a custom IDbConnectionFactory which I want to use for all the connections made by my DbContext class, so early on in the application's lifecycle, before any database work is done, I call
Database.DefaultConnectionFactory = new MyConnectionFactory();
However, MyConnectionFactory.CreateConnection is never called, which suggests to me that EF's changed it back - but the debugger shows that it's still a MyConnectionFactory after several queries have run. For some reason, it's just not using it.
My DbContext is initialised by passing the name of a connection string from the app.config file, and those connection strings do specify an explicit provider (as indeed they have to) so I'm wondering if that's causing a per-connection override of the connection factory based on the connection string. Does this happen and can I stop it without registering a completely new provider (although maybe that's not too hard to do?).
Whatever I see online about this (much obscured by the defaultConnectionFactory tag in various app.config examples) suggests you can just change it to an IDbConnectionFactory instance of your choice and it'll work, but mine isn't behaving.
The purpose of this is to allow me to run a particular set of SQL statements whenever a new connection is opened, so the second part of this question would be does anybody know a better way to do this?
I know it is not ideal but this worked for me:
public class DBBase : DbContext
{
public DBBase(string nameOrConnectionString)
: base(Database.DefaultConnectionFactory.CreateConnection(nameOrConnectionString), true)
{
}
// ...
}
You need to get the connection that you built for each call that you are wanting to use. For example using the following code.
private static void UsingCustomConnection()
{
using (var conn = Database.DefaultConnectionFactory.CreateConnection("YourDbName"))
{
using (var context = new YourContext(conn))
{
context.Destinations.Add(new Destination {Name = "Colorado"});
context.SaveChanges();
}
}
}
You will need to setup this in YourContext
public YourContext(DbConnection connection)
: base(connection, contextOwnsConnection: false)
{
}

Update issue with Generic Repositories using EF

I have created Generic Repository and I have two entities that i need to update in a transaction.
Here is what i m doing..
ProductOrganizationEntity poDataContext= new ProductOrganizationEntity();
IRepository<tblProductInfo> productRepo = new GenericRepository<ProductOrganizationEntity, tblConfirmation>(poDataContext);
Piece of Code which is causing problem is this.
using (TransactionScope txScope = new TransactionScope())
{
productRepo.Attach(productEntity);
productRepo.SaveChanges();
new ProductLocation().SaveLocation(productEntity.Locations, productEntity.productCode);
txScope.Complete();
}
productRepo.SaveChanges(); This is where it throws me Error. The error is
The operation could not be performed because OLE DB provider "SQLNCLI10" for linked server "Venus" was unable to begin a distributed transaction.
(We do have server named Venus but its not access in anyway in these transactions at all. Secondly as i said this works without transaction block).
This piece of code works fine if taken out from Transaction Block.
ProductLocation.SaveLocation is creating Repository for Location . Here is the code from Save Location.
IRepository<LocationInfo> locRepo= new GenericRepository<ProductOrganizationEntity, LocationInfo>(new ProductOrganizationEntity());
if (loc.locID <= 0) // This is a new Location to be added.
locRepo.Add(locEntity);
else
locRepo.Attach(siteToAdd);
locRepo.SaveChanges();
Here is what i have done in my generic repository for thse methods
public void Attach(TEntity entity)
{
if (entity == null)
throw new ArgumentException("Update : Supplied Entity is Null.");
_currentDbSet.Add(entity);
System.Data.Entity.Infrastructure.DbEntityEntry entry = _dataContext.Entry(entity);
entry.State = System.Data.EntityState.Modified;
}
and this is what i have in SaveChanges in my generic repo.
public virtual void SaveChanges()
{
if (_dataContext == null)
throw new Exception("SaveChanges: DataContext is not initialized.");
_dataContext.SaveChanges();
}
What is that i am doing wrong here .
I appreciate any pointers.
It might be possible that your server is linked to another SQL server at the database level.
Perhaps look at this: http://msdn.microsoft.com/en-us/library/ms188279.aspx
Must admit I've never used linked servers (not yet at least), but seeing "Linked Servers" in the error made me think of this.

Managing transactions between EntityFramework and EnterpriseLibrary's DatabaseFactory

I'm working with an existing set of code that manages multiple database updates in a single transaction. Here is a simplified example:
Database db = DatabaseFactory.CreateDatabase();
using (DbConnection dbConnection = db.CreateConnection())
{
dbConnection.Open();
DbTransaction dbTransaction = dbConnection.BeginTransaction();
try
{
//do work
dbTransaction.Commit();
}
catch (Exception ex)
{
dbTransaction.Rollback();
}
}
I am also using EntityFramework in this same project for new development. Below is a simplified example of the usage of my repository class:
List<ThingViewModel> things = new List<ThingViewModel>();
// populate list of things
IObjectRepository thingRepository = new ThingRepository();
thingRepository.AddThings(things);
thingRepository.Save();
I want the work done in 'AddThings' to happen as part of the transaction in the first block of code.
Is there some clean way of blending my repository pattern into this existing code or vice-versa? I'm not at the point yet where it is feasible to rewrite the existing code to be entirely within EntityFramework, so I'm looking for some some interim approach.
I have tried passing the transaction from the older code into the repository, and thus EntityFramework, but that does not seem to work. I have also tried passing the ObjectContext back out to the older code in order to enlist it in the transaction. Neither approach works.
I cannot believe that I am the first person to encounter this hurdle in migrating existing code to EntityFramework... there must be something I am not considering.
I'll list the things that I have tried below:
using (TransactionScope transactionScope = new TransactionScope())
{
Database db = DatabaseFactory.CreateDatabase();
using (DbConnection dbConnection = db.CreateConnection())
{
dbConnection.Open();
DbTransaction dbTransaction = dbConnection.BeginTransaction();
try
{
//do work
dbTransaction.Commit();
}
catch (Exception ex)
{
dbTransaction.Rollback();
}
}
Thing thing = new Thing(){
Prop1 = Val1,
Prop2 = Val2
};
ThingObjectContext context = new ThingObjectContext();
context.Things.AddObject(thing);
context.SaveChanges();
transactionScope.Complete();
}
This last example 'works', it does not function as a transaction. When the EF insert fails, the EL commands are not rolled back by the TransactionScope. If I don't put those explicit calls to .Commit() and .SaveChanges(), nothing happens. I would really like for this to share the same connection if possible. Two other variations of this I am currently playing around with is trying to use the same connection between both EF and EL as well as use EnlistTransaction on one side or the other. Definitely trying to keep this from becoming a MSDTC - don't want the extra overhead associated with that.
Use TransactionScope instead of explicit transaction management. You'll simplify the overall code and everything you do should automatically detect and use the same transaction.
Is there any way you can call Database.GetOpenConnection() instead of CreateConnection() in your EL code, and pass in the things.Connection that you create inside of a TransactionScope block? I haven't tested this, but that is what I would try first.