MVC : Does putting data in cache or session belong in controller? - asp.net-mvc

I'm a bit confused if saving the information to session code below, belongs in the controller action as shown below or should it be part of my Model?
I would add that I have other controller methods that will read this session value later.
public ActionResult AddFriend(FriendsContext viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
// Start - Confused if the code block below belongs in Controller?
Friend friend = new Friend();
friend.FirstName = viewModel.FirstName;
friend.LastName = viewModel.LastName;
friend.Email = viewModel.UserEmail;
httpContext.Session["latest-friend"] = friend;
// End Confusion
return RedirectToAction("Home");
}
I thought about adding a static utility class in my Model which does something like below, but it just seems stupid to add 2 lines of code in another file.
public static void SaveLatestFriend(Friend friend, HttpContextBase httpContext)
{
httpContext.Session["latest-friend"] = friend;
}
public static Friend GetLatestFriend(HttpContextBase httpContext)
{
return httpContext.Session["latest-friend"] as Friend;
}

I wouldn't worry too much about where you put your code to save the model to session. It's one line of code so you are not saving anything or making anything clearer by extracting it out.
For creating your Friend object, I personally would either use something like Automapper, or have a populate method on my view model.
var friend = viewModel.Populate(new Friend());
public void Populate(Friend friend)
{
friend.FirstName = this.FirstName;
}
As for saving the friend to session, if you do want to extract it, I would do something similar to your static methods but as session extension methods. Just because it makes it immediately clear where it's being stored.
// set
Session.LatestFriend(friend);
// get
var latestFriend = Session.LatestFriend();

Controller is the right place to store and get data, and populate models with that Data.
Models are used as support for that data to be shown within views.
So what you're doing is pretty correct.
No need for the static class that will just get or store from Session, as it is about only one line of code.
+1 for the advice of Bigfellahull when dealing with multiple fields initialization.

Related

How to instantiate a user profile object once per request, in ASP.NET MVC, and then reuse in all code for that request?

I am using MVC3, ASP.NET4.5, SQL Server 2008.
I have a "User" class which contains details about the current user ie Organisation details.
At present I instantiate a new user object on each view or action where needed which makes calls to the db, and of course this is wasteful, as I could instantiate this object when the request is first made. I could then reuse the object throughout the request processing, to include action and view processing.
What would be the correct approach to do this. I had thought of loading the object into a session, but I am trying to avoid the use of these. Also I had an idea of using a static class that checks for the present of the instantiated object, and creates it is absent, then gets the relevant properties. Here is some possible code to illustrate my thinking:
public static class stCurrentUser
{
public static CurrentUser GetUserDetails()
{
CurrentUser myUser = (CurrentUser)System.Web.HttpContext.Current.Session["User"];
if (myUser == null)
{
myUser = new CurrentUser();
System.Web.HttpContext.Current.Session["User"] = myUser;
return myUser;
}
else
{
return myUser;
}
}
}
Thanks

mvc best practice for common code

I have an MVC application. The following code is being used in multiple places within a controller and in multiple controllers. I would like to have this code in one place and call it from each location. What's the best way to do that in MVC?
The code below gets a row from the database and creates ViewData that can be read from the view. With webforms, I would create a public sub within a class and pass the year and month values. Is there a way this code could become part of the model?
var monthlyexpenseincome = (from vu_monthlyresult in dbBudget.vu_MonthlyResults
where vu_monthlyresult.Month == defaultmonth && vu_monthlyresult.Year == defaultyear
select vu_monthlyresult).Single();
var yearlyexpenseincome = (from vu_yearlyresult in dbBudget.vu_YearlyResults
where vu_yearlyresult.Year == defaultyear
select vu_yearlyresult).Single();
ViewData["MonthlyExpenses"] = monthlyexpenseincome.Expenses;
ViewData["MonthlyIncome"] = monthlyexpenseincome.Income;
ViewData["MonthlyProfit"] = monthlyexpenseincome.Income - monthlyexpenseincome.Expenses;
Generally, If you have common code across multiple controllers, you can create another class which inherits from Controller and keep your methods there and let your indidual Controlellers inherit this new class
public class BaseController : Controller
{
protected string GetThatInfo()
{
//do your magic logic and return some thing useful
return "This is demo return.Will be replaced";
}
}
And now you can inherit from this for your other controllers
public class UserController: BaseController
{
public ActionResult Index()
{
return VieW();
}
}
But In your case, The data you are taking is something specific to your Domain data. so i would suggest you to move it to a different class ( like a new Service / Business Layer)
public static class ProfitAnalysis
{
public static decimal GetTotalExpense()
{
//do your code and return total
}
}
And you can call it from wherever you wanted like
decimal totalExp=ProfitAnalysis.GetTotalExpense();
And You will soon realize, Having so much ViewData usage is making your code difficult to read and Maintain. Do not wait for that day. Switch to strongly typed classes to pass data.
You should place your queries in a "business layer", which is just a class that you call to do the business logic. Then you can reuse it in any place you like, just instantiate the business class and use it. You could also make the methods static if they don't require state, then you wouldn't have to even instantiate it.
For example:
var expenseService = new expenseService();
ViewData["MonthlyExpenses"] = expenseService.GetMonthlyExpenses();

Asp.net MVC Model for view and Layout

I've been trying to find a good way to handle the Models of our Asp.net MVC websites when having common properties for all the pages. These properties are to be displayed in the Layout (Master Page). I'm using a "BaseModel" class that holds those properties and my Layout use this BaseModel as its model.
Every other model inherits from that BaseModel and each has specific properties relative to the view it represents. As you might have guessed, my Models are actually View Models even if that's not quite relevant here.
I have tried different ways to initialize the BaseModel values
By "hand" in every view
Having a base controller that has an Initialize virtual method to do it (so specific controller can implement specific common behavior for exemple)
Having a base controlelr that override OnActionExecuting to call the Initialize method
Using a helper class to do it outside of the controller
Using a Model Factory
But none of those really appeal to me:
Seems obvious to me, but DRY is one reason enough to justify that (actually I never tried that solution at all, I'm just putting it to be able to loop on that point in the last point).
I don't like that one because it means that whenever a new Controller is added, you need to know that it has to inherit from the BaseController and that you need to call the Initialize method, not to mention that if your controller has overriden the base one, to call the base anyway to maintain the values.
see next point
and 3. are a variation on the same topic but that doesn't really help with the issues of the second solution.
My favorite so far, but now I have to pass a few more variables to set those values. I like it for the inversion of dependence. But then if I want to provide values from the session, I need to pass them explicitly for exemple, then I'm back to square one as I have to provide them by hand (being references or through an interface of any kind)
Of course, (almost) all of those solutions work, but I'm looking for a better way to do it.
While typing this question, I found maybe a new path, the builder pattern that might also do, but implementations can become quickly a burden too, as we can have dozens of views and controllers.
I'll gladly take any serious recommandation/hint/advice/patterns/suggestion !
Update
Thanks to #EBarr I came up with another solution, using an ActionFilterAttribute (not production code, did it in 5 minutes):
public class ModelAttribute : ActionFilterAttribute
{
public Type ModelType { get; private set; }
public ModelAttribute(string typeName) : this(Type.GetType(typeName)) { }
public ModelAttribute(Type modelType)
{
if(modelType == null) { throw new ArgumentNullException("modelType"); }
ModelType = modelType;
if (!typeof(BaseModel).IsAssignableFrom(ModelType))
{
throw new ArgumentException("model type should inherit BaseModel");
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = ModelFactory.GetModel(ModelType);
var foo = filterContext.RequestContext.HttpContext.Session["foo"] as Foo;
model.Foo = foo;
model.Bar = somevalue;
filterContext.Controller.TempData["model"] = model;
}
}
Calling it is then really simple:
[Model(typeof(HomeModel))]
public ActionResult Index()
{
var homeModel = TempData["model"] as HomeModel;
// Add View Specific stuff
return View(homeModel);
}
And it gives me the best of every world. The only drawback is to find a proper way to passe the model back to the action.
Here it's done using the TempData object, but I also consider updating the model that one can find in the ActionParameters.
I'm still taking any serious recommandation/hint/advice/patterns/suggestion for that, or the previous points.
I went through almost exactly the same process as I dove into MVC. And you're right, none of the solutions feel that great.
In the end I used a series of base models. For various reasons I had a few different types of base models, but the logic should apply to a single base type. The majority of my view models then inherited from one of the bases. Then, depending on need/timing i fill the base portion of the model in ActionExecuting or OnActionExecuted.
A snippet of my code that should make the process clear:
if (filterContext.ActionParameters.ContainsKey("model")) {
var tempModel = (System.Object)filterContext.ActionParameters["model"];
if (typeof(BaseModel_SuperLight).IsAssignableFrom(tempModel.GetType())) {
//do stuff required by light weight model
}
if (typeof(BaseModel_RegularWeight).IsAssignableFrom(tempModel.GetType())) {
//do more costly stuff for regular weight model here
}
}
In the end my pattern didn't feel too satisfying. It was, however, practical, flexible and easy to implement varying levels of inheritance. I was also able to inject pre or post controller execution, which mattered a lot in my case. Hope this helps.
The idea that gave me #EBarr to use an action filter was actually working but felt wrong in the end, because there was no clean way to retrieve the model without passing through a viewbag, or the httpcontext items, or something alike. Also, it made mandatory to decorate every action with its model. It also made the postback more difficult to handle. I still believe that this solution has merits and might be useful in some specific scenarios.
So I was back to square one and started looking more into that topic. I came to the following. First the problem has two aspects
Initializing the data for the views
Rendering the data
While looking for more idea, I realized that I was not looking at the problem from the right perspective. I was looking at it from a "Controller" POV, whereas the final client for the model is the view. I was also reminded that the Layout/Master page is not a view and should not have a model associated with it. That insight put me on what feels the right path for me. Because it meant that every "dynamic" part of the Layout should be handled outside of it. Of course, sections seems the perfect fit for that, because of their flexibility.
On the test solution I made, I had (only) 4 different sections, some mandatory, some not. The problem with sections, is that you need to add them on every page, which can quickly be a pain to update/modify. To solve that, I tried this:
public interface IViewModel
{
KeyValuePair<string, PartialViewData>[] Sections { get; }
}
public class PartialViewData
{
public string PartialViewName { get; set; }
public object PartialViewModel { get; set; }
public ViewDataDictionary ViewData { get; set; }
}
For exemple, my model for the view is this:
public class HomeViewModel : IViewModel
{
public Article[] Articles { get; set; } // Article is just a dummy class
public string QuickContactMessage { get; set; } // just here to try things
public HomeViewModel() { Articles = new Article[0]; }
private Dictionary<string, PartialViewData> _Sections = new Dictionary<string, PartialViewData>();
public KeyValuePair<string, PartialViewData>[] Sections
{
get { return _Sections.ToArray(); }
set { _Sections = value.ToDictionary(item => item.Key, item => item.Value); }
}
}
This get initialized in the action:
public ActionResult Index()
{
var hvm = ModelFactory.Get<HomeViewModel>(); // Does not much, basicaly a new HomeViewModel();
hvm.Sections = LayoutHelper.GetCommonSections().ToArray(); // more on this just after
hvm.Articles = ArticlesProvider.GetArticles(); // ArticlesProvider could support DI
return View(hvm);
}
LayoutHelper is a property on the controller (which could be DI'ed if needed):
public class DefaultLayoutHelper
{
private Controller Controller;
public DefaultLayoutHelper(Controller controller) { Controller = controller; }
public Dictionary<string, PartialViewData> GetCommonSections(QuickContactModel quickContactModel = null)
{
var sections = new Dictionary<string, PartialViewData>();
// those calls were made in methods in the solution, I removed it to reduce the length of the answer
sections.Add("header",
Controller.UserLoggedIn() // simple extension that check if there is a user logged in
? new PartialViewData { PartialViewName = "HeaderLoggedIn", PartialViewModel = new HeaderLoggedInViewModel { Username = "Bishop" } }
: new PartialViewData { PartialViewName = "HeaderNotLoggedIn", PartialViewModel = new HeaderLoggedOutViewModel() });
sections.Add("quotes", new PartialViewData { PartialViewName = "Quotes" });
sections.Add("quickcontact", new PartialViewData { PartialViewName = "QuickContactForm", PartialViewModel = model ?? new QuickContactModel() });
return sections;
}
}
And in the views (.cshtml):
#section quotes { #{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "quotes").Value); } }
#section login { #{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "header").Value); } }
#section footer { #{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "footer").Value); } }
The actual solution has more code, I tried to simplify to just get the idea here. It's still a bit raw and need polishing/error handling, but with that I can define in my action, what the sections will be, what model they will use and so on. It can be easily tested and setting up DI should not be an issue.
I still have to duplicate the #section lines in every view, which seems a bit painful (especialy because we can't put the sections in a partial view).
I'm looking into the templated razor delegates to see if that could not replace the sections.

Persist data between different controllers from a base page

I am not sure I am asking the right question here.
I have a shared page (master page) that calls a couple of partial pages for side menu, header, footer etc.. and all my controllers inherit a BaseController.
Now, depending on the user login status, I need to show different data in all those partial pages and I thought where is the best place to check whether a user is logged in or not - BaseController.
And therein lies my problem. I need to contact one of my web services to see if a user is logged in and get some relevant data if he is. I only need to do this once, and since all controllers inherit from BaseController, each of those partial page calls results in the web service call.
Obviously, I cannot just stick a private bool variable isUserAuthenticated and check for flag, as, each controller will have a new instance of the base controller.
In traditional asp.net projects, I would put this stuff in HttpContext.Current.Items[] and use re-use it but I cannot (somehow) access that in MVC.
I cannot just not inherit from basepage on partial pages as they can also be called independently and I need to know the user login status then too.
What is the best way to call a function just once, or, rather, store a bool value for the duration of one call only? - accessible between controlers..
How do people do this?
thanks, sorry, I'm a newbie to mvc!
You can still use HttpContext.Items, but you'll need to access it via a HttpContextBase instance.
For backwards compatibility you can wrap an HttpContext in an HttpContextWrapper, like so
var context = new HttpContextWrapper(HttpContext.Current);
#iamserious's answer above suggests using a static property - which I strongly disagree with. Setting a static variable is application wide and would mean each and every user would be using the same variable - so all would have the same login data. You want to store it either per user in Session or per Request via HttpContext.Items.
I'd suggest doing something using like this approach, then no matter where you call ContextStash.GetInstance, you'll receive the same instance for the lifetime of the same request. You could also follow the same pattern and use HttpContext.Session instead of HttpContext.Items:
// could use this.HttpContext inside a controller,
// or this.Context inside a view,
// or simply HttpContext.Current
var stash = ContextStash.GetInstance(this.HttpContext);
if(!stash.IsSomething)
{
// do something to populate stash.IsSomething
}
// class
public class ContextStash
{
const string cacheKey = "ContextStash";
public ContextStash(HttpContextBase context)
{
// do something with context
}
// your shared properties
public bool IsSomething { get; set; }
public string Foo { get; set; }
public int Bar { get; set; }
// instance methods
public static ContextStash GetInstance()
{
return GetInstance(new HttpContextWrapper(HttpContext.Current));
}
public static ContextStash GetInstance(HttpContext context)
{
return GetInstance(new HttpContextWrapper( context ));
}
public static ContextStash GetInstance(HttpContextBase context)
{
ContextStash instance = context.Items[cacheKey] as ContextStash;
if(null == instance)
{
context.Items[cacheKey] = instance = new ContextStash(context);
}
return instance;
}
}
well, if you just want to one variable across several instances of BaseController, use the static keyword, like so:
public class BaseController : Controller
{
private static bool isUserAuthenticated;
}
Now, no matter how many instances of BaseController you have, they all will share a single isUserAuthenticated variable, you change value in one, you change it in all.
This is the very basic of most object oriented programming and you should really take some time out to go through the concepts of OOP, if you don't mind me saying.

ASP.net Dynamic Data - OnPropertyChanging Not Updating other columns

Here is my code:
partial void OnisApprovedChanging(bool value)
{
this.dateApproved = DateTime.Now;
}
'dateApproved' is updated in the business logic, but this change is not being applied to the database table. I've seen some examples where DateUpdated columns are updated whenever any edit to a table is made, but I'm only interested in updating the time-stamp when this field is updated and I'm not sure of the best way to access the DataContext from this scope.
Do I need to instantiate the Data-context and update it manually?
EDIT Did some more research, and found that some blogs suggested adding business logic on update like this:
public partial class DataContext : System.Data.Linq.DataContext
{
partial void Updateaccount(account instance)
{
//business logic here
}
}
However, I cannot determine any way to find out if particular fields have changed. Any ideas?
Found this is the way to get the original entity and do comparisons.
partial void Updateaccount(account instance)
{
account acctPriorToUpdate = accounts.GetOriginalEntityState(instance);
if (instance.isApproved != acctPriorToUpdate.isApproved)
{
//Do Stuff
}
this.ExecuteDynamicUpdate(instance);
}

Resources