when using,
$transaction = Yii::app()->db->beginTransaction();
After this code cant update and insert data in Database.
Why?
Check if you do a proper commit and for get eventual error
you should follow the common scenario
try
{
$transaction= Yii::app()->db->beginTransaction();
Yii::app()->db->createCommand($sql1)->execute();
Yii::app()->db->createCommand($sql2)->execute();
//.... other SQL executions
Yii::app()->db->commit();
}
catch(Exception $e)
{
Yii::app()->db->rollBack();
}
In this way you can manage exception and show the eventual reason of yor fails or see the result in db after you do the commit.
Related
What's the best way to handle transaction errors in Asp.net core and entity framework?
At this time I have come with something like:
using (var transaction = _dbContext.Database.BeginTransaction())
{
try
{
await _dbContext.MyTable1.AddAsync(table1Entity);
await _dbContext.SaveChangesAsync();
Entity2 e2 = new Entity2();
e2.Table1Id = table1Entity.Id;
await _dbContext.SaveChangesAsync();
await transaction.CommitAsync();
return new CreatedAtRouteResult(...);
}
catch (Exception ex)
{
System.Console.Write(ex);
await transaction.RollbackAsync();
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
return Problem(
detail: context.Error.StackTrace,
title: context.Error.Message);
}
}
But don't really know if this is a good practice. How would you do this?
There is nothing wrong with the way you are handling the transactions, but there are some improvements you can make here:
Remove the data access code from your controller and move it into a separate class.
Do not return the technical details of the error, but a user friendly message.
AddAsync only exists for special use cases, all other cases should use the non-async method Add.
From the EF docs:
"This method is async only to allow special value generators, such as
the one used by
'Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo',
to access the database asynchronously. For all other cases the non
async method should be used."
I think your code was good, except you don't need to call:
await transaction.RollbackAsync();
in catch block. Failed transaction will auto-rollback when disposed.
Link: https://learn.microsoft.com/en-us/ef/core/saving/transactions
I am making a call to the SQL database via Entity Framework, this call takes about 2 mins to execute.
I want to make sure this call only occurs once. Once the call is made, I place the results in cache. I notice if multiple users are on the site, it can be more than 2 mins till the data is returned, whats the best way to approach this? Should I use a mutex? or does Entity Framework (version 4) have any functionality built in to handle this type of situation. I am using MVC 4. Thank you!
public IEnumerable<AdListing> AllActiveAds()
{
try
{
if (PullCache(Constants.CacheKeys.AllActiveAds) == null)
{
using (var db = new MyEntities())
{
db.CommandTimeout = 300;
List<AdListing> results =
(from a in
db.AdListings
.Include("AdPhotos")
.Include("tbLocation")
where !a.Deleted
select a).ToList();
PushCache(results, Constants.CacheKeys.AllActiveAds);
}
}
return (List<AdListing>) PullCache(Constants.CacheKeys.AllActiveAds);
}
catch (Exception ex)
{
HandleException(ex);
return null;
}
}
We're using NHibernate in a project that gets data out of the database and writes reports to a separate system. In my scenario, a patient will usually, but not always, have a next appointment scheduled when the report gets written. The query below gets the next appointment data, to include in the report.
private NextFollowup GetNextFollowup(int EncounterID)
{
try
{
NextFollowup myNextF = new NextFollowup();
IQuery myNextQ = this.Session.GetNamedQuery("GetNextFollowup").SetInt32("EncounterID", EncounterID);
myNextF = myNextQ.UniqueResult<NextFollowup>();
return myNextF;
}
catch (Exception e)
{
throw e;
}
}
Here's the question:
Usually this works fine, as there is a single result when an appointment is scheduled. However, in the cases where there is no next followup, I get the error that there is no unique result. I don't really want to throw an exception in this case, I want to return the empty object. If I were to get a list instead of a UniqueResult, I'd get an empty list in the situations where there is no next followup. Is there a better way to handle the situation of "when there is a value, there will be only one" than using a list in the HQL query?
This may work:
private NextFollowup GetNextFollowup(int encounterID)
{
IQuery query = this.Session.GetNamedQuery("GetNextFollowup").SetInt32("EncounterID", encounterID);
// nextFollowup will be either the next instance, or null if none exist in the db.
var nextFollowup = query.Enumerable<NextFollowup>().SingleOrDefault();
return nextFollowup;
}
Note: updated naming to follow Microsoft best practices
The try catch is not serving any purpose here except to loose the stack trace if there is an exception so I've removed it.
If you want to return a new NextFollowup if none exist, you can update the query line to:
var nextFollowup = query.Enumerable<NextFollowup>().SingleOrDefault() ?? new NextFollowup();
I am trying to find the perfect way to handle this exception and force client changes to overwrite any other changes that caused the conflict. The approach that I came up with is to wrap the call to Session.Transaction.Commit() in a loop, inside the loop I would do a try-catch block and handle each stale object individually by copying its properties, except row-version property then refreshing the object to get latest DB data then recopying original values to the refreshed object and then doing a merge. Once I loop I will commit and if any other StaleObjectStateException take place then the same applies. The loop keeps looping until all conflicts are resolved.
This method is part of a UnitOfWork class. To make it clearer I'll post my code:
// 'Client-wins' rules, any conflicts found will always cause client changes to
// overwrite anything else.
public void CommitAndRefresh() {
bool saveFailed;
do {
try {
_session.Transaction.Commit();
_session.BeginTransaction();
saveFailed = false;
} catch (StaleObjectStateException ex) {
saveFailed = true;
// Get the staled object with client changes
var staleObject = _session.Get(ex.EntityName, ex.Identifier);
// Extract the row-version property name
IClassMetadata meta = _sessionFactory.GetClassMetadata(ex.EntityName);
string rowVersionPropertyName = meta.PropertyNames[meta.VersionProperty] as string;
// Store all property values from client changes
var propertyValues = new Dictionary<string, object>();
var publicProperties = staleObject.GetType().GetProperties();
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
propertyValues.Add(p.Name, p.GetValue(staleObject, null));
}
}
// Get latest data for staled object from the database
_session.Refresh(staleObject);
// Update the data with the original client changes except for row-version
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
p.SetValue(staleObject, propertyValues[p.Name], null);
}
}
// Merge
_session.Merge(staleObject);
}
} while (saveFailed);
}
The above code works fine and handle concurrency with the client-wins rule. However, I was wondering if there is any built-in capabilities in NHibernate to do this for me or if there is a better way to handle this.
Thanks in advance,
What you're describing is a lack of concurrency checking. If you don't use a concurrency strategy (optimistic-lock, version or pessimistic), StaleStateObjectException will not be thrown and the update will be issued.
Okay, now I understand your use case. One important point is that the ISession should be discarded after an exception is thrown. You can use ISession.Merge to merge changes between a detached a persistent object rather than doing it yourself. Unfortunately, Merge does not cascade to child objects so you still need to walk the object graph yourself. So the implementation would look something like:
catch (StaleObjectStateException ex)
{
if (isPowerUser)
{
var newSession = GetSession();
// Merge will automatically get first
newSession.Merge(staleObject);
newSession.Flush();
}
}
I am looking for a simple NHibernate example which will show me how iterate on an entire table. Here is what I have so far, but it is not working. I am getting an "System.InvalidOperationException: Operation is not valid due to the current state of the object.". What am I doing wrong?
public IEnumerable<EMPDATA> getEMPData()
{
using (ISession session = NHibernateHelper.OpenSession())
{
IEnumerable<EMPDATA> empData = session.CreateQuery("from EMPDATA").Enumerable<EMPDATA>();
return empData;
}
}
public static void Main(System.String[] args)
{
log.Debug("Entered main");
Console.WriteLine("Entered main");
try
{
IEMPDataRepository repository = new EMPDataRepository();
IEnumerable<EMPDATA> iterList = repository.getEMPData();
while( iterList.GetEnumerator().MoveNext())
{
EMPDATA emp = iterList.GetEnumerator().Current;
log.Debug(emp.EMP_ID);
}
}
catch (System.Exception ex)
{
log.Error("Exception occured reading emp data", ex);
}
Here is my mapping:
You request an Enumerable result, which probably relies on the session still beeing open.
since you Dispose the session after returning the Enumerable instance, you have closed the connection to the database.
EDIT: see NotSupportedException on IQuery's Enumerable when using statelesssession
Short answer: use .List instead of .Enumerable.
Longer answer:
1. I agree with Phill- looks like a job for a SP
2. Diego is (obviously) right, but if I were you i'd use SetFirstResult() and SetMaxResult() in order to control the amount of data you load into memory in each iteration (don't forget to sort by something when using this method, of course).