thread sleep executes directly when I open the page - c#

I'm trying to do make some proccess in the web page and then I want the page sleep like 30 second.
for example
public ActionResult Generate()
{
var formsIdentity = (FormsIdentity)HttpContext.User.Identity;
var ticket = FormsAuthentication.Decrypt(formsIdentity.Ticket.Name);
Customer customer = _customerService.Get(x => x.Email == ticket.Name).FirstOrDefault();
if (model.OldPassword == customer.Password)
{
customer.Password = model.NewPassword;
_customerService.Update(customer, customer.Id);
return view();
}
Thread.Sleep(30000);
customer.Password = "something";
_customerService.Update(customer, customer.Id);
return view();
}
In here for example I want to change password via user input. After password change I want the page sleep 30 second and then update the data in database again. But if use thread.sleep page and I try to open the page it doesnt open for 30 second and then it opens so how can I fix it? Changing password is just an example.

You should not perform such operations in controller actions you need to use some kind of background processing. Since it seems that you are using .NET Framework so the app possibly hosted inside IIS (which can recycle app pool) then you should look into libraries for standalone background processing like Hangfire or Quartz.NET.

Related

Write to database and return at the same time

I have a situation where I wish to return data or redirect the user elsewhere, and also log information to a database at the same time.
My current code is similar to the following:
public async Task<IActionResult> SendUser(string target)
{
Target dbTarget = await DbContext.Targets.SingleOrDefaultAsync(target => target.url == target);
dbTarget.LastAccess = DateTime.Now();
dbTarget.TimesAccessed = dbTarget.TimesAccessed++;
DbContext.Targets.Update(dbTarget);
DbContext.SaveChangesAsync();
Redirect(target);
}
I would like this to Redirect the user, but log to the database seperately, without the users request having for the database update to complete.
One solution is to create a seperate method in the controller and just POST the new log item to it, which would cause a seperate request, however I wonder if there is a better method that does not require adding extra authentication steps and methods?
Try instead of DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync(); or just DbContext.SaveChanges(); Redirect will wait till all changes are saved.

How to wait after a database value then continue

I got an e-commerce website that uses the PayPal Adaptive Payment. The Adaptive Payment requests a PayKey to be generated from PayPal to create an invoice. The delay to get the PayKey is long enough so I had the idea to put the code in a separate thread, while the user answer some other questions before being redirected to PayPal, see code below:
await Task.Run<Task>(async () =>
{
var payResponse = await _payPalApplicationService.ProceedWithPayPal(currentEvent.Id, order.InvoiceId, order.TrackingId, owners.Single(), vModel.TotalPrice, vModel.DeliveryPriceTotal, orderToAdd.TotalTaxes, orderToAdd.SalesRate + orderToAdd.SalesRateTaxes, vModel.SKUViewModels, _payPalApplicationService.PayPalCore._serviceEndPointUrl);
order.PayKey = payResponse.payKey;
_orderService.Update(order);
await _unitOfWorkAsync.SaveChangesAsync();
});
The problem I got is some users can go quick enough so the PayKey has not been generated before being redirected to PayPal.
Did you know anything I can do to make sure I got the PayKey before redirect the users to PayPal? The thread task is done in a different controller action than the one with the redirection.
Thank you
David
It arguably violates an MVC principle of statelessness but a really simple solution would be to store the PayKey retrieval task against the session.
So change the above code to:
Session["PaypalTask"] = await Task.Run<Task>(async () =>
{
var payResponse = await _payPalApplicationService.ProceedWithPayPal(currentEvent.Id, order.InvoiceId, order.TrackingId, owners.Single(), vModel.TotalPrice, vModel.DeliveryPriceTotal, orderToAdd.TotalTaxes, orderToAdd.SalesRate + orderToAdd.SalesRateTaxes, vModel.SKUViewModels, _payPalApplicationService.PayPalCore._serviceEndPointUrl);
order.PayKey = payResponse.payKey;
_orderService.Update(order);
await _unitOfWorkAsync.SaveChangesAsync();
});
Then you can later retrieve the task in your other controller and await it there. If it has already completed exectution will just continue immeadiately, else it will wait for it to complete before continuing.
Something like this
public async Task<ActionResult> Index()
{
var payPalTask = Session["PaypalTask"] as Task;
await payPalTask;
return RedirectToAction("CompltedPayment");
}
Of course, you may also want to consider error handling etc.
UPDATE
I should mention that the above method stores session state in memory. There are some complications if your application uses multiple servers where you may want to research sticky sessions, or some kind of distributed cache like redis.
UPDATE2:
I have published a demo mocking out the async method with a simple Task.Delay to here. If you build this web app then navigate to /InitiatePp it will start the request. Then /PpResult/Result will give the status of the running task and /PpResult/Wait will await completion of the task.

Want to prevent default signalR OnDisconnect only from a certain view

Right now, I have overridden SignalR's OnDisconnect Method as follows:
public override Task OnDisconnected()
{
if (this.Context.User != null)
{
string userName = this.Context.User.Identity.Name;
var repo = new LobbyRepository();
Clients.Group("Lobby").remove(userName);
repo.RemoveFromLobby(userName);
}
return base.OnDisconnected();
}
However, this code is reached every time the user navigates to any view, temporarily breaking the signalR connection. How can I prevent this from happening only when the user is requesting a certain view?
The connection can be maintained as long you are in the same page, if you navigate away, the connection ends.
You can use Ajax to replace the content of your page, using a technique named "click hijacking" https://web.archive.org/web/20160305021055/http://mislav.net/2011/03/click-hijack/
But remember, the connection is associated to your page.
Cheers.

MVC Handling a CorpId for the site

I'm not sure I'm handling this the right way, but since I'm running into issues, I assume I'm not.
I have to have a corporation id sent in when loading the login screen.
Looks like this:
public ActionResult LogOn(string id)
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("CorpID: {0}", id));
if(ViewBag.CorpID != null)
sb.AppendLine(string.Format("ViewBag.CorpID: {0}", ViewBag.CorpID));
Guid corpIdGuid;
if (!Guid.TryParse(id, out corpIdGuid) && string.IsNullOrEmpty(ViewBag.CorpID))
return null;
// the id passed in will take presidence over the
// viewbag unless it is blank then we use viewbag
// one way or the other viewbag.corpid should not
// be blank
if(!string.IsNullOrEmpty(id))
ViewBag.CorpID = id;
// Session["CorpId"] = id;
//Not a junk guid.. continue.
return View();
}
I need this to establish what company we will be working with during this session.
The problem I am running into, is when the cookie timeout occurs, which is set to 10 minutes, it directs them back to this login and I have no corpid anymore.
I tried the viewbag and it's being reset.
I tried a cookie, but since it expires, the data is no longer there.
I tried a Profile Manager but since they are logged it, that puts me back to nothing.
How do I maintain this CorpId when the user has timed out and put back on the login screen? I need this information for each screen I have also.
Any input would be greatly appreciated!
You need to create a separate cookie that identifies the Corporate ID that doesn't expire with user's session. Session["CorpId"] will expire with the user session and won't work.
var corpCookie = new HttpCookie("CorpID", id);
corpCookie.Expires = DateTime.Now.AddDays(30.0);
HttpContext.Current.Response.Cookies.Set(corpCookie);
Each time the user logs in, you could update the expiry to make it a sliding expiration. To retrieve the cookie value, use the following:
var corpID = HttpContext.Current.Request.Cookies.Get("CorpID").Value;

Determine if new request or editing an existing record

I have a login page that authenticates to AD, works great. After login there is a page for a service request, user can save their request and return to it later to finish or submit it if it's done. These 2 actions set a flag in the record (saved or submitted). When a user logs in, I can present them with a list of their saved requests. Now, if they click on a saved request link, I can pass the record ID via the URL.... but then I get stuck.
Should I have a different page for editing/updating records, or should I use the new request page again? If I use the same page, what's the best way to do that? My impression is that there would be a lot of "if...returning...else", making the code difficult to read. On the other hand... if I do 2 pages, any change or update needs to go in both.
I'm using VS2010 and EF4. First project in this environment, don't know what's best practice here.
I'm a fan of reusing the page; there is some if/else, but not all over the place. For instance, when updating an entity, you can do:
SomeEntity entity;
bool adding = false;
if (key > 0)
entity = db.Entities.FirstOrDefault(i => i.Key == key);
if (entity == null)
{
entity = new SomeEntity { initialvalue = "X" };
adding = true;
}
entity.Z = someValue;
//set other props
if (adding)
db.Entities.AddObject(entity);
db.SubmitChanges();
As far as the UI is concerned, yes there is some showing/hiding, but effort-wise there will be less.

Categories