I have question about managing transaction in asp.net application.
For example i have application for planning vacations.
Controller has form to approving vacations.
One user - click save and approve vacation ---- employee which want vacation has - 1 day
second user - clik save and approve vacation and ?
//pseudocode
public void ApproveVacation(int vacationId)
{
//pull vacationdata from db
var vacation = _dbContext.Vacations.FirstOrDefault(x => x.Id == vacationId);
if (vacation != null && vacation.State != approved) //
{
using (TransactionScope scope = new TransactionScope())
{
vacation.state = approved;
vacation.Employee.Days = -1;
_dbContext.saveChanges();
scope.complete();
}
}
}
And question is simple, is transaction enough for this scenario or I must use one of concurency technique?
Thanks
EDIT : Context is created one per request.
Transaction handles atomicity of the operation so if operation modifies multiple database records it will always result in consistent state where all records are correctly modified (if operation succeeds) or all changes are rolled back (if operation fails).
Concurrency handles possible modification of the same record by multiple processes / users because both could load original version of the record but one could save it first so when the second process tries to save a record it can silently override previous changes.
So what are you trying to handle in your code?
You already have an implicit transaction when calling 'SaveChanges' so there's no need for a transaction scope.
And also, if you would change several items you would need to start the TransactionScope before you retrieve the data.
Related
I have an Ecommerce website build with UCommerce. During the checkout process the user will be redirected to the payment portal for the payment.
I want to prevent users from adding new items in the basket while the user is in the payment portal. My current solution is to save the basket to a Session before redirecting the user to the payment portal.
Session["checkoutOrder"] = TransactionLibrary.GetBasket(!TransactionLibrary.HasBasket()).PurchaseOrder;
How can I overwrite the current basket with the one in the Session After the payment? This is to revert the basket to its original state before the payment.
I tried this:
[HttpPost]
public ActionResult ExecutePayment()
{
var order = Session["checkoutOrder"] as PurchaseOrder;
order.Save();
...
}
But I'm getting an error on order.Save():
Batch update returned unexpected row count from update; actual row count: 0; expected: 1
I'd just add to this as well that your Session["orderInProcess"] is an anti pattern in uCommerce. You may run into nasty exceptions as you're persisting NHibernate entities through requests which can/will lead to Session disposed exceptions. It may also lead to the initial exception that you're experiencing as you're actually by-passing the sesssion state of NHibernate.
Just use TransactionLibrary.GetBasket(!TransactionLibrary.HasBasket()).PurchaseOrder; every time you're retrieving your basket. NHibernate will take care of caching the order for you.
Then you can use order properties to save the state you're in.
var basket = TransactionLibrary.GetBasket(!TransactionLibrary.HasBasket()).PurchaseOrder;
basket["CheckoutInProcess"] = "True";
Best regards
Morten
I handled this differently since I have no way of reverting back the basket to its original state.
I decided to block the user from adding items in the basket when the payment is in process.
I created a session Session["orderInProcess"]=true before I redirect the user to the payment gateway.
Now every time the user tries to add a new item in the basket, I will check first if his current order is in process. like so:
[HttpPost]
public ActionResult AddToBasket(string sku, string quantity, string variant = null)
{
if (Session["orderInProcess"] != null)
{
if (bool.Parse(Session["orderInProcess"].ToString()))
{
return Json(new
{
Success = false,
ErrorMessage = "Order is currently in process."
});
}
}
.....
}
I hope this helps.
I had a Posting on a blog about Sessions AND Cookies. Here are details
Sessions
Sessions are More Secure
Sessions are on the server
Cookies
Cookies are On client side
Less Secure
Once it is disable on browser the difficult to use.
On the basis of above argument i used sessions in Login system to keep UserId,UserName & roleName
Now on the the basis of roleName i will decide either this is Admin to enter to administrator section or not.
I have used this Code in Model in MVC
public bool LoginMe()
{
Int64 Error;
//create db
Database db = DatabaseFactory.CreateDatabase("DBContext");
DbCommand dbCommand = db.GetStoredProcCommand("ValidateUser");
db.AddInParameter(dbCommand, "#Username", DbType.String, this.UserName);
db.AddInParameter(dbCommand, "#Password", DbType.String, EncryptPassword(this.Password));
db.AddOutParameter(dbCommand, "#Error", DbType.Int64, 10);
DataSet dsResult = db.ExecuteDataSet(dbCommand);
Error = Convert.ToInt64(db.GetParameterValue(dbCommand, "#Error"));
if (Error == 1100)
{
try
{
var query = (from o in dsResult.Tables[0].AsEnumerable()
select new AllUser
{
UserId = o.Field<int>("UserId"),
UserName = o.Field<string>("UserName"),
roleName = o.Field<string>("roleName"),
}).Single(); // this will raise an exception if there isn't just one record returned
Session["UserId"] = query.UserId;
Session["UserName"] = query.UserName;
Session["roleName"] = query.roleName;
return true;
}
catch {
// do nothing and let method return false as something has gone wrong.
// add logging here if you are using it to show there has been a problem
}
}
return false;
}
I used it in View like #Session["UserId"]
Now an expert comment on this like
If you aren't using https and securing the session cookie then this might make it easy to hack your site, although that's the same for any session based site (nearly all of them)
It might be nice to add some check so that if you remove a user's rights, the session variables are deleted the next time that user requests something from the server,
otherwise they could carry on using the site even though their account it banned.You'd have to decide if this is likely and then how you want to do this (using an authorization filter maybe.)
Above comments confused me.Can any body make it clear?What is the best way to keep these information?
Session state uses client tickets to identify the server-side session, it may be susceptible to session ID spoofing and injection attacks.
So, to hack session values one would require hacking the remote-server.
And yes, for highly secure application(such as online banking) use https.
http://msdn.microsoft.com/en-us/magazine/cc163730.aspx#S9
Secure sockets layer (SSL) should be used to prevent network-level sniffing of session IDs, authentication tickets, application cookies, and other request/response information.
Can session value be hacked?
Use HTTPS if you application handles sensitive information(credit-card number,account num,passwords).
Store the User object (model with userId,username,role) in the session than separate attributes
Set setHttpOnly attribute for SESSION_ID.
It might be costly to refresh the User object stored in session before invoking every operation to reflect the current rights stored in database.
Hi we are currently working on kendo UI Scheduler and wanting to make the scheduler real time using SignalR.
What we are trying to achieve is if 2 customers are viewing the scheduler at the same time and client 1 makes a booking the 2nd client will see that someone has booked that particular time slot so that double booking does not occur.
also if a client makes a booking on the scheduler then the admin will also see the booking in real time.
currently we have the scheduler inserting to the database with no problem, from there we want to broadcast the newly created booking to all others who are viewing the scheduler at that time.
can this be done? if so any ideas.
i can supply code to what we have done upto now if need required.
my thoughts are to broadcast the new scheduler booking in the ActionScript method then broadcast the new booking to clients from there.
public ActionResult Tasks_Create([DataSourceRequest]DataSourceRequest request, TaskViewModel task)
{
if (ModelState.IsValid)
{
using (var sampleDB = new SampleEntities())
{
//Create a new Task entity and set its properties from the posted TaskViewModel
var entity = new Task
{
TaskID = task.TaskID,
Title = task.Title,
Start = task.Start,
End = task.End,
Description = task.Description,
RecurrenceRule = task.RecurrenceRule,
RecurrenceException = task.RecurrenceException,
RecurrenceID = task.RecurrenceID,
IsAllDay = task.IsAllDay,
OwnerID = task.OwnerID
};
sampleDB.Tasks.Add(entity);
sampleDB.SaveChanges();
task.TaskID = entity.TaskID;
}
}
(i was thinking to broadcast the new booking here using signalr ????)
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
Yes, it can be done (and broadcasting from your controller action is a reasonable approach). You'll probably want to create a group for people who are looking at the same data.
Take a look at this section in the docs on how to call client hub methods from non-hub classes.
I am trying to update an entity from a WCF client as follows:
Ctxt.MergeOption = MergeOption.NoTracking;
var q = Ctxt.Customers.Where(p => p.MasterCustomerId == "JEFFERSON").Select(o => o);
//DataServiceCollection<Customer> oCustomers = new DataServiceCollection<Customer>(q, TrackingMode.None);
DataServiceCollection<Customer> oCustomers = new DataServiceCollection<Customer>(q);
oCustomers[0].FirstName = "KEFFERSON";
//Ctxt.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
//ctxt.SaveChangesDefaultOptions = SaveChangesOptions.ReplaceOnUpdate;
Ctxt.SaveChanges();
When I try to save the modified entity, it first tries to load that entity using a select query (to database) and then issues update statement to database.
In my case, I simply want to have the entity to be directly updated in the database without fetching it first. I don't mind if it overwrites the data in database
I tried the following at WCF service:
protected override EF.Model.DataModel.PersonifyEntities CreateDataSource()
{
var ctxt = new EF.Model.DataModel.PersonifyEntities();
ctxt.Customers.MergeOption = System.Data.Objects.MergeOption.NoTracking;
ctxt.ContextOptions.ProxyCreationEnabled = false;
ctxt.ContextOptions.LazyLoadingEnabled = false;
return ctxt;
}
But, no luck. Can anyone help me on this?
For WCF DataServices, the client can only update entities that it tracks. So it has to have the entity downloaded in the client before it can make any changes and save it back. Thats why you see the fetch (I am assuming that this is the first fetch that you are seeing for that specific entity) before the update. Hope this helps.
My symfony edit form validates and saves. When I check the record in mysql query browser the record updates, but when the page refreshes the same data is there and the form hasn't updated. Any thoughts?
public function executeEdit(sfWebRequest $request)
{
$this->forward404Unless($items = Doctrine::getTable('items')->find(array($request->getParameter('item_id'))), sprintf('Object items does not exist (%s).', $request->getParameter('item_id')));
//make sure the item being edited is owned by the logged in user
$this->forward404Unless($items->getUser_id()==$this->getUser()->getGuardUser()->getId());
$cacheDir = sfConfig::get('sf_cache_dir').'/'. $app.'/'.$env.'/';
//Clear cache
$cache = new sfFileCache(array('cache_dir' => $cacheDir));
$cache->clean();
//set category id
$query=Doctrine_Query::create()
->select('name')
->from('categories')
->where('category_id="'.$items->category_id.'"')
->limit(1);
$category=$query->fetchArray();
#$items->category_id=$category[0]['name'];
$this->form = new itemsUserForm($items);
}
Two possibilities:
You have an open transaction in MySQL
You're not looking at the record in the browser you think you're looking at
I would make sure that #1 is not the case first. I believe you can do this by simply running the command
ROLLBACK;
If you have a transaction open, that will close it.