I want to maintain model state between method calls on the controller.
Is that possible?
A controller is created at the call of some of its methods and destroyed when the called method finishes its execution?
If not... how can I maintain the state between method calls?
If that is possible... how has it to be written?
thanks
Let's keep this simple! :-)
You asked following questions:
I want to maintain model state between method calls on the controller.
Is that possible?
Answer: yes, it is
A controller is created at the call of some of its methods and destroyed when the called method finishes its execution?
Answer: yes, it doesn't persist any state - it just returns the result generated by the action
If not... how can I maintain the state between method calls? If that is possible... how has it to be written?
Answer:
We had the same issue in our current business application using ASP.NET MVC2 and came up with following solutions:
Solution(s):
1st he Proof-Of-Concept for this application contained some special functionality using the global Session. (Note that the session is saved on the webserver)
Saving and reading from session in ASP.NET MVC:
public Action SaveIntoSession()
{
...
Session["SessionData"] = "Something to be stored in session";
...
}
public action ReadFromSession()
{
...
// UNBOXING is required when you're using the session as ASP.NET doesn't know what is stored into the session
string sessionData = (string)Session["SessionData"];
...
}
2nd There's another concept of saving the required information for each request into a Session Table into your database. (But I won't go too deep into that.. check this article for more info - although it's php, you can get your head around the concept pretty easily)
3rd Would be to use the TempDataDictionary as mentioned by Charlino. The problem with this is, that it only persists the state from one call to the another. All data that is stored will be deleted by the next request. (TempData is mostly used to display errors to the user..)
public Action SaveIntoTempData()
{
...
TempData["TempData"] = "Something to be stored ONE request ONLY";
...
}
public Action ReadFromTempData()
{
...
string tempData = (string)TempData["TempData"];
}
4th You could also use the ViewDataDictionary. It is not recommended to be used with sensitive data. (check this thread for more info)
public Action SaveIntoViewData()
{
...
TempData["ViewData"] = "Something to be stored into the page";
...
}
public Action ReadFromViewData()
{
...
string viewData = (string)ViewData["ViewData"];
}
At the end of the day. It is up to you and your team what best matches requirements.
You have access to the standard ASP.NET session, even in MVC. Check the docs.
Would TempData / TempDataDictionary be suitable?
HTHs,
Charles
I think by default you should be trying for a Restfull approach. If that is not viable then either serialise to the session object or store in a Singleton or something like that.
By default I don't think MVC maintains state between calls.
Related
I want the asp.net mvc domain in my C# code (model function). I trying to call this function on string but it's not worked.
string domain = "" + System.Web.HttpContext.Current.Request.Url;
When I tried to call same in controller's actionresult then this work but in c# models it's not work.
I hope the context will make it work but it's not worked.
Someone please explain me if something else I can use. What I want to do is getting url and I want to not pass the url (domain) form every call.
HttpContext.Current will only work if the code is executed within an existing HTTP context. For example this code will return null if you attempt to execute it in a unit test. It is a terribly bad practice to rely on any HttpContext specific things in your models. This property could be set in your controller (where you have access to the HttpContext) and then pass the model to your DAL and Service layers where the property will already be populated.
Depending on your context / models, it might not be available. This being said, you can pass the context in, or at least the url as a parameter to the model in need.
I have an application.
I am stuck at a point from where i want to pass a model from a Post method to a Get method.
// Code in get method
FoundAccounts fa=new FoundAccounts();
fa.register = model;
return RedirectToAction("FoundAccounts", fa);
//Post Method
public ActionResult FoundAccounts(FoundAccounts fa)
{
//Use that values here
}
Can i do it like this?
I am unable to find a way.
Please help me with the same.
Thanks
Can i do it like this?
No, you can't. You can only pass simple, scalar properties to the route values of a RedirectToAction call. It doesn't make sense to pass complex objects, because when you perform a redirect, only the simple properties will be included in the GET request as query string parameters.
So you have different possibilities:
Persist the entity in your backend and then pass only the id to the GET action (this is the solution I recommend):
int id = repository.Save(fa);
return RedirectToAction("FoundAccounts", new { id = id });
and then your GET action will take the id as action parameter and use this id to retrieve the entity from wherever you persisted it initially:
public ActionResult FoundAccounts(int id)
{
FoundAccounts model = repository.Get(id);
...
}
Pass all properties and leave the model binder dehydrate the entity in the GET action:
return RedirectToAction("FoundAccounts", new
{
prop1 = fa.prop1,
prop2 = fa.prop2,
...
});
Obviously here if you have some complex properties you will need to pass them as well. Remember that the properties you include will be the properties you will be able to retrieve in your GET action. Everything else will be lost:
return RedirectToAction("FoundAccounts", new RouteValueDictionary
{
{ "prop1.SubComplexProp1", fa.prop1.SubComplexProp1 },
{ "prop1.SubComplexProp2", fa.prop1.SubComplexProp2 },
{ "prop2", fa.prop2 },
});
The drawback of this solution is that if you have lots of properties this could quickly become cumbersome. And you could even hit on a roadblock because there's a limitation to the size of a GET request. This limitation will vary between browsers, but I wouldn't pass anything more than 2048 characters in a GET request.
Use Session or TempData (not recommended as it introduces state into your application);
TempData["fa"] = fa;
return RedirectToAction("FoundAccounts");
and then inside the GET action retrieve the model from the Session or TempData:
public ActionResult FoundAccounts()
{
FoundAccounts model = TempData["fa"] as FoundAccounts;
...
}
The difference between Session and TempData is that TempData will survive only for a single redirect and will then be automatically evicted by the framework. Under the covers it uses Session, it's just that it is automatically cleared once you read the value in the GET action. The problem with this of course is that if the user hits F5 to refresh the page in his browser, you will no longer find the value in TempData because it was evicted. Even worse if the user decides to bookmark the GET action, he will have the same problem if later he decides to come back and navigate to this bookmark. So people tend to use Sessions for those kind of things. Sessions of course do not solve the problem with bookmarks (because the user could have closed his browser in-between and the Session will be lost). Also Sessions introduce other problems in web farm scenarios. For example if the session is stored in-memory (which is the default), this means that you could store the value on one node of the farm but when you perform the redirect you could hit another node of the farm and then this node no longer has any knowledge of the session. So people start to use an out-of-process sessions - either stored in a State Service machine or SQL service. But what's the point? I mean take a look at my first and recommended solution. It's exactly what you will end up a fortiori if you want to have scalable solution working in a web farm environments.
I have a registration page in my application. It has 3 states and 1 error state(If any error comes):
Fill Basic Information
Select Package
Say Thanks
Error
Now I want to use state pattern here. First I created a console application which is OK. Now I want to implement this logic in my MVC application but I am confused about the structure. I mean how many views, models and controller I need and where to place my logic.
1 controller: RegistrationController
6 action methods:
GET+POST for Index (fill in basic info)
GET+POST for Package
GET for Thank you
GET for Error
This is rough code to make your mind going:
public class RegistrationController : Controller
{
public ActionResult Index()
{
RegistrationState model = RegistrationState.Init();
// just display the "Fill Basic Info" form
return View(model);
}
[HttpPost]
public ActionResult Index(RegistrationState data)
{
// process data and redirect to next step
this.TempData["RegState"] = data;
if (!this.ModelState.IsValid || data.State == State.Error)
{
// error should handle provided state and empty one as well
return RedirectToAction("Error");
}
return RedirectToAction("Package");
}
public ActionResult Package()
{
RegistrationState data = this.TempData["RegState"] as RegistrationState;
if (data == null)
{
return RedirectToAction("Error");
}
// get packages and display them
IList<Package> model = this.repository.GetPackages();
return View(new Tuple.Create(data, model));
}
[HttpPost]
public ActionResult Package(RegistrationState data)
{
// process data blah blah blah
}
// and so on and so forth
....
}
As you can see you still have to write some MVC-related code to act upon state changes. In my example everything's done in action methods. But action filters could be used as well. If you can't come up with a general action filter that can serve many different state objects then it's best to just write the code in action methods.
Another approach
If you know Asp.net MVC good enough you could take this a step further and write a state machine ControllerFactory that would work along with routing in a sense as:
{StateObjectType}/{State}
ControllerFactory would therefore be able to parse view data to a known state object type and pass execution to particular action. According to state. This would make it a specially state machine suited Asp.net MVC application.
The more important question is of course whether you can create the whole application with this pattern or are there just certain parts of it that should work like this. You could of course combine both approaches and provide appropriate routing for each.
Important notices
You should be very careful how you define your error state, because entering invalid field data shouldn't result in error state but rather in data validation errors that actually display within the view beside the field with invalid data (ie. invalid date provided as 13/13/1313). Your error state should only be used for actual object state error that's not related to user input. What would that be is beyond my imagination.
As mentioned in my comment you should check out some Asp.net MVC intro videos and you'll see how validation works in Asp.net MVC. Also rather simple stuff.
State pattern of this kind is not something a regular Asp.net MVC developer would use, because it would most likely complicate code more than taking the normal approach. Analyse before you decide. Asp.net MVC is very clean code wise so adding additional abstraction over it may become confusing. And your domain model (state classes) would most likely have a much more complex code as simple POCOs with data annotations.
In your case data validation would also be more complicated (when used with data annotations) because you object should be validated according to its state which may be different between states. POCO objects are always validated the same. This may mean that we may use more classes but they are smaller, simpler and easier to maintain.
I think you are confusing states. Examples of state are:
Awaiting for a user to register
User registered successfully
User didn't register successfully
Now each of these states would have a page:
localhost:8034/Register
localhost:8034/Register/Success
localhost:8034/Register/Failure
If user can't register because they left some fields empty, they will be in the first state and you will have to display some validation messages.
Because of this, as the minimum I'll have a controller called Register and the following action methods:
Index() GET/POST
Success() GET
Failure() GET
So, I have this variable that I push into the controller via POST from a form in my view.
I then push the variable into viewdata so it's available to the next view which is fine. But that view has no need of a form so I'm unable to push that same variable into the next controller. In short, it's cumbersome to push pieces of information back and forth from controller to view and reverse, so I'm looking for a way to keep a global variable alive inside a controller so that it's accessible by all action results... The general breakdown of my program is this...
-User types a "name"
-I send "name" to controller.
-I push 'name' into viewstate (query entity framework to get a list of stuff 'name'
has access to) and return that list into the view.
-In that view I can access the 'name' since it was in view state.
-Clicking on a link inside the page takes me to another controller where I need
to get access to 'name' WITHOUT passing view Routing or POST.
Obviously the easiest way would be to declare 'name' globally and then it's always available but for the life of me I can't figure out how.
Have you considered storing it in the Session?
This will allow you to easily access it, either from your controller or views, and avoids the need for global variables.
Storing:
[HttpPost]
public ActionResult YourPostMethod(string name)
{
Session["Name"] = "yourName";
}
Access: *
Make Sure to check that it exists prior to grabbing it:
var whatsMyName = (Session["Name"] != null) ? Session["Name"] : "";
Scalability Consideration
It's worth mentioning that MVC applications are designed to mimic the web and are stateless. Introducing Session variables changes this, so be aware that it can introduce issues regarding scalability, etc.
Each user that stores data within the Session will take up resources at the server level. Depending on the number of users and what you are storing within the Session, you could potentially run out of memory if those values become too large.
Why not use the session object, which is an associative array which lives while the client is connected?
$_SESSION['name'] = "zzzz"; // store session data
name = $_SESSION['name']; //retrieve data
You can use this for each user till their session is active.. hope this helps
Recently I started working with MVC, before that I used "classic" ASP.NET.
After using Ruby on Rails (RoR), I wonder how to implement POST request handling in MVC similar to how RoR operates. In RoR you use the Post method, so you need only one function for a view.
In ASP.NET MVC I need to use 2 separate functions for GET and for POST, so I need to initialize the same data twice, and I don't like to repeat something in my code.
How can I check if the request is POST in one method?
Update:
Solution is found: I have to use Request.HttpMethod.
Thank you!
I came across this question wanting to know the same thing. Below is a detailed description of my situation and the solution that I used (which utilizes the other answers provided here). I originally tried to use the two separate method approach, but I ran into a problem when the method signatures of these methods became identical.
I have a page that displays report data. At the top of the page there is a form with some fields, which allow the user to specify report parameters such as start date, end date, etc.
I originally approached this by creating two separate methods to handle the Get and the Post methods. The post method would redirect the browser to the get method so that any parameters that were specified would be added to the query string and so that the browser would not prompt the user with a dialog saying that it is going to resend the data that they entered if they refresh. Note: I realized later that I could accomplish this by setting the method attribute of my form element to "Get", but I think ideally a controller shouldn't have knowledge of how a view is implemented, so in my opinion that is irrelevant.
As I developed these two methods I eventually found myself in a situation where the method signatures became identical. Furthermore, my code for these two methods became nearly identical, so I decided to merge them into a single method and to just check the request verb so that I could do something slightly different when the request is not a "Get". A distilled example of my two methods is shown below:
// this will not compile because the method signatures are the same
public ActionResult MyReport(DateRangeReportItem report)
{
// if there are no validation errors and the required report parameters are completed
if (ModelState.IsValid && report.ParametersAreComplete)
{
// retrieve report data and populate it on the report model
report.Result = GetReportData(report.CreateReportParameters());
}
return View(report);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyReport(DateRangeReportItem report)
{
if (ModelState.IsValid && report.ParametersAreComplete)
{
// redirect to the same action so that if the user refreshes the browser it will submit a get request instead of a post request
// this avoids the browser prompting the user with a dialog saying that their data will be resubmitted
return RedirectToAction("MyReport", new { StartDate = report.StartDate, EndDate = report.EndDate });
}
else
{
// there were validation errors, or the report parameters are not yet complete
return View(report);
}
}
Why am I accepting a model object as the parameter to my get method? The reason is that I wanted to take advantage of the validation logic already built into the model object. If someone navigates to my page directly with all parameters already specified in the query string, then I want to go ahead and retrieve the report data and display it on the page. However, if the parameters specified in the query string are invalid then I also want validation errors to appear on the page. By putting my model object as the parameter, the MVC framework will automatically attempt to populate it and will capture any validation errors without any additional work on my part.
I used the other answers posted for this question to create a RequestHttpVerb property on a base controller class in my project:
public HttpVerbs RequestHttpVerb
{
get { return (HttpVerbs)Enum.Parse(typeof(HttpVerbs), this.Request.HttpMethod, true); }
}
So finally my consolidated method looks like the following:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult MyReport(DateRangeReportItem report)
{
// check if there are any validation errors in the model
// and whether all required report parameters have been completed
if (ModelState.IsValid && report.ParametersAreComplete)
{
// this is unnecessary if the form method is set to "Get"
// but within the controller I do not know for sure if that will be the case in the view
if (HttpVerbs.Get != this.RequestHttpVerb)
{
// redirect to the same action so that if the user refreshes the browser it will submit a get request instead of a post request
// this avoids the browser prompting the user with a dialog saying that their data will be resubmitted
return RedirectToAction("MyReport", new { StartDate = report.StartDate, EndDate = report.EndDate });
}
// there were no validation errors and all required report parameters are complete
// retrieve report data and populate that data on the model
report.Result = GetReportData(report.CreateReportParameters());
}
// display the view with the report object
// Any model state errors that occurred while populating the model will result in validation errors being displayed
return View(report);
}
That's my current solution to the problem. I would prefer not to have to check the Request.HttpMethod property in order to determine whether I needed to perform the redirect, but I didn't see another solution to my problem. I would have been fine with keeping two separate methods to handle Get and Post requests, but the identical method signature prevented this. I would have preferred to rename my Post action handler method to avoid the method signature conflict and to use some mechanism to indicate to the MVC framework that my renamed method should still handle the "MyReport" action, but I am not aware of any such mechanism in the MVC framework.
You only need separate methods for GET and POST if their method signatures differ, there's no reason why one action method can't handle GET and POST methods.
If you need to know whether it was a GET or POST, you could check using Request.HttpMethod in your action, but I would advise using a separate method decorated with the [AcceptVerbs(HttpVerbs.Post)] attribute as suggested by the other posters.
You don't check in ASP.NET MVC. You decorate your method with the [AcceptVerbs(HttpVerbs.Post)] attribute to indicate that the method applies to post only, and accept the model in the method used to handle the post.
I'd strongly suggest doing the walkthrough for NerdDinner to understand more about the ASP.NET MVC framework.
You may take a look at the Request.HttpMethod property.
Correct way to do is using ModelBinding during Post request.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(EmployeeViewModel model)
{
//validate data, save employee, handle validation errors...
}
This way you will not have to init your data again.