Automatic actions able during time in ASP .NET MVC - c#

So, I wanted to create Post which can be subscribed by multiple users (Some kind of "looking for group to play with" site). And, after 4 hours passed, I want the author to be able to Upvote the participants, and after 24 Hours passed, the post should be automatically deleted from the database.
To make it, i've added a constructor to my Post.cs
public Post()
{
Subscribers = new List<ApplicationUser>(); //This part is not important
int timeToRateInt = Convert.ToInt32(AddedOn.Hour) + 4;
int timeToDeleteInt = Convert.ToInt32(AddedOn.Hour) + 24;
int now = Convert.ToInt32(DateTime.Now.Hour);
if(now>=timeToRateInt)
{
CanRate = true;
}
if(now>=timeToDeleteInt)
{
ToDelete = true;
}
}
The CanRate and ToDelete Are properties which i use later, but that is not that important, because program does not work properly at this point. (Later everything works perfectly fine if I manually change the database table data). So, CanRate and ToDelete Are still set to false. What is the issue? And if it should be assigned through controller, how to make sure it will change after user refreshes the site(Any page)?

Okay, so thing I've done is cutting the code and pasting it into my Action in Controller responsible for partial view. So this way everything works perfectly fine. I'll keep this question and answer here if anyone will have this issue.

Related

MVC Passing Data while opening a page

Hi I'm fairly new to MVC 5, I know how to pass data from my entities to a single view but I am now trying to create a button that opens a new view and also passes some info. I was wondering what some good ways to do this are.
I have model Position that has
positionId,
positionName,
positionDescription,
storeLocation
and a second model that is a job application
I want to create a button in my position details index that will open my application create page and pass in the positionId to be used locally in the application.
So my thought is to create a constructor for application that takes an int Id and use a this.appliedPositionId = id; or something like that. The issue is I'm not sure how to pass that info using a button to the view. I've tried Redirect and RedirectAction but I think I am using them wrong.
#Html.ActionLink("Create", "Create", "yourController", new { id = Model.PositionId }, null)
assuming you have a public ActionResult Create (int id) { ... } method –
Stephen Muecke answered solved it, thank you.

SQL write to ASP.NET user table doesn't save

My setup:
ASP.NET 4.5 web api (on Azure) saving data to SQL db (also on Azure)
AngularJS web front end (another Azure web site)
When a user first signs up, I show them a "getting started intro". The intro is only supposed to run once - I log the timestamp of the intro launch date as a custom field in the ASP.NET user table.
Imagine my surprise when I log in (as a user would) and see the intro TWICE.
The AngularJS front end is properly sending the "intro viewed" message to the ASP.NET api, and the api responds with a success message. However, when I look at the raw data in the db, the timestamp is most definitely NOT updated. Consequently, the user will see the intro a second time (at which point the timestamp gets recorded in the db properly).
I have a crappy workaround. After the client requests an OAuth Bearer token from my server, the client then requests user information (to decide whether or not to show the tour). Waiting 100ms and then sending the "tour viewed" message back to the server masks the issue.
I've not seen ANY other issues storing data at any point. Because our db is on Azure, I can't hook up Profiler and the built in auditing doesn't give me any clues.
Is there something about requesting the token that leaves ASP.NET identity in a funny state? And it takes a brief wait before you can write to the table? Are custom fields that extend the base Identity setup prone to problems like this? Is the UserManager possibly doing something weird in its black box?
Does anyone have suggestions for how to continue debugging this problem? Or ever hear of anything like it?
Here's the relevant code that should be updating the "tour viewed" timestamp in the db:
[HttpPost, Route("UserInfo")]
public async Task<IHttpActionResult> UpdateUserInfo(UpdateBindingModel model)
{
var currentUser = UserManager.FindById(User.Identity.GetUserId());
if (model.FirstName != null)
{
currentUser.FirstName = model.FirstName;
}
if (model.LastName != null)
{
currentUser.LastName = model.LastName;
}
if (model.SetIntroViewCompleteDate)
{
currentUser.IntroViewCompleteDate = DateTime.UtcNow;
}
if (model.SetIntroViewLaunchDate)
{
currentUser.IntroViewLaunchDate = DateTime.UtcNow;
}
if (model.SetTipTourCompleteDate)
{
currentUser.TipTourCompleteDate = DateTime.UtcNow;
}
if (model.SetTipTourLaunchDate)
{
currentUser.TipTourLaunchDate = DateTime.UtcNow;
}
IdentityResult result = await UserManager.UpdateAsync(currentUser);
if (result.Succeeded)
{
var data = new UserInfoViewModel
{
FirstName = currentUser.FirstName,
LastName = currentUser.LastName,
IntroViewLaunchDate = currentUser.IntroViewLaunchDate
};
return Ok(data);
}
return InternalServerError();
}
UPDATE ********* 4/18
I've also tried to move completely away from UserManager stuff. I've tried the following modifications (pulling the user data from a table like I would access any other data), but it still behaves the same. I'm starting to think that putting custom fields on the ApplicationUser object is a bad idea...
New db retrieve and save looks like this:
ApplicationDbContext newContext = new ApplicationDbContext();
var currentUser = await (from c in newContext.Users
where c.Email == User.Identity.Name
select c).SingleOrDefaultAsync();
//update some values
await newContext.SaveChangesAsync();
Basically the problem might be with initialization of the `UserManager' and the fact that this class works on the db context so you need to persist changes to that context. Here is an example:
var userStore = new UserStore<ApplicationUser>(new MyDbContext());
var userManager = new UserManager(userStore);
That way you remember both manager and context. Then in your method you would normally call:
IdentityResult result = await userManager.UpdateAsync(currentUser);
followed by persisting this change to db context:
var dbContext = userStore.context;
dbContext.saveChanges();
Based on your comment that waiting 100ms masks the issue, I think you may have a problem with the multiple async await calls. Try running the calls synchronously and see if you still have the same issue. My guess is that the problem might go away. My experience has been that using async await can be tricky when you have calls to asynchronous methods that call other asynchronous methods. You may have code that is executing without the proper results returned.
Well, here's what I did to solve the problem. I totally de-coupled my custom user data from the built in ASP.NET identity stuff. I've now got a separate object (and therefore separate SQL table) that stores things like FirstName, LastName, LastActiveDate, etc, etc.
This has solved my problem entirely, though it has introduced another call to the database in certain situations. I've deemed it to be not a big enough performance issue to worry about. I'm left thinking that this was some sort of weird race condition involving the generation of a token for an ASP.NET identity user then quickly writing to an Azure SQL database - lord knows what it was exactly in my code that caused the problem.
If you've got a problem that's hard to solve, often the best plan is to change the problem.
Now I need to find a meta thread discussing what to do with bounty points when you've blown up the problem...

Windows Phone 8 - Binding

I have a WP8 Pivot application which contains a Model that is meant to automatically bind against the View/Page.xaml. This is done by the auto-generated NotfifyPropertyChanged code which I have assigned to all of the properties that are spat out on to the page.
The problem as I see it is when the application is installed for the first time, the Model is obviously empty as the application is completely fresh. At this point, I call a web service which successfully retrieves data. Once retrieved, I save the data and assign the data to the Model.
Although I assign the data to the model, the View doesn't update itself. I also noticed that the NotifyPropertyChanged code doesn't fire when doing:
App.ViewModel.Signs = results.Signs
The code for the Model looks like this:
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
The rest of the properties have the same notion, i.e. NotifyPropertyChanged("objectName");.
When I relaunch the application, the information on the screen successfully appears. It is only when the application fires from beginning or when the user manually asks for the latest data that it fails to update the View/Page.xaml.
Any ideas what I could do to fix this?
Maybe your NotifyPropertyChanged method is broken, or the Signs property does not call the method correctly.

can I redirect a route if the post was triggered by a specific control?

I have an app that is slowly being upgraded to MVC (MVC.net?) from ASP.net. As a consequence, I'm stuck with some awkward things like only being able to have one Form element per page because of the master page (refactoring that out would be a lot of work). Another is my little "Login/Logout" button in the corner (also on the master page). It's a lovely little holdover from the ASP.net days when everything did POST it seemed.
I've worked around this by having a method in each controller that looks like this:
public ActionResult CheckForLogoutButton()
{
var coll = this.Request.Form;
if ((coll.Cast<string>().Select(key => new KeyValuePair<string, string>(
key, coll[key])).Any(x => x.Value.Contains("LoginStatus1"))))
{
return RedirectToAction("LogOut", "Account", new { logout = true });
}
else return new EmptyResult();
}
Then any action that is a post has to call this method and redirect if it gets a RedirectToRouteResult. It certainly works and doesn't violate the PRG pattern. Well, we could argue that a hyperlink shouldn't be doing a POST.
But I wonder if there's a better way. Would it make more sense to tinker with the MVC pipeline and just check to see if this particular control ('LoginStatus1') initiated the POST, regardless of where it's headed and then redirect to the proper action? Keep in mind I have no idea if that's easy to do. The alternative is to have this bit of code in a base controller class and then remember to check in every POST handler. Seems like a recipe for broken code.
A better way of doing your own fix is to make your action method a global action filter, that way you won't need to add it every time you create a controller. http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx

What is a good method for preventing a user from submitting a form twice?

I have a purchase page and I don't want the user to be able to refresh the page and resubmit the form once they get to the 'order complete' page because it automatically sets them up in our system via database values and charges their card via paypal (only want these to happen ONCE)... I have seen some sites that say 'Don't hit refresh or you will get charged twice!' but that is pretty lame to leave it open to possibility, what's a good way to only allow it to be submitted once or prevent them from refreshing, etc?
PS: I saw a few similar questions: PHP: Stop a Form from being accidentally reprocessed when Back is pressed and How do I stop the Back and Refresh buttons from resubmitting my form? but found no satisfactory answer... an ASP.NET MVC specific answer would be ideal too if there is a mechanism for this.
EDIT: Once they click submit it POSTS to my controller and then the controller does some magic and then returns a view with an order complete message, but if I click refresh on my browser it does the whole 'do you want to resend this form?' that is bad...
The standard solution to this is the POST/REDIRECT/GET pattern. This pattern can be implemented using pretty much any web development platform. You would typically:
Validate submission after POST
if it fails re-render the original entry form with validation errors displayed
if it succeeds, REDIRECT to a confirmation page, or page where you re-display the input - this is the GET part
since the last action was a GET, if the user refreshes at this point, there is no form re-submission to occur.
I 100% agree with RedFilter's generic answer, but wanted to post some relevant code for ASP.NET MVC specifically.
You can use the Post/Redirect/Get (PRG) Pattern to solve the double postback problem.
Here's an graphical illustration of the problem:
What happens is when the user hits refresh, the browser attempts to resubmit the last request it made. If the last request was a post, the browser will attempt to do that.
Most browsers know that this isn't typically what the user wants to do, so will automatically ask:
Chrome -
The page that you're looking for used information that you entered.
Returning to that page might cause any action you took to be repeated.
Do you want to continue?
Firefox - To display this page, Firefox must send information that will repeat any action (such as a search or order confirmation) that was performed earlier.
Safari -
Are you sure you want to send a form again?
To reopen this page Safari must resend a form. This might result in duplicate purchases, comments, or other actions.
Internet Explorer -
To display the webpage again, the web browser needs to
resend the information you've previously submitted.
If you were making a purchase, you should click Cancel to
avoid a duplicate transaction. Otherwise, click Retry to display
the webpage again.
But the PRG pattern helps avoid this altogether by sending the client a redirect message so when the page finally appears, the last request the browser executed was a GET request for the new resource.
Here's a great article on PRG that provides an implementation of the pattern for MVC. It's important to note that you only want to resort to a redirect when an non-idempotent action is performed on the server. In other words, if you have a valid model and have actually persisted the data in some way, then it's important to ensure the request isn't accidentally submitted twice. But if the model is invalid, the current page and model should be returned so the user can make any necessary modifications.
Here's an example Controller:
[HttpGet]
public ActionResult Edit(int id) {
var model = new EditModel();
//...
return View(model);
}
[HttpPost]
public ActionResult Edit(EditModel model) {
if (ModelState.IsValid) {
product = repository.SaveOrUpdate(model);
return RedirectToAction("Details", new { id = product.Id });
}
return View(model);
}
[HttpGet]
public ActionResult Details(int id) {
var model = new DetailModel();
//...
return View(model);
}
While serving up the order confirmation page you can set a token that you also store in the DB/Cache. At the first instance of order confirmation, check for this token's existence and clear the token. If implemented with thread safety, you will not be able to submit the order twice.
This is just one of the many approaches possible.
Note that the PRG pattern does not completely guard against multiple form submissions, as multiple post requests can be fired off even before a single redirect has taken place - this can lead to your form submissions not being idempotent.
Do take note of the answer that has been provided here, which provides a workaround to this issue, which I quote here for convenience:
If you make use of a hidden anti-forgery token in your form (as you
should), you can cache the anti-forgery token on first submit and
remove the token from cache if required, or expire the cached entry
after set amount of time.
You will then be able to check with each request against the cache
whether the specific form has been submitted and reject it if it has.
You don't need to generate your own GUID as this is already being done
when generating the anti-forgery token.
Give each visitor's form a unique ID when the page is first loaded. Note the ID when the form is submitted. Once a form has been submitted with that ID, don't allow any further requests using it. If they click refresh, the same ID will be sent.
Simply do a redirect from the page that does all the nasty stuff to the "Thank you for your order" page. Having done that, the user can hit refresh as many times as he likes.
If you doesn't like redirect the user to other page, then by using my way you dose not need Post/Redirect/Get (PRG) Pattern and the user remain on the current page without fear of the negative effects of re-submitting of the form!
I use a TempData item and a Hidden field (a property in the ViewModel of the form) to keep a same Guid in both sides (Server/Client) and it is my sign to detect if the form is Resubmitting by refresh or not.
Final face of the codes looks like very short and simple:
Action:
[HttpPost]
public virtual ActionResult Order(OrderViewModel vModel)
{
if (this.IsResubmit(vModel)) // << Check Resubmit
{
ViewBag.ErrorMsg = "Form is Resubmitting";
}
else
{
// .... Post codes here without any changes...
}
this.PreventResubmit(vModel);// << Fill TempData & ViewModel PreventResubmit Property
return View(vModel)
}
In View:
#if (!string.IsNullOrEmpty(ViewBag.ErrorMsg))
{
<div>ViewBag.ErrorMsg</div>
}
#using (Html.BeginForm(...)){
#Html.HiddenFor(x=>x.PreventResubmit) // << Put this Hidden Field in the form
// Others codes of the form without any changes
}
In View Model:
public class OrderViewModel: NoResubmitAbstract // << Inherit from NoResubmitAbstract
{
// Without any changes!
}
What do you think?
I make it simple by writing 2 class:
NoResubmitAbstract abstract class
ControllerExtentions static class (An Extension class for System.Web.Mvc.ControllerBase)
ControllerExtentions:
public static class ControllerExtentions
{
[NonAction]
public static bool IsResubmit (this System.Web.Mvc.ControllerBase controller, NoResubmitAbstract vModel)
{
return (Guid)controller.TempData["PreventResubmit"]!= vModel.PreventResubmit;
}
[NonAction]
public static void PreventResubmit(this System.Web.Mvc.ControllerBase controller, params NoResubmitAbstract[] vModels)
{
var preventResubmitGuid = Guid.NewGuid();
controller.TempData["PreventResubmit"] = preventResubmitGuid ;
foreach (var vm in vModels)
{
vm.SetPreventResubmit(preventResubmitGuid);
}
}
}
NoResubmitAbstract:
public abstract class NoResubmitAbstract
{
public Guid PreventResubmit { get; set; }
public void SetPreventResubmit(Guid prs)
{
PreventResubmit = prs;
}
}
Just put them in your MVC project and run it... ;)
Off the top of my head, generate a System.Guid in a hidden field on the GET request of the page and associate it with your checkout/payment. Simply check for it and display a message saying 'Payment already processed.' or such.
Kazi Manzur Rashid wrote about this (together with other asp.net mvc best-practices). He suggests using two filters to handle data transfer between the POST and the follwing GET using TempData.

Categories

Resources