Should ViewModels have Count properties to simplify the View logic? - asp.net-mvc

Which approach to ViewModels is better:
1. Just have an ICollection<T> in the ViewModel and access it's properties in views, which is pretty much what I'd do in ASP.NET Forms, like this:
public ICollection<Order> Orders {get; set;}
so that in the View I would do something like this
#if(Model.Orders.Count > 0){
or
2. Create a property for the Count of an ICollection so the View can simply reference that value directly.
public ICollection<Order> Orders { get; set; }
public int OrderCount { get { return Orders.Count ; } }
and then in the view
#if(Model.OrderCount > 0) {
or perhaps a boolean property HasOrders to further reduce the logic in the View?
Edit
I'm a bit surprised by the comments, I accept that this is subjective, but then so are questions about whether to use a string property for a date and everyone has to start learning somewhere.
Will I have numerous uses of the OrderCount property? The if and then a label to display the actual count. As such it will be used more frequently than say the customer email address yet I would be astonished if anyone suggested that
public string Email { get; set; }
was taking things too far.
To try to refocus the question a little; what I'm trying to determine is should the ViewModel provide simple properties for everything the view needs - so there is no need to reach down into the Model.Orders to access the Count. Should the View be kept pure and free from logic / 'programming'

3.) Don't use a Collection<T> on a viewmodel, it's probably overkill. Instead, use T[]. Why? Because you shouldn't need .Add, .Remove, and other overhead methods offered by ICollection for an IEnumerable property in a viewmodel. In the end, if you are just using it as a DTO to pass data from a controller to a view, an array is perfectly fine. Nothing will have to be added to or removed from the enumerable during transit to and from the controller. Arrays are generally faster and leaner than Lists and other IEnumerable implementations.
public Order[] Orders { get; set; }
Then, don't use .Count, use .Length. Having a separate property is usually overkill too IMO. Why? Because it just means you end up writing more code where you don't have to. Why add an OrdersCount property when you can just use Orders.Length?
#if (Model.Orders.Length > 0) {
If you are looking for something a little shorter, you can use the .Any() LINQ extension method (note you will have to have using System.Linq; when using this in a viewmodel class, but nothing extra should be needed to use it in a razor view):
#if (Model.Orders.Any()) { // returns true if Model.Orders.Length > 0
One possible exception to this guideline could be if Orders is not set, meaning it is null. In that case, your razor code above would throw a NullReferenceException. For this you could create a HasOrders property on the viewmodel to test against null and .Length. However a simpler solution could be to just initialize the property in a constructor:
public class MyViewModel
{
public MyViewModel()
{
Orders = new Order[0];
}
public Order[] Orders { get; set; }
}
Granted, with the above someone could still set the array to null, so it's your decision of whether to do this, or create a separate property to test against null, or just test against null in your razor code.
using System.Linq;
public class MyViewModel
{
public Order[] Orders { get; set; }
public bool HasOrders { get { return Orders != null && Orders.Any(); } }
}
...or...
#if (Model.Orders != null && Model.Orders.Any()) {
Any way you go, you end up with a little more code in either the consuming class or the consumed class. Use these factors to decide which approach means less code to write:
a.) Is it possible for the property to be null?
b.) How many collection properties are in the viewmodel?
c.) How many times do you have to test against either null or .Length in a razor view?

Related

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.

Entity Framework ASP.NET MVC private model fields

There is a field in our database which really ought to be a boolean, but for some reason the original developers made it a CHAR which will either be set to "1" or "0".
[Column("CHARGEABLE")]
[StringLength(1)]
private string Chargeable { get; set; }
I want my model to represent this field as a boolean so I figured I could add a property to my model to wrap it:
[NotMapped]
public bool ChargeableTrue
{
get
{
return Chargeable == "1" ? true : false;
}
set
{
Chargeable = value ? "1" : "0";
}
}
Now on my View I just display the EditorFor ( ChargeableTrue ), but when I click save it doesn't actually update it.
I think what is happening is that when the model is being updated, it's still attempting to get the value of 'Chargeable' from the View, even though I haven't displayed it there. And since there is no input field, it just gets null and ends up saving that to the database.
if (ModelState.IsValid)
{
db.Entry(call).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
What is one expected to do in this situation?
Based on KMan's answer, here's the extended version just in case you're not familiar with creating view models.
The idea is that your domain object is not really what you want to be updating exactly from your views. Instead, you create a go-between that can also include view-specific items (like a list of objects to populate a drop-down).
public class MyViewModel {
public bool Chargeable { get; set; }
}
Now you can do this:
#* In view *#
Html.EditorFor(m => m.Chargeable)
// In controller
public ActionResult Save(MyViewModel model) {
if (ModelState.IsValid) {
var domainObject = new MyObject() {
Chargeable = model.Chargeable ? "1" : "0"
};
// the rest of your code using domainObject
}
}
I'd consider just creating an overload of your domain object's constructor that accepts your view model to keep the mapping in one place. I typically use a tool like AutoMapper to map objects or manual extension methods.
A view model typically contains a sub-set of your domain object's properties, but can contain all of them or more properties like lists, visbility states, etc. They come in incredibly useful and I've never done a MVC project where I haven't used them.
Use a view model and make your mapping on the controller.

Are related records loaded into HashSet or SortedSet?

Assume we have POCO class for Entity Framework 4:
public class Order
{
public long Id { get; set; }
public ISet<OrderItem> OrderItems { get; set; }
}
And this method to retrieve the order from database:
public Order GetOrder(long orderId)
{
using (var context = new MyModelEntities())
{
return context.Orders.Include("OrderItems").Where(order => order.Id == orderId).FirstOrDefault();
}
}
So suppose we do this:
Order myOrder = GetOrder(1);
Is myOrder.OrderItems a HashSet or SortedSet? Is there a way to control this?
Good question.
As far as i know (and there is no MSDN/blog/article i am aware of that dispells/proves this), a navigational property can be of any type as long as it implements ICollection<T>.
Both HashSet<T> and SortedSet<T> implement ICollection<T>, so either would be viable candidates.
Did you step through the code? You should be able to see which concrete class get's resolved.
Most people use ICollection<T> / IList<T>. Why are you wanting to declare the property as ISet<T>?
Why don't you just declare which type you want, instead of the interface.
Or you could try using dependency injection (For<ISet>().Use<HashSet>()).

ASP.NET MVC: Connection Controller with Model

I'm still learning, but with the stackoverflow commnuties help, I've been able to get closer and closer.
What I have right now is a View "Index.aspx":
System.Web.Mvc.ViewPage<Data.Models.GetDealsModel>
The Model:
public class GetDealsModel
{
// set up the model
public string DealId { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Logo { get; set; }
public string Website { get; set; }
public string TotalRows { get; set; }
}
And the controller:
public ActionResult Index()
{
LinqToDealsDataContext db = new LinqToDealsDataContext();
XElement xmlTree = XElement.Parse("<Request><ZipCode>92612</ZipCode></Request>");
var deals = db.spSearchDeals(xmlTree);
return View(deals);
}
And with this configuration I'm now getting this error:
The model item passed into the dictionary is of type 'System.Data.Linq.SqlClient.SqlProvider+SingleResult`1[Data.Models.spSearchDealsResult]', but this dictionary requires a model item of type 'Data.Models.GetDealsModel'.
I'm guessing that there's an issue connecting my Controller to my Model... I'm not sure why. PLEASE help me connect this final peice.
NOTE: I do understand that eventually I should separate my logic in the controller into a Repository Pattern, but for now, this will do.
You need to translate the data coming back from this call:
var deals = db.spSearchDeals(xmlTree);
into a GetDealsModel type. So something like:
GetDealsModel dealsModel = new GetDealsModel()
{
DealId = deals.DealId,
StreetAddress = deals.StreetAddress,
....
};
return View(dealsModel);
The reason being that your View is strongly typed to take a GetDealsModel, but your deals variable is not of that type and it gives you that exception when you pass it to the View.
You should create object of type GetDealsModel, but your DB Query returns object of type Data.Models.spSearchDealsResult. Try something like:
return new GetDealsModel
{
DealId = deals.Id,
// other fields here
}
Add to your learning curve list the following items:
Repository Pattern
Ask yourself the following question: Why do I need a service layer?
Read Steven Sanderson's book. It teaches you to think in MVC.
The above applies to your problems because your issues are clearly related to having code in your Controllers that should be in your Model (ie, data access code should be in a repository class). Ie, you are not thinking in MVC.
Your model should include the necessary repository classes, eg, DealRepository.
You need a Service class to map the objects your repository digs out of your database to your model class: that way conversion problems are encapsulated into the Service Layer code.
If you do this, you can then write in your controller:
public ActionResult Index()
{
return(DealService.GetByZipcode(92612));
}
Where DealService.GetByZipcode basically just maps DealRepository.GetByZipcode(92612) to your model class and returns the mapping result.
The DealRepository.GetByZipcode method would be roughly:
public static DealEntity GetByZipcode(string zip)
{
LinqToDealsDataContext db = new LinqToDealsDataContext();
XElement xmlTree = XElement.Parse("<Request><ZipCode>" + zip + "</ZipCode></Request>");
var deals = db.spSearchDeals(xmlTree);
return deals;
}
The DealEntity class is just whatever Linq gives you for your table.
The reason WHY for all this:
The reason for this structure is as follows:
a. All you data access code is in one place: DealRepository. You can test and debug that independently of everything else.
b. The mapping code is all in one place: DealService. You can test and debug that independently of everything else.
c. In other words, you need to properly separate your concerns.
The problem with your existing code is precisely that you have NOT separated concerns. Ie, you have taken a dash of MVC and put it in a food processor and ended up with mush full of problems that are way more difficult to deal with than they need be.
Your model is mixed into your controller, there is no repository, no service layer.
So hold your horses just a while and take the time to read Steve Sanderson's book.
I would also try modelling a simpler problem. That xml parsing makes my head hurt even on a good day.
NOTE:
You could seriously improve your naming conventions. LinqToDealsDataContext? You're kidding, right?

How do I pass multiple objects to ViewPage in ASP.NET MVC?

I think I know the answer, but I would like to bounce around some ideas.
I would like to pass several (in this instance 2) somewhat different pieces of data to a View. My initial thought is simply to wrap-up the various objects into a containing object and pass them along that way. Then from the View, I'd have something like
var objContainer = ViewData.Model;
var thisObject = objContainer.ThisObject;
var thatObject = objContainer.ThatObject;
and these could be used independently in the Master Page and View Page.
Is that the "best" way?
I find it useful to create additional classes dedicated that are to be presented to the Views. I keep them in a separate namespace called 'Core.Presentation' to keep things organized. Here is an example:
namespace Core.Presentation
{
public class SearchPresentation
{
public IList<StateProvince> StateProvinces { get; set; }
public IList<Country> Countries { get; set; }
public IList<Gender> Genders { get; set; }
public IList<AgeRange> AgeRanges { get; set; }
}
}
Then I make sure that my View is a strongly typed view that uses the generic version of that presentation class:
public partial class Search : ViewPage<SearchPresentation>
That way in the View, I can use Intellisense and easily navigate through the items.
Yes, the class that you specify as the model can be composed of other classes. However, why not just use the dictionary like so:
ViewData["foo"] = myFoo;
ViewData["bar"] = myBar;
I think this is preferable to defining the model as a container for otherwise unrelated objects, which to me has a funny smell.
I've got the same dealie going on. Here's my solution (may not be the best practice, but it works for me).
I created a number of "Grouping" classes:
public class Duo<TFirst,TSecond> { /*...*/ }
public class Trio<TFirst,TSecond, TThird> { /*...*/ }
and a factory object to create them (to take advantage of type inference... some of the TFirsts and TSeconds and TThirds can be LONG)
public static class Group{
public static Duo<TFirst, TSecond> Duo(TFirst first, TSecond second) {
return new Duo<TFirst, TSecond>(first, second);
}
/*...*/
}
It gives me type safety and intellisense with a minimum of fuss. It just smells because you're grouping together classes that essentially have no real relation between them into a single object. I suppose it might be better to extend the ViewPage class to add a second and third ViewModel, but the way I did it takes lots less work.

Resources