DataContext.Submit and TransactionScope - c#

Please explain me pseudocode below.
My idea is: 3-nd SubmitChanges will commit o.Status and will not commit o.TransactionId, and my object will get corrupted in database (I mean it will not be consistent anymore).
XDataContext DB = .....;
XOrder o = DB.XOrders.Single(.......);
try
{
using (var t = new TransactionScope())
{
o.Status = XOrderStatus.Completed;
DB.SubmitChanges(); // 1
string s = null;
s.Trim(); // crash here;
o.TransactionId = ......; // some calculations here
DB.SubmitChanges(); // 2
t.Complete();
}
}
catch (Exception ex)
{
XEvent e = new XEvent();
e.Type = XEventType.Exception;
e.Data = .........; // some calculations here
DB.XEvents.InsertOnSubmit(e);
DB.SubmitChanges(); // 3
}
Is it any 'best practices' for my case?

Is it any 'best practices' for my case?
Yes. Use one DataContext instance per unit of work.
catch (Exception ex)
{
XEvent e = new XEvent();
e.Type = XEventType.Exception;
e.Data = .........; // some calculations here
using (XDataContext dc2 = new XDataContext())
{
dc2.XEvents.InsertOnSubmit(e);
dc2.SubmitChanges(); // 3
}
}

Related

Proper way to calculate order via entity framework / sql

Is this a good way to set the transaction level to serializable? If I set a lower transaction level, I will get duplicate orders. What is better approach? I assume this method will be invoked frequently by different users.
Here is my code
using (var tran = context.Database.BeginTransaction(IsolationLevel.Serializable))
{
try
{
var lastDocument = context.Documents.OrderByDescending(x => x.Id).FirstOrDefault();
int order = 1;
if (lastDocument != null)
{
order = lastDocument.Order + 1;
}
var document = new Document
{
CreatedDate = DateTimeOffset.UtcNow,
Name = Guid.NewGuid().ToString(),
Order = order
};
context.Documents.Add(document);
context.SaveChanges();
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
}
}

Setting Controls in Classes

I'm trying a booking system, I want to put controls on the booking aspect. I want to use If and then cases. I want to control in such a way that if number of booking is 4, then it will throw an exception and stop inserting in the database.
public ApiResult<TimeModelExtended> SaveBooking(Booking booking)
{
AchimotaGCDb repo = new AchimotaGCDb();
var result = new ApiResult<TimeModelExtended>();
try
{
booking.PlayDate = getPlayDate(booking.RefCode);
Int16 nb = getNbBooked(booking.RefCode);
if (nb == 4)
{
Exception ex = new Exception();
result.Successfull = 0;
result.InternalError = ex.Message;
result.Error = "Booking slot is full";
}
else if (nb == 0)
{
booking.BookingStatus = 1;//Booked already
}
else
{
booking.BookingStatus = 0;//Reservation already
}
repo.Insert(booking);
result.Successfull = 1;
result = GetOneteeTime(booking.RefCode);
}
catch (Exception ex)
{
result.Successfull = 0;
result.InternalError = ex.Message;
result.Error = "Error from server";
}
finally
{
repo.Dispose();
}
return result;
}
help to solve that.
If you want to throw an exception, you need to really throw it:
if (nb == 4)
{
throw new Exception("Booking slot is full.");
}
But I don't think throwing an exception is a good idea. Throwing an exception and validation is a different thing.
Here is my suggestion:
if (nb == 4)
{
return result = new ApiResult<TimeModelExtended>()
{
Successfull = 0,
InternalError = "Other messages",
Error = ""Booking slot is full."
};
}
This will return as result message that nothing will continue unless you satisfy that nb != 4

Same function for updating and Saving?

My code to use the function for updating is here and it works also
[HttpPost]
public bool SaveDefCompny(DefCompanyDTO DefCmpny)
{
using (RPDBEntities db = new RPDBEntities())
{
using (TransactionScope trans = new TransactionScope())
{
//the problem is here incase of saving
var UpdateDefCmpnyId = (from CmpnyId in db.DefCompanies
where CmpnyId.Id == DefCmpny.Id
select CmpnyId).First();
List<DefCompany> list = new List<DefCompany>();
list.Add(UpdateDefCmpnyId);
try
{
foreach (DefCompany DefCmpny1 in list)
{
DefCmpny1.Id = DefCmpny1.Id;
DefCmpny1.ShortName = DefCmpny.ShortName;
DefCmpny1.FullName = DefCmpny.FullName;
DefCmpny1.ContactPerson = DefCmpny.ContactPerson;
DefCmpny1.Address1 = DefCmpny.Address1;
DefCmpny1.CompanyCity = DefCmpny.CompanyCity;
DefCmpny1.CompanyState = DefCmpny.CompanyState;
DefCmpny1.CompanyCountry = DefCmpny.CompanyCountry;
DefCmpny1.ZipPostCode = DefCmpny.ZipPostCode;
DefCmpny1.TelArea = DefCmpny.TelArea;
DefCmpny1.CurrentCurrencyCode = DefCmpny.CurrentCurrencyCode;
db.SaveChanges();
trans.Complete();
}
}
catch (Exception ex)
{
}
}
return false;
}
}
when I try to save instead of updating the line of code
var UpdateDefCmpnyId = (from CmpnyId in db.DefCompanies
where CmpnyId.Id == DefCmpny.Id
select CmpnyId).First();
gives null value and hence saving fails because record is new and not present in database so how to handle null in case of saving how to use try catch so that when value is null it proceed to saving code that add
How about something along these lines:
var UpdateDefCmpnyId = (from CmpnyId in db.DefCompanies
where CmpnyId.Id == DefCmpny.Id
select CmpnyId).FirstOrDefault();
if(UpdateDefCmpnyId == null)
{
//insert
//(handle the id however you need to for insert. depending on your setup, you might be able to leave it empty and let the database put it in for you)
}
else
{
//update
//set the id as you do in the question
}

SqlException: do not abort a transaction

I have a code that adds data to two EntityFramework 6 DataContexts, like this:
using(var scope = new TransactionScope())
{
using(var requestsCtx = new RequestsContext())
{
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
logsCtx.SaveChanges();
}
}
}
There is an insert trigger in Requests table that rejects some values using RAISEERROR. This situation is normal and should be handled by the try-catch block where the SaveChanges method is invoked. If the second SaveChanges method fails, however, the changes to both DataContexts must be reverted entirely - hence the transaction scope.
Here goes the error: when requestsCtx.SaveChanges() throws a exception, the whole Transaction.Current has its state set to Aborted and the latter logsCtx.SaveChanges() fails with the following:
TransactionException:
The operation is not valid for the state of the transaction.
Why is this happening and how do tell EF that the first exception is not critical?
Really not sure if this will work, but it might be worth trying.
private void SaveChanges()
{
using(var scope = new TransactionScope())
{
var log = CreateRequest();
bool saveLogSuccess = CreateLogEntry(log);
if (saveLogSuccess)
{
scope.Complete();
}
}
}
private LogEntry CreateRequest()
{
var req = new Request { Id = 1, Value = 2 };
var log = new LogEntry { RequestId = 1, State = "OK" };
using(var requestsCtx = new RequestsContext())
{
requestsCtx.Requests.Add(req);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
finally
{
return log;
}
}
}
private bool CreateLogEntry(LogEntry log)
{
using(var logsCtx = new LogsContext())
{
try
{
logsCtx.Logs.Add(log);
logsCtx.SaveChanges();
}
catch (Exception)
{
return false;
}
return true;
}
}
from the documentation on transactionscope: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx
If no exception occurs within the transaction scope (that is, between
the initialization of the TransactionScope object and the calling of
its Dispose method), then the transaction in which the scope
participates is allowed to proceed. If an exception does occur within
the transaction scope, the transaction in which it participates will
be rolled back.
Basically as soon as an exception is encountered, the transaction is rolled back (as it seems you're aware) - I think this might work but am really not sure and can't test to confirm. It seems like this goes against the intended use of transaction scope, and I'm not familiar enough with exception handling/bubbling, but maybe it will help! :)
I think I finally figured it out. The trick was to use an isolated transaction for the first SaveChanges:
using(var requestsCtx = new RequestsContext())
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
using(var outerScope = new TransactionScope())
{
using(var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
requestsCtx.SaveChanges();
innerScope.Complete();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
}
logsCtx.SaveChanges();
outerScope.Complete();
}
}
Warning: most of the articles about RequiresNew mode discourage using it due to performance reasons. It works perfectly for my scenario, however if there are any side effects that I'm unaware of, please let me know.

Detach Entity Collection

Well, i have a Model with a collection saving changes in a loop structure
foreach(Customer objCustomer in listCustomer)
{
try
{
db.Customer.Add(objCustomer);
db.SaveChanges();
}
catch(Exception ex)
{
db.Entry(objCustomer).State = EntityState.Detach;
}
}
When it throws me any exception in a collection related to entity, the next ones keeps throwing exceptions.
I tried to detach the entire collection but it didn't work
foreach(Customer objCustomer in listCustomer)
{
try
{
db.Customer.Add(objCustomer);
db.SaveChanges();
}
catch(Exception ex)
{
for (int i = 0; i < objCustomer.Address; i++)
{
db.Entry(objCustomer.Address[i]).State = EntityState.Detach;
}
db.Entry(objCustomer).State = EntityState.Detach;
}
}
Any suggestion?
I don't know why but it works, i used like it refers in this post
EntityCollection Clear() and Remove() methods
objCustomer.Address.ToList().ForEach(x => db.Entry(x).State = EntityState.Detached);
It is almost as i did before using "for"
Thanks anyway everyone
i thought that the exception of second code maybe has been never occurred?
In addition,your code has any transaction ?
Try this? Warning not tested!
using(var db = new Context())
{
foreach(Customer objCustomer in listCustomer)
{
var exists = db.Customer.FirstOrDefault(x => x.CustomerID == objCustomer.CustomerID;
if( exists = null)
{
db.Customer.Add(objCustomer);
}
else
{
db.Entry(objCustomer).State = EntityState.Modified;
}
db.SaveChanges();
}
}

Categories

Resources