I want to use a database transaction with the name( a string) in EF 6.
I found aBeginTransaction() method, but it only has a IsolationLevel Parameter: BeginTransaction(IsolationLevel)
Is there a way to use a transaction with a string in EF6?
It's a bit elaborate, but you can use an existing connection + transaction in a DbContext. I adapted the example here to create a named transaction:
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (var trn = conn.BeginTransaction("TransactionName"))
{
using (var db = new MyContext(conn, false))
{
db.Database.UseTransaction(trn);
... // your code here
}
}
}
Note that your context class should implement this constructor and that the second parameter (contextOwnsConnection) must be false. Or use a constructor that defaults to false:
public MyContext(DbConnection connection)
: base(connection, false)
{ }
Related
First off, prior to asking this question I have been researching connection pooling for the better part of my morning. What I've found is that is turned on by default, handled by the application/system, and C#'s System.Data.X namespaces (where X is something like OLEDBConnection / SqlConnection ) handles it automatically.
But as I'm still new to the whole database access and pooling, I am looking for clarification since I wrote a class to handle connections at a higher level.
public abstract class AbstractDatabase<T> where T: where T : System.Data.Common.DbConnection
{
// Take this class's connection string and return a new connection object
public abstract T GetConnection();
public abstract SqlKata.Compilers.Compiler { get; }
// Compile the query and return a DataTable
// This is actually pointing to a static method that uses the IDBConnection interface and logs any errors, but I copied the relavant code here
public virtual DataTable GetDataTable(SqlKata.Query query)
{
using (var Conn = GetConnection())
{
if (Conn.State == ConnectionState.Closed) Conn.Open();
using (var Cmd = Conn.CreateCommand())
{
Cmd.CommandType = CommandType.Text;
Cmd.Connection = Conn;
Cmd.CommandText = Compiler.Compile(Query).ToString(); // the query is fully formed string here
Cmd.Parameters.AddDummyParameter(); // I noticed that without any parameters, this didn't work, so this method adds a dummy, even if the string doesn't have any '?' characters
using (var DR = Cmd.ExecuteReader())
{
DataTable DT = new DataTable();
DT.Load(DR);
return DT;
}
}
}
}
}
public class SomeDatabase : AbstractDatabase<OleDbConnection>
{
// class implementation
}
here is my question:
If I were to somewhere else in my program do something like this:
DataTable Tbl1 = SomeDatabase.GetDataTable(Query1);
DataTable Tbl2 = SomeDatabase.GetDataTable(Query2);
....
DataTable Tbl10 = SomeDatabase.GetDataTable(Query10);
My understanding is that the method's Using ( var Conn = GetConnection()) statement will automatically issue a Conn.Close() and dispose of the Conn as the method exits. But connection pooling is automatically enabled, so it will actually reuse that connection almost immediately instead of closing it, correct?
With that level of abstraction, does it make sense to write it like this?
using ( var conn = SomeDatabase.GetConnection())
{
DataTable Tbl1 = SomeDatabase.GetDataTable(Query1);
....
DataTable Tbl10 = SomeDatabase.GetDataTable(Query10);
}
Would this make any difference?
Should the method be written to accept the connection that already open ?
Should the class be written to use the same connection object itself, rather than calling GetConnection() every time ? ( for example, a get-only property that initializes the backing field on first request to it, then all subsequent requests actually receive the backing field's object)
in my project I am using one of the overloads for instantiating EntityConnection.
internal static EntityConnection GetEntityConnection(string name)
{
metadataWorkspace = new MetadataWorkspace(...);
var connection = new SqlConnection(GetConnection(name));
connection.AccessToken = OptionalAccessToken(connection);
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
using (var ec = new EntityConnection(metadataWorkspace, connection))
{
return ec;
}
}
I am wondering, when the EntityConnection instance gets disposed automatically thanks to the usage of using does also the open connection within get disposed?
The answer to your question lies in the reflecting of the EntityConnection. Look into that and see for yourself, if underlying connection gets disposed. Many objects do something like that
public void Dispose()
{
// dispose underlying objects
_privateMemeber.Dispose();
}
For example, there was such a bug in MySql .NET provider.
But the general rule should be, "if you have not created it, you don't dispose it". In your case, using should be used outside of GetEntityConnection
using (var ec = new GetEntityConnection(...))
After you research, if EntityConnection disposes underlying object, that should be enough.
Otherwise you need to create disposing hierarchy
using (var conn = new GetConnection(...))
using (var enConn = new GetEntityConnection(...))
{
. . . . .
}
The following code uses Entity Framework 6 and Managed Oracle Providers to call an Oracle stored procedure that returns multiple cursors.
The using statement is throwing the following exception:
System.ObjectDisposedException: 'Cannot access a disposed object.Object name: 'OracleConnection'.'
If I remove the using statement and instead use the code in the following post. I get no errors.
Using Entity Framework to Call an Oracle Stored Procedure with Multiple Cursors
Why is the using statement causing an exception? It has been suggested to me that there is a bug with the Oracle Managed Provider. But, my colleagues are using the same provider and their using statements are working fine.
Example Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using Oracle.ManagedDataAccess.Client;
using System.Data.Entity.Infrastructure;
namespace MyCompany
{
public class MyClass
{
private MyDbContext _dbContext = new MyDbContext();
public MyItems GetMyItems(string id)
{
var sqlQuery = "";
var oracleParameters = new List<OracleParameter>();
var oneEntityList = new List<OneEntity>();
var twoEntityList = new List<TwoEntity>();
var threeEntityList = new List<ThreeEntity>();
sqlQuery = #"
BEGIN
MY_PACKAGE.GetMyItems(:id, :p_cursor1, :p_cursor2, :p_cursor3);
END;
";
oracleParameters = new List<OracleParameter>
{
new OracleParameter("p_id", id),
new OracleParameter("p_cursor1", OracleDbType.RefCursor, ParameterDirection.Output),
new OracleParameter("p_cursor2", OracleDbType.RefCursor, ParameterDirection.Output),
new OracleParameter("p_cursor3", OracleDbType.RefCursor, ParameterDirection.Output)
};
using (var connection = _dbContext.Database.Connection)
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = sqlQuery;
command.Parameters.AddRange(oracleParameters.ToArray());
using (var reader = command.ExecuteReader())
{
oneEntityList = ((IObjectContextAdapter)dbContext).ObjectContext
.Translate<OneEntity>(reader)
.ToList();
reader.NextResult();
twoEntityList = ((IObjectContextAdapter)dbContext).ObjectContext
.Translate<TwoEntity>(reader)
.ToList();
reader.NextResult();
threeEntityList = ((IObjectContextAdapter)dbContext).ObjectContext
.Translate<ThreeEntity>(reader)
.ToList();
}
return new MyItems { OneEntity = oneEntityList, TwoEntity = twoEntityList, ThreeEntity = threeEntityList };
}
}
}
}
It is correct and proper to use using statements around disposable objects when you own the lifetime; however, in this case: you don't! The connection here belongs to the data-context, and presumably the data-context itself is IDisposable, and it will dispose the connection when the data-context is disposed.
So: while you might be allowed to borrow the connection from the data-context for the purposes of executing queries - you shouldn't be trying to dispose it here. That would end up closing/disposing a connection at unexpected times, with unpredictable results.
Conversely: if you had a var conn = new OracleConnection(...), then clearly you do own that connection (unless you hand it to something that will manage the lifetime), and you should dispose it.
Just to complicate things further... currently, your MyClass seems to own the db-context, via:
private MyDbContext _dbContext = new MyDbContext();
So ideally, your MyClass should be disposable (: IDisposable), and disposing MyClass should cascade to dispose _dbContext.
I have a windows form application with .NET 4 and Entity Framework for data layer
I need one method with transaction, but making simple tests I couldn't make it work
In BLL:
public int Insert(List<Estrutura> lista)
{
using (TransactionScope scope = new TransactionScope())
{
id = this._dal.Insert(lista);
}
}
In DAL:
public int Insert(List<Estrutura> lista)
{
using (Entities ctx = new Entities (ConnectionType.Custom))
{
ctx.AddToEstrutura(lista);
ctx.SaveChanges(); //<---exception is thrown here
}
}
"The underlying provider failed on Open."
Anyone have any ideas?
PROBLEM RESOLVED - MY SOLUTION
I solved my problem doing some changes.
In one of my DAL I use a Bulk Insert and others Entity.
The problem transaction was occurring by the fact that the bulk of the transaction (transaction sql) do not understand a transaction scope
So I separated the Entity in DAL and used the sql transaction in its running some trivial. ExecuteScalar ();
I believe that is not the most elegant way to do this, but solved my problem transaction.
Here is the code of my DAL
using (SqlConnection sourceConnection = new SqlConnection(Utils.ConnectionString()))
{
sourceConnection.Open();
using (SqlTransaction transaction = sourceConnection.BeginTransaction())
{
StringBuilder query = new StringBuilder();
query.Append("INSERT INTO...");
SqlCommand command = new SqlCommand(query.ToString(), sourceConnection, transaction);
using (SqlBulkCopy bulk = new SqlBulkCopy(sourceConnection, SqlBulkCopyOptions.KeepNulls, transaction))
{
bulk.BulkCopyTimeout = int.MaxValue;
bulk.DestinationTableName = "TABLE_NAME";
bulk.WriteToServer(myDataTable);
StringBuilder updateQuery = new StringBuilder();
//another simple insert or update can be performed here
updateQuery.Append("UPDATE... ");
command.CommandText = updateQuery.ToString();
command.Parameters.Clear();
command.Parameters.AddWithValue("#SOME_PARAM", DateTime.Now);
command.ExecuteNonQuery();
transaction.Commit();
}
}
}
thanks for the help
According to the all-mighty Google, it seems that EF will open/close connections with each call to a database. Since it's doing that, it will treat the transaction as using multiple connections (using a distributed transaction). The way to get around this is to open and close the connection manually when using it.
Here's the information on the distributed transactions issue.
Here's how to manually open and close the connection.
A small code sample:
public int Insert(List<Estrutura> lista)
{
using (TransactionScope scope = new TransactionScope())
{
using (Entities ctx = new Entities (ConnectionType.Custom))
{
ctx.Connection.Open()
id = this._dal.Insert(ctx, lista);
}
}
}
public int Insert(Entities ctx, List<Estrutura> lista)
{
ctx.AddToEstrutura(lista);
ctx.SaveChanges();
}
Instead of employing TransactionScope, it is better to employ UnitOfWork pattern while working with entity framework. please refer to:
unit of work pattern
and also;
unit of work and persistance ignorance
In the light of Closing connections explicitly in Entity Framework and http://msdn.microsoft.com/en-us/library/bb738582%28v=vs.90%29.aspx it seems that I should be using the context to create connections rather than doing the following
using (SqlConnection con = new SqlConnection("Persist Security Info=False;Integrated Security=true;Initial Catalog=Remember;server=(local)"))
{
...
}
My understanding is that I'll
Get rid of the connection string
Utilize connection pooling built into EF
But how do I acquire an SQL connection through the context?
In EF5 (changed for EF6) the following would return the connection:
var connection = ((EntityConnection)context.Connection).StoreConnection;
If you are using EF in a right way you will probably never need to get inner db connection.
I used next code to getting connection (Entity Framework 5.0):
var connection = Context.Database.Connection;
I found out that the magic lies in ExecuteStoreCommand()
new AdventureEntities().ExecuteStoreCommand(
#" UPDATE Users
SET lname = #lname
WHERE Id = #id",
new SqlParameter("lname", lname), new SqlParameter("id", id));
Then there is no need for an explicit Connection, it actually made the code a lot cleaner. The one-liner above replaced all of the following code
using (SqlConnection con = new SqlConnection("Persist Security Info=False;Integrated Security=true;Initial Catalog=Remember;server=(local)"))
{
con.Open();
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = #"
UPDATE Users
SET lname = #lname
WHERE Id = #id";
cmd.Parameters.AddWithValue("lname", lname);
cmd.Parameters.AddWithValue("id", id);
cmd.ExecuteNonQuery();
}
}
var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString);
string sqlConnectionString = efConnectionStringBuilder.ProviderConnectionString;
If the EF connection string is stored in your web config file, you can assign it to a SQL Data Source with this code:
var connString = ConfigurationManager.ConnectionStrings["MyDataEntities"].ConnectionString;
EntityConnection ec = new EntityConnection(connString);
var storeConnect = ec.StoreConnection;
SqlDataSource1.ConnectionString = storeConnect.ConnectionString;
I believe the easiest way to create a SqlConnection in EF6 is to do the following:
DBContext context = new DBContext();
SqlConnection sqlconn = new SqlConnection(context.Database.Connection.ConnectionString);
You could use the following as well. I wanted to inject some Dapper.NET into a existing project that already had Entity framework entity context so I created the following:
public class EFConnectionAccessor : IDisposable
{
private readonly SqlConnection sqlConnection;
private readonly MyEntities entities;
public EFConnectionAccessor()
{
entities = new MyEntities();
var entityConnection = entities.Connection as EntityConnection;
if (entityConnection != null)
{
sqlConnection = entityConnection.StoreConnection as SqlConnection;
}
}
public SqlConnection connection
{
get
{
sqlConnection.Open();
return sqlConnection;
}
}
public void Dispose()
{
sqlConnection.Close();
sqlConnection.Dispose();
entities.Dispose();
}
}
Called using
using (SqlConnection sqlConnection = new EFConnectionAccessor().connection)
{
// ADO.NET CODE HERE - Or in my case dapper.net
}
the DataContext will allow you to call your Entity Objects right off of your DataContext reference, abstracting away all of the details of the underlying connection logistics.