This post exists because trying to explain something 140 characters at a time makes you sound insane.
Earlier Brad Wilson speculated:
Theory: in ASP.NET MVC, master pages cause users to use ViewData instead of strongly typed models more often than any other reason.
To which I replied:
@bradwilson That, plus keeping properties in sync on a class that exists only to stand between an action and view isn’t very DRY
It’s true there are a huge number of reasons to need data in a master layout like navigation, sidebar widgets, current user profile, ad codes, etc. But if that was the main cause should be pretty easy to address by having the master layout’s data in a layer superclass for all of your action-view models.
I believe another even more serious problem is the view model can make you repeat yourself a huge number of times.
One of the things that’s really nice about actions on a controller is you can avoid the proliferation of classes you get with the command pattern. If you make a model for each action you get that problem back in spades. You could keep track of commonality between actions to reduce the number of action/view model classes, but that’s more work and you’re playing a balancing act between how many classes you are making and how many unused properties are acceptable for actions that don’t produce strictly identical data.
Plus it’s not DRY… I’ve added the action to a controller, then I need to add the class to hold the data. I’ve acquired some data in the action, then I need to add the typed property to the action’s model. Some data’s refactored out of the action, then I need to remember to remove it from the data bucket class too.
Just to throw an idea out there, to have a DRY action-view model class, how about adding the idea of a dynamic partial class to the language? A partial class which merges with additional auto-implemented properties at compile-time, as a result of the property assignments on that type.
So here’s that you would have today. The following assumes DefaultMasterData members will be assigned by filters if the model has an appropriate base class.
public class DefaultMasterData
{
public UserProfile Profile {get;set;}
public string AdvertisementCampaignCode {get;set;}
}
public class HomeIndexData : DefaultMasterData
{
public Product Product {get;set;}
public Order CurrentOrder {get;set;}
}
[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
public ActionResult Index()
{
...acquiring stuff here...
// return view of model
return View(new HomeIndexData {Product = mostRecentProduct, CurrentOrder = activeSessionOrder});
}
}
Here’s if you had a dynamic partial class. The assignment of non-existant properties results in an implicit partial with auto-implemented properties to be merged in at compile time.
public class DefaultMasterData
{
public UserProfile Profile {get;set;}
public string AdvertisementCampaignCode {get;set;}
}
public dynamic partial class HomeIndexData : DefaultMasterData
{
}
[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
public ActionResult Index()
{
...acquiring stuff here...
// return view of model
// creates implicit partial with auto-implemented properties
return View(new HomeIndexData {Product = mostRecentProduct, CurrentOrder = activeSessionOrder});
}
}
And to kill just a bit more code, how about a the ability to declare the named type without the empty class statement?
public class DefaultMasterData
{
public UserProfile Profile {get;set;}
public string AdvertisementCampaignCode {get;set;}
}
[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
public ActionResult Index()
{
...acquiring stuff here...
// return view of model
// creates implicit partial with auto-implemented properties
// also results in the initial declaration of the type HomeIndexData based on DefaultMasterData
return View(new dynamic partial HomeIndexData:DefaultMasterData
{Product = mostRecentProduct, CurrentOrder = activeSessionOrder}
);
}
}
There! Now I think you’ll see people using a base of ViewPage<HomeIndexData> in Index.aspx.
Clearly there are lots of questions about namespace, etc., but you get the idea.
Final note, to declare an anonymous type with a base class, it would be nice to support something like.
var x = new :MyBase {Foo="bar", Frap=5};
Another final note - you wouldn’t need to do it all inline of course.
[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
public ActionResult Index()
{
var model = new dynamic partial HomeIndexData:DefaultMasterData();
...acquiring stuff here...
// assignments causing more auto-impl props here
model.Product = mostRecentProduct;
model.CurrentOrder = activeSessionOrder;
// return view of model
return View(model);
}
}
Recent Comments