a good way of passing multiple values from View to Controller - asp.net-mvc

This is something happening everyday
I'm looking for a better way of grabbing the values from view into the action, since the controller create and destroy based on http request, is there a good way to pass all the params thru?(By that I mean more than 5 params)
I think about session but there is a chance pepple lose their session and the important value gone, besides session, is there any other way around?

I would first consider whether your application needs to save that much information between views. MVC is a REST based architecture, and is typically designed to be stateless.
With that said, your options for passing around state with a user boil down to Session, Cookies, and Database.
I would create a static class that stores and retrieves its data from session.
public static class CustomPersistStore
{
public static CustomClass Current{
get{
var instance = HttpContext.Current.Session["key"] as CustomClass;
if(instance = null) {
instance = new CustomClass();
}
return instance;
}
}
}

If you want to pass values from the View to the Action the best practice is to use strongly typed views.
http://howmvcworks.net/OnViews/BuildingAStronglyTypedView

Related

Why global property in MVC Controller is still empty

I have this code in my Controller:
List<string> order = new List<string>();
[Route("Reservations/Overview/Refresh/id")]
[AllowAnonymous]
public JsonResult AddOrder(string id)
{
if(!order.Contains(id))order.Add(id);
return Json($"ok", JsonRequestBehavior.AllowGet);
}
[Route("Reservations/Overview/Check/id")]
[HttpPost]
public JsonResult Check(string id)
{
if (order.Contains(id))
{
order.Remove(id);
return Json(true);
}
else return Json(false);
}
Everything is working but my global order list is always empty. Why?
HTTP is stateless. Each request instantiates a new instance of the controller class, which wouldn't reflect any changes made to class-level variables on a previous instance (which has long since been disposed) from a previous request.
Basically you need to persist your data somewhere. There are a variety of places to do this:
Session state
Static values
A database
A cache mechanism
On the page itself (posted back with form data)
Cookies
A file
and so on...
Each option is going to have its pros and cons, depending on the full set of functionality you need. Session state may be the simplest approach to get you started. On the page itself may be considered more RESTful and, thus, more scalable/portable. A database would be more secure than on the page itself because users can modify anything on the page. Etc.
But the point remains regardless of which option you want... The data needs to be persisted (saved) somewhere in order to be able to read it again at a later time.
Global variables are not persisted across multiple requests to the controller. You can persist them to a database, or store the orders in session:
Session["Orders"] = orders;
You have to store the updated list of orders to session every time you modify the list or data within the list in any way. Session is per user; just be careful how much data you put in session if you do. If you use a database, you need to persist the record change anytime a value changes on a record, or when creating a new record.

MVC - How to instantiate, store and make a typed variable available throughout the application, once per page view

I am developing an MVC app to serve multiple domains - each is a branch of a larger company.
A LocalBranch class stores details such as phone, address, email, location coordinates etc.
I want to create a single instance of this class per http request and have it available throughout the application - from within controllers, views, some helper classes and other code.
Is there a recommended way of doing this?
Right now I have it as a property on a BaseController and use ViewBagto pass it to views. But I would prefer it strongly typed in Views if possible.
I don't want to put it in an application variable, because we need to serve different values to different domains.
I would rather avoid a session variable if possible because we might scale up to use multiple servers in the future, and I've heard this doesn't play well with sessions.
Please feel free to update tags / title if you think there is a clearer way of expressing what I'm after. Thank you.
The best way to maintain your state in a web application per request is simply use the HttpContext class.
You need to store your state(LocalBranch) as an Item in the HttpContext:
HttpContext.Current.Items.Add("LocalBranch", GetLocalBranch());
You can fetch the Item all across your application like this:
LocalBranch branch = HttpContext.Current.Items["LocalBranch"] as LocalBranch;
The Items property is simply a key value Dictionary. The value is an object. You will have to check for nulls and this is really similar to the Session object you know. The main difference is the scope. The HttpContext is a dot net object that has a lifetime of an http request.
Now using the HttpContext the way I've shown you is the simplest way to do it.
You can go two steps forward and use a framework called Unity and add a lifetime to your objects.
Unity does much more and the lifetime management is just one gem.
You can create a custom HttpContext lifetime that generates objects per request. Something like this.
And them all you need to do is:
1.Register you LocalBranch class with the HttpContext lifetime.
2.Add a static Current property which will use the Unity container and resolve the correct instance of LocalBranch.
3.Use it something like this: LocalBranch.Current
BTW, you can use Unity's dependency injection for injecting objects into controllers and other modules. That's a better practice then just using the static Current property.
You kind of have two questions here. The first is "How do I create a single instance of this class per HttpRequest?" The second is "How do I make this available to strongly typed views?"
The first has pretty much been answered by #amir-popovich to use dependency injection. However, FWIW I would probably use Ninject instead of Unity (just preference, really) and I would probably implement it differently. I would not use HttpContext, and simply build a service (which is instanciated using Ninject's OnePerHttpRequest Module, passing the domain as an argument to get the proper values).
Then, in order to add these LocalBranch values to your strongly typed View Model, you can first create a base view model which holds this type:
public class BaseViewModel
{
public LocalBranch Branch {get;set;}
}
Then, make all of your current view models inherit this base type
public MyViewModel : BaseViewModel
{
public string SomeValue {get;set;}
}
Then in your controller, it is easy enough to add these values from the service you created from the first step
public ActionResult SomeAction()
{
var vm = new MyViewModel();
vm.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
//do other stuff
return View(vm);
}
However, that gets pretty tedious to add that to each controller action, so you can instead create a Result Filter to add it for you:
public class LocalBranchResultFilter : FilterAttribute, IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
//This method gets invoked before the ActionResult is executed.
filterContext.Controller.ViewData.Model.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
}
}
Now, you can just decorate your Controller and/or Actions with the filter (you could even set it in the Global Filters if you want).
You can embed the child actions into your layout or a view. You can even cache its output so you don't keep re-querying the database.
controller
[ChildActionOnly]
[OutputCache(Duration=500, VaryByParam="*")]
public ActionResult Info()
{
var localBranch = db.GetLocalBranch();
return PartialView("_Info", localBranch);
}
_Info view
This bit will get inserted into your other views
#model LocalBranch
<span>#Model.address</span>
<span>#Model.phone</span>
Use in _Layout or other view
<p>lorem ipsum...</p>
#Html.Action("Info")

MVC5 keep object in all functions

This question might sound dumb, but I am new to asp.net mvc and can't find the answer to my question.
In my application ( a game) I have a model of the game GameModel (it contains a multidimensional array). What I want is to be able to use the same object in every controller I use. So I create it once and after that use it in every controller function.
Basically there is one view, and all other functions in the controller edit the object with functions of the model.
My idea was put the object in a session variable, make a function to check the session variable if the object is not set set the object. But this does not look logic to me, hopefully someone has a better solution.
According to your question, you want to keep track of a user's data (game data).
Storing GameModel in Session variable make sense for that scenario.
If you see yourself calling that Session variable from a lot of places, you can create a BaseController and keep it there. Then inherit all controllers from it.
For example,
public class BaseController : Controller
{
public GameModel CurrentGameModel
{
get
{
var model = Session["GameModel"] as GameModel;
if (model == null)
{
model = new GameModel();
Session["GameModel"] = model;
}
return model;
}
set { Session["GameModel"] = value; }
}
}
public class HomeController : BaseController
{
}
Note: You have to keep in mind that if Application Pool recycles or Application crashes, all data stored in Session variable will be lost.
If you want to persist data, you need to store in persistent storage like database.
I don't understand why you don't think Session looks good. It's purpose is exactly keeping data per user througout multiple requests.
You could also return the state of the game to the client using hidden fields. That would be even better than Session, given that your game state doesn't change in the server, as a response to someone else's action.
And finally you can use a static property of a class. Static properties in ASP.NET are kept alive througout the application lifecicle and are visible equally to all users. Meaning, if a user writes something there, another user can read it. You can allocate data per user using a Dictionary<>, though, where the key is the user Id.

Singletons and ASP.NET MVC

Right now I'm having an issue with a Singleton that I just wrote for use in ASP.NET MVC -- My Singleton looks like this:
public sealed class RequestGenerator : IRequestGenerator
{
// Singleton pattern
private RequestGenerator()
{
requestList = new Stack<Request>();
appSettings = new WebAppSettings();
}
private static volatile RequestGenerator instance = new RequestGenerator();
private static Stack<Request> requestList = new Stack<Request>();
// abstraction layer for accessing web.config
private static IAppSettings appSettings = new WebAppSettings();
// used for "lock"-ing to prevent race conditions
private static object syncRoot = new object();
// public accessor for singleton
public static IRequestGenerator Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new RequestGenerator();
}
}
}
return instance;
}
}
private const string REQUESTID = "RequestID";
// Find functions
private Request FindRequest(string component, string requestId)
private List<Request> FindAllRequests(string component, string requestId)
#region Public Methods required by Interface
// Gets and increments last Request ID from Web.Config, creates new Request, and returns RequestID
public string GetID(string component, string userId)
// Changes state of Request to "submitted"
public void SetID(string component, string requestId)
// Changes state of Request to "success" or "failure" and records result for later output
public void CloseID(string component, string requestId, bool success, string result)
// Verifies that Component has generated a Request of this ID
public bool VerifyID(string component, string requestId)
// Verifies that Component has generated a Request of this ID and is owned by specified UserId
public bool VerifyID(string component, string userId, string requestId)
// Returns State of Request ID (Open, Submitted, etc.)
public Status GetState(string component, string requestId)
// Returns Result String of Success or Failure.
public string GetResult(string component, string requestId)
#endregion
}
And my controller code looks like this:
public ViewResult SomeAction()
{
private IRequestGenerator reqGen = RequestGenerator.Instance;
string requestId = reqGen.GetID(someComponentName, someUserId);
return View(requestId);
}
Everything works okay the first time I hit the controller. "reqGen" is assigned the instance of the Singleton. A new instance of Request is added to the internal list of the Singleton. And then we return a View(). The next time I hit this controller's SomeAction(), I'm expecting the Singleton to contain the List with the instance of SomeClass that I had just added, but instead the List is empty.
What's happened? Has Garbage Collection gobbled up my object? Is there something special I need to consider when implementing the Singleton pattern in ASP.NET MVC?
Thanks!
EDIT: Ahh, the lightbulb just went on. So each new page request takes place in a completely new process! Got it. (my background is in desktop application development, so this is a different paradigm for me...)
EDIT2: Sure, here's some more clarification. My application needed a request number system where something being requested needed a unique ID, but I had no DB available. But it had to be available to every user to log the state of each request. I also realized that it could double as a way to regulate the session, say, if a use double-clicked the request button. A singleton seemed like the way to go, but realizing that each request is in its own process basically eliminates the singleton. And I guess that also eliminates the static class, right?
EDIT3: ok, I've added the actual code that I'm working with (minus the implementation of each Method, for simplicity sake...) I hope this is clearer.
EDIT4: I'm awarding the green check mark to Chris as I'm beginning to realize that an application-level singleton is just like having a Global (and global's are evil, right?) -- All kidding aside, the best option really is to have a DB and SQLite seems like the best fit for now, although I can definitely see myself moving to an Oracle instance in the future. Unfortunately, the best option then would be to use an ORM, but that's another learning curve to climb. bugger.
EDIT5: Last edit, I swear. :-)
So I tried using HttpRuntime.Cache, but was surprised to find that my cache was getting flushed/invalidated constantly and couldn't figure out what was going on. Well, I was getting tripped up by a side-effect of something else I was doing: Writing to "Web.config"
The Answer --> Unbeknownst to me, when "web.config" is altered in anyway, the application is RESTARTED! Yup, everything gets thrown away. My singleton, my cache, everything. Gah. No wonder nothing was working right. Looks like writing back to web.config is generally bad practice which I shall now eschew.
Thanks again to everyone who helped me out with this quandary.
The singleton is specific to the processing instance. A new instance is being generated for each page request. Page requests are generally considered stateless so data from one doesn't just stick around for another.
In order to get this to work at the application level, the instance variable will have to be declared there. See this question for a hint on how to create an application level variable. Note that this would make it available across all requests.. which isn't always what you want.
Of course, if you are trying to implement some type of session state then you might just use session or use some type of caching procedure.
UPDATE
Based on your edits: A static class should not maintain data. It's purpose is to simply group some common methods together, but it shouldn't store data between method calls. A singleton is an altogether different thing in that it is a class that you only want one object to be created for the request.
Neither of those seem to be what you want.
Now, having an application level singleton would be available to the entire application, but that crosses requests and would have to be coded accordingly.
It almost sounds like you are trying to build an in memory data store. You could go down the path of utilizing one of the various caching mechanisms like .NET Page.Cache, MemCache, or Enterprise Library's Caching Application Block.
However, all of those have the problem of getting cleared in the event the worker process hosting the application gets recycled.. Which can happen at the worst times.. And will happen based on random things like memory usage, some timer expired, a certain number of page recompiles, etc.
Instead, I'd highly recommend using some type of persisted storage. Whether that be just xml files that you read/write from or embedding something like SQL Lite into the application. SQL Lite is a very lightweight database that doesn't require installation on the server; you just need the assemblies.
You can use Dependency Injection to control the life of the class. Here's the line you could add in your web.config if you were using Castle Windsor.
<component id="MySingleton" service="IMySingleton, MyInterfaceAssembly"
type="MySingleton, MyImplementationAssembly" lifestyle="Singleton" />
Of course, the topic of wiring up your application to use DI is beyond my answer, but either you're using it and this answer helps you or you can take a peak at the concept and fall in love with it. :)

ASP.NET MVC - State and Architecture

After a pair programming session, an interesting question came up which I think I know the answer for.
Question: Is there any other desired way in ASP.NET MVC to retain 'state' other than writing to database or a text file?
I'm going to define state here to mean that we have a collection of person objects, we create a new one, and go to another page, and expect to see the newly created person. (so no Ajax)
My thoughts are we don't want any kung-fu ViewState or other mechanisms, this framework is about going back to a stateless web.
What about user session? There are plenty of valid use cases to store things in session. And what about a distributed caching system like memcached? You also seem to leave out the query string - which is an excellent state saver (?page=2). To me those seem like other desirable methods to save state across requests...?
My thoughts are we don't want any kung-fu ViewState or other mechanisms, this framework is about going back to a stateless web.
The example you provided is pretty easy to do without any sort of "view state kung fu" using capabilities that are already in MVC. "User adds a person and sees that on the next screen." Let me code up a simple PersonController that does exactly what you want:
public ActionResult Add()
{
return View(new Person());
}
[HttpPost]
public ActionResult Add(PersonViewModel myNewPersonViewModel)
{
//validate, user entered everything correctly
if(!ModelState.IsValid)
return View();
//map model to my database/entity/domain object
var myNewPerson = new Person()
{
FirstName = myNewPersonViewModel.FirstName,
LastName = myNewPersonViewModel.LastName
}
// 1. maintains person state, sends the user to the next view in the chain
// using same action
if(MyDataLayer.Save(myNewPerson))
{
var persons = MyDataLayer.GetPersons();
persons.Add(myNewPersion);
return View("PersonGrid", persons);
}
//2. pass along the unique id of person to a different action or controller
//yes, another database call, but probably not a big deal
if(MyDataLayer.Save(myNewPerson))
return RedirecToAction("PersonGrid", ...etc pass the int as route value);
return View("PersonSaveError", myNewPersonViewModel);
}
Now, what I'm sensing is that you want person on yet another page after PersonSaveSuccess or something else. In that case, you probably want to use TempData[""] which is a single serving session and only saves state from one request to another or manage the traditional Session[""] yourself somehow.
What is confusing to me is you're probably going to the db to get all your persons anyway. If you save a person it should be in your persons collection in the next call to your GetPersons(). If you're not using Ajax, than what state are you trying to persist?
ASP.NET MVC offers a cleaner way of working with session storage using model binding. You can write a custom model binder that can supply instances from session to your action methods. Look it up.

Resources