I have to be able to set the primary key of the entities I am trying to add in my list "final_zones". I have a controller constructor as follows:
public UtilController(CFETSContext context)
{
_context = context;
}
......Then in a web api method.........
using (var transaction = _context.Database.BeginTransaction())
{
_context.Database.ExecuteSqlCommand(#"SET IDENTITY_INSERT [CFETSWeb].[dbo].[Zone] ON");
_context.SaveChanges();
transaction.Commit();
}
using (var transaction = _context.Database.BeginTransaction())
{
_context.Zones.AddRange(final_zones);
_context.SaveChanges(); //ERROR HERE
transaction.Commit();
}
No matter what I do, I cannot seem to get IDENTITY_INSERT to turn on. I get the following error:
{"Cannot insert explicit value for identity column in table 'Zone' when IDENTITY_INSERT is set to OFF."}
I just can't seem to get it to turn off. I can add entities to my DB in the controller, so I know everything else is working. I have tried doing this without a transaction as well with the same result.
_context.Database.ExecuteSqlCommand(#"SET IDENTITY_INSERT [CFETSWeb].[dbo].[Zone] ON");
_context.Zones.AddRange(final_zones);
_context.SaveChanges();
Any ideas? I am at a loss for what to try next. Thanks.
I solved it with the following:
using (var dbContextTransaction = _context.Database.BeginTransaction())
{
try
{
_context.Database.ExecuteSqlCommand(#"SET IDENTITY_INSERT [CFETSWeb].[dbo].[Zone] ON");
_context.Zones.AddRange(final_zones);
_context.SaveChanges();
dbContextTransaction.Commit();
}
catch (Exception e)
{
dbContextTransaction.Rollback();
}
}
NOTE: You HAVE to do this per table. IDENTITY_INSERT can only be set for 1 table at a time it seems, so you can do it this way or toggle it to OFF in the same transaction.
Also, IDENTITY_INSERT has to be in a transaction, as it only stays on for the duration of a transaction.
The SET IDENTITY_INSERT statement is scoped to one SQL session, which is practically equivalent to one set of statements that is sent over an open connection.
However, by default, an EF context will open and close a connection for each single statement with database interaction it executes. So after the ExecuteSqlCommand statement, the connection is closed, and the IDENTITY_INSERT is reset.
Now it's a somewhat hidden feature that EF won't close a connection if you open it before the context executes statements. So if you do this ...
try
{
_context.Database.Connection.Open();
_context.Database.ExecuteSqlCommand(#"SET IDENTITY_INSERT [CFETSWeb].[dbo].[Zone] ON");
_context.Zones.AddRange(final_zones);
_context.SaveChanges();
}
finally
{
_context.Database.Connection.Close();
}
... you'll notice that the IDENTITY_INSERT setting will "survive" the ExecuteSqlCommand statement.
Related
I have not had any luck with transactions and entity framework 5. I have the following code:
context.Database.Connection.Open();
transaction = context.Database.Connection.BeginTransaction(IsolationLevel.Serializable);
//some work happens
context.SaveChanges();
//some additional work
context.SaveChanges();
transaction.Commit();
At the very first context.SaveChanges call, I get an exception: "Connection is already part of a local or a distributed transaction"
Right now I am actually just doing a trivial proof of concept where all I am doing is attaching an entity, marking it as modified and then calling save changes.
As a troubleshooting deal, I put in an event handler for when the connection state changes and had a breakpoint in there. Doing that, I verified that the connection did not close on me between when I started the transaction and when I called save changes.
Any help figuring out why it is giving me that exception would be tremendously appreciated.
This is the way we used transactions before. It worked for us:
public void DoSomething()
{
using (var db = GetContext())
{
using (var ts = GetTransactionScope())
{
//do stuff
db.SaveChanges();
ts.Complete();
}
}
}
public TransactionScope GetTransactionScope()
{
var tso = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
return new TransactionScope(TransactionScopeOption.Required, tso);
}
If for some reason you have to do multiple SaveChanges calls in one transaction the recommended way is to wrap them in a TransactionScope:
using(var tran = new TransactionScope())
{
using(var context = new MyContext())
{
//some work happens
context.SaveChanges();
//some additional work
context.SaveChanges();
}
tran.Complete(); // without this call the transaction is rolled back.
}
The default isolation level is serializable. Each connection that is opened within the transaction enlists in this transaction. By default, EF always opens and closes connections when it executes queries.
I guess the cause of this exception you've got is that EF creates a transaction object itself when it executes SaveChanges. It tries to use its connection to start this transaction, but the connection is already part of the transaction you created. By using a TransactionScope, the EF transaction just enlists in the ambient transaction.
Does someone knows how to call a StoredProc using the same transaction of an objectContext SaveChanges method (EntityFramework 5)?
The goal is to apply the objects changes and call a stored Proc that does some "magic" on the DB, but, if something goes wrong (either with the SaveChanges or with the SP execution) no changes would be committed at all.
Steps:
Create the context
get the connection from the context
Create the transaction (TransactionScope)
Open the connection (will enlist the connection into the ambient transaction created in 3. and will prevent from closing the connection by the context)
Do SaveChanges()
Execute your stored procedure
Commit the transaction
Close the connection
Some code (MyContext is derived from DbContext):
using (var ctx = new MyContext())
{
using (var trx = new TransactionScope())
{
var connection = ((IObjectContextAdapter)ctx).ObjectContext.Connection;
try
{
ctx.Entities.Add(new MyEntity() { Number = 123 });
ctx.SaveChanges();
ctx.Database.ExecuteSqlCommand("INSERT INTO MyEntities VALUES(300)");
trx.Complete();
}
finally
{
connection.Close();
}
}
}
In my sql stored procedure, i do some insertion and updating which in some scenarios throws Primary Key or unique key violation.
When I try to execute this procedure from ADO.net, .net application also throws that exception and let me know that something wrong had happen.
But when I try to execute this procedure from EF, it just executes. Neither it show anything nor update anything.
How should I handle or notify user that something wrong had happen?
Ado.Net code is
SqlConnection sqlConnection = new SqlConnection(#"data source=database01; database=test; user id=test; password=test;");
SqlCommand cmd = new SqlCommand("[uspUpdateTest]", sqlConnection);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("RunID", RunID);
cmd.Parameters.AddWithValue("RunCode", RunCode);
sqlConnection.Open();
var str = cmd.ExecuteNonQuery();
Entity Framework Code is
TestEntities context = new TestEntities();
var str=context.UpdateRun(RunID, RunCode);
I am very much sure, you must set some return type(dummy) in your function import. It makes sense most of the time, because if you don't do so, your method name does not appear in intellisense and you will no be able to access it using context.MethodName.
My suggestion for you is, remove the return type of your Function Import and set it to none. Execute your method using ExecuteFunction method of context.
Context.ExecuteFunction(FunctionName,Parameters). It'll definitely throws the exception.
First of all, make sure you're throwing an Exception in your stored procedure which we can catch in our C# code. See - http://social.msdn.microsoft.com/forums/en-US/adodotnetdataproviders/thread/efea444e-6fca-4e29-b100-6f0c5ff64e59 - quote:
If you want RAISERROR to throw a SqlException, you need to set its
severity above 10. Errors with a severity of 10 and below are
informational, and thus don't throw exceptions.
I'll also show you the following code. I have been using this in my MVC controllers when getting data from my service layer using Entity Framework:
try
{
try
{
//Entity Framework/Data operations that could throw the data exception
//...
} catch (DataException dex) //see http://msdn.microsoft.com/en-us/library/system.data.dataexception.aspx
{
//Specifically handle the DataException
//...
}
}
catch (Exception e)
{
//do something (logging?) for the generic exception
throw e;
}
You can put a breakpoint on the last catch if the first catch doesn't trigger to see Exception-type/inner-exception of 'e' and go from there. It is useful to put a breakpoint on the generic exception, as it let's me know when I haven't handled something.
We can use the following way for sql raised error exception from entity framework:
Let's say, we have DBContext. So that
var connection= (SqlConnection)db.Database.Connection;
if (connection != null && connection.State == ConnectionState.Closed)
{
connection.Open();
}
SqlCommand com = new SqlCommand("spname", connection);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.Add(new SqlParameter("#parameter", parameter));
try
{
com.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex.message;
} `
The question here gives quite a nice summary of catching and handling the exceptions gracefully. You have several options after that for rolling back etc.
I need to use a Transaction Scope with Entity Framework 4 and a Firebird database. I am using the FireBird Entity Framework provider.
My problem is that once SaveChanges has been called on an object, the data is persisted to the database, instead of when transactionScope.Complete() is called. This results in data never rolling back, even if an exception occurs inside the using (TransactionScope ...) block.
This seems to be a problem with the FireBird DB, I have tested the exact same code with MS SQL 2008 and RollBack works correctly.
What do I need to do to enable Rolling Back with FireBird?
using ( var context = new Model1Container() )
{
bool success = false;
using ( TransactionScope transactionScope = new TransactionScope() )
{
PERSON person = new PERSON();
person.NAME = "test";
context.AddToPERSON(person);
context.SaveChanges(SaveOptions.DetectChangesBeforeSave);
success = true;
//transactionScope.Complete(); If this line is not hit, Transaction should Roll Back, but it does not.
}
if ( success )
{
context.AcceptAllChanges();
}
}
For firebird you need to explicitly say that it has to participate by adding Enlist=True in the connectionstring.
I'm using the LINQ Entity Framework and I've came across the scenario where I need to access the newly inserted Identity record before performing multiple operations using procedure.
Following is the code sinppet:
public void SaveQuote(Domain.Quote currentQuote)
{
try
{
int newQuoteId;
//Add quote and quoteline details to db
if (currentQuote != null)
{
using (QuoteContainer quoteContainer = new QuoteContainer())
{
**quoteContainer.AddToQuote(currentQuote);**
newQuoteId = currentQuote.QuoteId;
}
}
else return;
// Execution of some stored Procedure by using above newly generated QuoteId
}
catch (Exception ex)
{
throw ex;
}
}
In the next function
quoteContainer.SaveChanges(); will get called to commit the DB changes.
Can any one suggest whether the above approach is correct?
correct so far.
remember: you cannot get IDENTITY until insert has occured! on an update, your entity already holds the IDENTITY (mainly PK)