tldr> Once a customer has been selected, how can all other controllers execute their actions always in the 'context' of that customer without manually passing the ID around?
I'm trying to figure out what the 'right' way is to handle the situation where one entire con开发者_如何转开发troller (or more) are all dependent on an idea from a previous controller. For instance, let's say you were building some kind of customer management system. There'd be all kinds of customer functions, probably located on CustomerController. But then when you got into order management, you'd likely want to have an OrderController.
If you had only one OrderController, your methods would probably look like:
public ActionResult Edit(string id){...}
The id would be the id of the order, right? Where I get a little lost is when I need to get back to the customer. It's like the 'context' of all the actions taking place are within the customer. You could do this by always adding the customer id onto actions (URLs):
http://site.com/Orders/Edit/1234?customerId=abc
But this seems like it gets quite tedious to be grabbing that value and jamming it onto every action. There are options like Session, but that seems sloppy.
What's the right way to do this?
You can reduce general session sloppiness by writing a wrapper around the session, which exposes strongly typed properties.
//Sloppy weak typing:
int userId = (int)HttpContext.Current.Session["UserId"];
//Strongly typed goodness
int userId = SessionWrapper.UserId;
If you really want to avoid storing your value into the session, you can use an action filter that takes care of the value. I've used this approach once and it goes like this:
- Create a new route that contains a placeholder for your value, e.g. customer id.
- When customer is selected, add the id to route for the next GET or POST call.
- Create an action filter that reads the id from route and adds it to action parameters before action takes place (OnActionExecuting). When action is done this same filter adds the id from action parameters back to route data (OnActionExecuted).
- Use this new action filter wherever you wish, probably at controller level.
Now your customer id is easily persisted in the URL even though you do not manually add it to all links. If you need to use the customer id in an action method, you just add a parameter with the correct name. It is persisted over action method call even if you do not take it as a parameter.
If you want to go further, in your new action filter you can get the data from database and add that to action parameters instead of adding only id. This way you can further simplify your action methods.
Session is a place to store things like that.
You can also use a distributed cache (http://msdn.microsoft.com/library/cc645013.aspx) which scales well. Sample code from link above
// CacheFactory class provides methods to return cache objects
// Create instance of CacheFactory (reads appconfig)
DataCacheFactory fac = new DataCacheFactory();
// Get a named cache from the factory
DataCache catalog = fac.GetCache("catalogcache");
//-------------------------------------------------------
// Simple Get/Put
catalog.Put("toy-101", new Toy("thomas", .,.));
// From the same or a different client
Toy toyObj = (Toy)catalog.Get("toy-101");
// ------------------------------------------------------
// Region based Get/Put
catalog.CreateRegion("toyRegion", true);
// Both toy and toyparts are put in the same region
catalog.Put("toy-101", new Toy( .,.), "toyRegion");
catalog.Put("toypart-100", new ToyParts(…), "toyRegion");
Toy toyObj = (Toy)catalog.Get("toy-101", "toyRegion");
Serializing session info into an encrypted session cookie is another solution here. The benefit is the ability to avoid consumption of server resources and accomplish the same goal:
private void setSessionCookie() {
HttpCookie ck = new HttpCookie(XConstants.X_SESSION_COOKIE_KEY) {
Expires = DateTime.Now.AddMinutes(_sessionInfo.SessionTimeoutMinutes)
};
DateTime now = DateTime.UtcNow;
_sessionInfo.SessionLastValidatedAt = now;
ck.HttpOnly = true; // server-only cookie
ck["LastCheck"] = now.ToString(XConstants.XDATEFORMAT);
ck["Content"] = new Cipher().Encrypt(Serializer.Serialize(_sessionInfo).OuterXml,
ConfigurationManager.AppSettings[XConstants.X_APPKEY_KEY]);
System.Web.HttpContext.Current.Response.Cookies.Add(ck);
}
精彩评论