I have question regarding Entity Framework. In my program I first fill-up my DbContext with data. Then I need to delete all the data from the tables in DB, but only if new data will be saved. If db.Savechanges() throws an exception I need my old data to still be in the tables.
My code is:
static void Main(string[] args)
{
PdmContext db = new PdmContext();
FillDbContext();
try
{
if (db.SaveChanges() > 0)
{
using (var del = new PdmContext())
{
DeleteModel.deleteFromAllTables();
}
db.SaveChanges();
}
}
catch (Exception exp)
{
Logger.Log("Exception (global catch));
}
}
I can't seem to figure this out. Anyone can help with this? :)
You can use Transaction which will make sure to revert the operation done within the scope of it if the operation fails at some stage :
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var del = new PdmContext())
{
DeleteModel.deleteFromAllTables();
}
db.SaveChanges();
scope.Complete(); // commits the transaction
}
Now the changes to the database will be atomic so that it will only keep all changes or not at all. I have not included exception handling code for simplicity but due to any reason if the scope.Complete() was not being executed and control exists the transaction block without executing that the transaction will get rolled back.
You need to use a Transaction.
see how to use that:
using (var dbContextTransaction = PdmContext.Database.BeginTransaction())
{
try
{
// HERE your operation insert etc.
PdmContext.SaveChanges();
dbContextTransaction.Commit(); // here, apply your operation
}
catch (Exception)
{
dbContextTransaction.Rollback(); // here, undo your operations
}
}
You can handle such scenario with transaction management.
There is two way to handle it.
1) You can use single dbcontext for all operation instead of create multiple for single operation.
using (var context = new SchoolContext())
{
try
{
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
2) Using single DbContextTransaction object:
using (var context = new SchoolContext())
{
context.Database.Log = Console.Write;
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.SaveChanges();
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
}
I hope it work for you.
Related
I am getting Error : The transaction associated with the current connection has completed but has not been disposed. after Log.WriteEntry($"Error occurred while trying to create invoice for transaction {transaction.TransactionLogId} : {ex.Message}"); statement when i run the code.. Can someone please help?
public virtual GroupCreationResult CreateGroups(IEnumerable<TransactionDetail> transactions)
{
var transactionDetails = transactions as TransactionDetail[] ?? transactions.ToArray();
var successes = new List<int>(transactionDetails.Length);
var errors = new List<TransactionError>();
foreach (var transaction in transactionDetails)
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = new TimeSpan(0, 2, 0) }))
{
try
{
foreach (var service in _invoiceCreationServices)
{
try
{
service.CreateInvoice(transaction);
}
catch (Exception ex)
{
Log.WriteEntry($"Error occurred while trying to create invoice for transaction {transaction.TransactionLogId} : {ex.Message}");
if (!(ex.ToString().Contains("Violation of PRIMARY KEY constraint 'PK_APInvGrp'.") || ex.ToString().Contains("Violation of PRIMARY KEY constraint .")))
{
Log.WriteEntry($"error occured while adding the transaction {transaction.TransactionLogId} - {ex.ToString()}");
errors.Add(new TransactionError(transaction.TransactionLogId, ex.ToString()));
scope.Complete();
break;
}
}
}
Log.WriteEntry($"successfully added the transaction {transaction.TransactionLogId}");
successes.Add(transaction.TransactionLogId);
scope.Complete();
}
catch (Exception exception)
{
Log.WriteEntry($"error1 occured while adding the transaction {transaction.TransactionLogId} - {exception.ToString()}");
//errors.Add(new TransactionError(transaction.TransactionLogId, exception.ToString()));
}
}
}
return BuildGroupCreationResult(successes, errors);
}
I am trying to switch Unique identifiers between 2 entities. My current solution works but is far from pretty. First i set one username to "temp" so there is no overlap in usernames. Afterwards I switch them.
public void SwitchUsernames(Guid personIdOld, Guid personIdNew)
{
// Get accounts
var personOld = GetAccount(personIdOld);
var personNew = GetAccount(personIdNew);
// switch usernames
string usernamePersonOld = personOld.username;
string usernamePersonNew = personNew.username;
try
{
personOld.SetUsername("Temp");
Context.SaveChanges();
}
catch (Exception) { throw; }
try
{
personNew.SetUsername(usernamePersonOld);
Context.SaveChanges();
}
catch (Exception)
{
personOld.SetUsername(usernamePersonOld);
Context.SaveChanges();
throw;
}
try
{
personOld.SetUsername(usernamePersonNew);
Context.SaveChanges();
}
catch (Exception)
{
personOld.SetUsername(usernamePersonOld);
Context.SaveChanges();
personNew.DomainAccounts.SetUsername(usernamePersonNew);
Context.SaveChanges();
throw;
}
}
The trycatch blocks are there to rollback everything in case something fails. If i do not save the context after every change it will fail. I really would like this a lot cleaner but i dont know if there is a way. I am here to learn so please enlighten me.
I really would like this a lot cleaner
Use a transaction, when an error occurs it will not push to the database so no need for a rollback.
Something like this:
public void SwitchUsernames(Guid personIdOld, Guid personIdNew)
{
// Get accounts
var personOld = Accounts.Find(personIdOld);
var personNew = Accounts.Find(personIdNew);
// switch usernames
string usernamePersonOld = personOld.UserName;
string usernamePersonNew = personNew.UserName;
using (var tran = _context.Database.BeginTransaction())
{
var temp = "TempName" + Guid.NewGuid().ToString();
personOld.UserName = temp;
_context.SaveChanges();
personNew.UserName = usernamePersonOld;
_context.SaveChanges();
personOld.UserName = usernamePersonNew;
_context.SaveChanges();
tran.Commit();
}
}
i getting this error message from my transaction code, and there is no other transaction uncompleted
The transaction operation cannot be performed because there are
pending requests working on this transaction.
TransactionScope tran = null;
using (tran = new TransactionScope())
{
try
{
string resNumber = repoKontrat.KontratNoUret(kontratTipi); //procedure
_seciliKontrat.ID = repoKontrat.KaydetID(seciliKontrat);//insertOnSubmit()
repoKontratKalem.KaydetProc(
_seciliKontrat.ID,
await repoKontratKalem.Getir_Table(gridKontratKalemler.ToList()),
digerKalemleriDegistir //bool
); // procedure
gp.Kaydet(_seciliKontrat.ID, gp.KontratGpTahminiveGercekHesapla(_seciliKontrat.ID));//insertOnSubmit()
gp.Kaydet(_seciliKontrat.ID, gp.KontratGpTahminiveGercekHesapla(_seciliKontrat.ID));//procedure
repoKontrat.SatisKontratiFirmaBilgileriniGuncelle(_seciliKontrat.ID);//procedure
//-----
foreach (var item in silinenKasaKayitlari)
{
repoCariHareketler.Sil(Convert.ToInt32(item));//insertOnSubmit()
}
repoCariHareketler.Kaydet(_seciliKontrat.ID, await repoCariHareketler.Getir_Table(gridKontratKasa.ToList()));//insertOnSubmit()
tran.Complete();
}
catch (Exception ex)
{
throw ex;
}
finally
{
tran = null;
}
lib.ParamsInsert is called via another dll.
The transaction is rolled back when there is a problem with the throw statement. However, the operations in paramsInsert can not be undone.
In lib.ParamsInsert, there is a transaction in its own error.
spcarikart.Repository lib = new spcarikart.Repository();
using (var transaction = db.Database.BeginTransaction())
{
try
{
var result = db.Acenta.Add(obj).Entity;
var a = lib.ParamsInsert(db, new Params
{
Baslik = "Bahdir",
Deger = "1"
});
// ....Maybe Error other process
db.SaveChanges();
return result;
}
catch (Exception ex)
{
transaction.Rollback();
}
}
ParamsInsert
using (var transaction = db.Database.BeginTransaction())
{
try
{
var resul = db.Params.Add(obj).Entity;
db.SaveChanges();
transaction.Commit();
return resul;
}
catch (Exception ex)
{
transaction.Rollback();
throw new Exception();
}
}
They use different transactions. Don't start new transaction, as I see, you work with the same db context, so needn't start transaction in paramsInsert, remove using (var transaction = db.Database.BeginTransaction()) from it.
Here's my code:
TransactionScope trans = new TransactionScope();
dbDataContext db = new dbDataContext()
// did some insert
var user = new User();
user.ID = 1;
user.Name = "Nick";
db.Users.Add(user);
db.SubmitChanges();
trans.Complete();
Now how do I rollback transaction if some error occurs while saving changes?
There are two ways in which you can use transactions in entity framework. One using TransactionScope and another using database transaction.
For using database transaction, use the following code example:
using (dbDataContext db = new dbDataContext())
using (var dbContextTransaction = db.Database.BeginTransaction())
{
try
{
var user = new User(){ID = 1, Name = "Nick"};
db.Users.Add(user);
db.SaveChanges();
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
And here's an example of EF's TransactionScope:
using (TransactionScope tranScope = new TransactionScope())
using (dbDataContext db = new dbDataContext())
{
try
{
var user = new User(){ID = 1, Name = "Nick"};
db.Users.Add(user);
db.SaveChanges();
tranScope.Complete();
}
catch(Exception ex){}
}
As you can see, you don't need to call any Roll-Back method in the case of TransactionScope. If you don't call the Complete() method, the transaction does not get committed and gets rolled back automatically before being disposed.