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.
Related
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 have a link on a grid in my AdminUsers view
grid.Column(header: "", format: (item) => (condition ? Html.ActionLink("Impersonate", "Impersonate", "Admin", new { id = item.username }, null) : Html.Label("Impersonate"), style: "webgrid-column-link"),
In the controller, I have
public ActionResult Impersonate(string id)
{
string result = ORCA.utilities.users.setImpersonation(id);
if(result == "nocommonfields")
return RedirectToAction("AdminUsers", "Admin");
else
return RedirectToAction("terms_of_use", "Forms");
}
How can send an error message to display when I return to the AdminUsers page?
You may use TempData
if(result == "nocommonfields")
{
TempData["ErrorMessage"]="This is the message";
return RedirectToAction("AdminUsers", "Admin");
}
and in your AdminUsers action, you can read it
public ActionResult AdminUsers()
{
var errMsg=TempData["ErrorMessage"] as string;
//check errMsg value do whatever you want now as needed
}
Remember, TempData has very short-life span. Session is the backup storage behind temp data.
Alternatively, You may also consider sending a flag in your querystring and read it in your next action method and decide what error message to show.
The TempData controller property can be used to achieve this kind of functionality. Its main drawback in my opinion is that it uses the session storage in to store its contents. This means that you'll have extra work getting it to function on a web farm, or that you need to turn on sessions in the first place.
The good thing about TempData is that is exactly does what you want. Its a string based dictionary and you can put anything in it and by default get it out only once. So before calling RedirectToAction() you set your message. On the next request you check for messages and display them. By retrieving the messages they are automatically deleted at the end of the request.
As an alternative you could use cookies for transporting the message between the two requests. Essentially you could either roll your own solution, or implement a custom ITempDataProvider which transports the contents of TempData via cookies. Note that you need to properly secure cookies. MachineKey.Protect() can help you if you are rolling your own.
I was facing the same problem you did and created a solution for it called FlashMessage. Perhaps this could save you some work. It's available on NuGet as well. Usage is simple: you simply queue a message before you call RedirectToAction() as follows:
if(result == "nocommonfields")
{
FlashMessage.Warning("Your error message");
return RedirectToAction("AdminUsers", "Admin");
}
In your view you include the following statement to render any previously queued messages:
#Html.RenderFlashMessages()
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.
I am storing user objects in session, pulling them out in the controllers, and sometimes write some data into them. but when to users post at the same time, the sessions get mixed fro some reason.
Does anyone have any idea how that is possible ?
typical post:
[HttpPost]
public ActionResult Index(QuestionModel model, FormCollection collection)
{
var person = ((Person)Session["Person"]);
if (!ModelState.IsValid)
{
ModelState.Clear();
ModelState.AddModelError("", Global.Global.error);
return View(new QuestionModel(person.page, (CultureInfo)Session["culture"]));
}
person.page = model.Page;
while (person.Answers.Count > model.Page - 1)
{
person.Answers.RemoveAt(person.Answers.Count - 1);
}
var answer = new Answer() { answer = model.ChosenAnswer, Question = "Q" + person.page };
person.Answers.Add(answer);
if (!CheckForNextPage(person.page)) { person.hasFinishedQuestions = true; return RedirectToRoute("Result"); }
person.page++;
return View(new QuestionModel(person.page, (CultureInfo)Session["culture"]));
}
I echo the session id on every page, and when a couple of users are using the website they get each others session + sessionid ...
#update: 3 experienced developers have been looking for the problem for 2 days, still no solution. already removed about 95% off the code, still same issue. server posts back responses from another session
This is not possible.
So this is my guess:
You are testing this wrongly, you are using different tabs from the same browser.
Some people don't know that this doesn't create a different session.
Try testing this on 2 different browsers (i.e. firefox and chrome) as they will not share the session (as the session id is normally stored in a cookie).
Please report back if this was the case.
We "solved" it. We didn't actually solve it, but we copied all the sources to a new project, recompiled, and everything worked. Untill this day, we still don't know why, and how that error happened ...