MVC Partial Model Updates - asp.net-mvc

I often find myself in the situation where I only want to present and edit some fields from my model. Let's say I have a model that represts an address, perhaps I just want the form to update the city and post code fields (bad example, but hopefully it explains the scenario).
I know of two methods:
1) Persist the unwanted fields in hidden input elements on the form, or...
2) Create a dedicated view model that just defines the fields I need.
I favour option #2, but I don't have a nice clean way of merging the data from the view model back into the 'real' model within the controller action. At the moment, I follow this approach...
1) Store the record I'd in a hidden field on the view model
2) When the page posts back, the controller retrieves the original record and I manually assign each field from the view model to the real model
3) Save the real model back to the data store.
This works, but it is quite a lot of work and very easy to miss an assignment/reassignment and I was wondering if anyone knew of another approach?

Use the System.ComponentModel.DataAnnotations.MetadataType.
Something like:
public class BaseClassOfProperties
{
public string Name { get; set; }
}
public interface INameViewableProperties
{
[Display(name = "Your Name")]
string Name { get; set; }
}
public interface INameHiddenProperties
{
//[scaffoldColumn(false)] this completely hides the fields
[UIHint("Hidden")] // i think...
string Name { get; set; }
}
[MetadataType(typeof(INameViewableProperties)]
public class NameViewAbleProperties : BaseClassOfProperties
{
}
[MetadataType(typeof(INameHiddenProperties)]
public class NameHiddenProperties : BaseClassOfProperties
{
}

Related

With MVC/Entity Framework, how does one manage housekeeping tasks in the view?

My understanding is that only one model can be passed to the view at a time. The problem with this that I see is that I am being forced to pass the Entity Framework model, and not any model that will manage housekeeping in the view. Here is what I mean:
You need to make a page that allows someone to submit Cars to the database. Along with the form fields (e.g. CarName, CarMake, CarYear) you also need a checkbox at the bottom of the page called "Remember Values" which when checked will "remember" the form values when the user clicks the Submit button at the bottom, so when they return all of their form data is still in the form fields. Needless to say, this Remember Values variable is not part of the Entity Framework model- it is just a housekeeping variable for use in the view.
How would you guys handle adding this checkbox? It feels like it should be part of the model, but I can't send two models to the view. Am I just looking at this issue wrong? What would you recommend?
.NET 4.5/MVC 5/EntityFramework 6
This is a good situation to be using ViewModels.
Build your ViewModels with all properties that you'd want to send/retrieve to/from your view. For example:
EF Entity
public class Car {
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Make { get; set; }
public virtual string Year { get; set; }
}
View Model
public class AddCarViewModel {
public Car Car { get; set; }
public bool RememberValues { get; set; }
}
Controller
public class CarController : Controller {
// Constructor....
public ActionResult Add() {
var vm = new AddCarViewModel();
return View(vm);
}
[HttpPost]
public ActionResult Add(AddCarViewModel vm) {
if (ModelState.IsValid) {
_carService.Save(vm.Car);
}
return View(vm);
}
}
Another good approach is to create Domain Transfer Objects, which are POCO classes to hold data that is transferred through the pipes. For example, in your business layer you may want to audit any changes to your Car model. So you may have properties like CreatedBy, CreatedDate, UpdatedBy, UpdatedDate, etc. (These properties are generally never displayed to the end-user but are important to store).
So you'd create the following classes:
public class Car {
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Make { get; set; }
public virtual string Year { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User UpdatedBy { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual DateTime UpdatedDate { get; set; }
}
public class CarDTO {
public Guid Id { get; set; }
public string Name { get; set; }
public string Make { get; set; }
public string Year { get; set; }
}
and you can use a library such as AutoMapper to map properties from Car -> CarDTO:
var car = _carService.GetCarById(id);
var carDTO = Mapper.Map<Car, CarDTO>(car);
This way, you can choose which properties you want exposed to your views by utilizing DTO's.
I always create an additional model that I can convert to and from between the EF model.
This additional model gets passed to the View and holds al the neccesary properties like CarName, Carmake, CarYear, Remember and probably most importantly, the Id of that particular object.
So when the user submits, this model gets passed to the Post method where you can extract all the required properties. You fetch the database model using the Id from your DbContext and update the properties with the values that were just passed through.
Technically you can send two models to the view, if the model is actually something like a Tuple:
#model Tuple<SomeEFModel, SomeViewModel>
Though that's kind of ugly. And if you're creating a view model anyway you might as well just make it a composite of the Entity Framework model. Something like:
public class SomeViewModel
{
public SomeEFModel EFModel { get; set; }
public string SomeOtherProperty { get; set; }
// other stuff...
}
Then just build an instance of that in the controller and send it to the model:
#model SomeViewModel
You could even just de-couple the EF model and the view model entirely, creating a custom view model that has everything for that view and then translating to/from that and the EF model at the controller level. Ultimately it comes down to what implementation looks cleaner and is easier to maintain, which can differ from one context to another.
Edit: Another option, if the models get unwieldy for whatever bits of the framework you're relying on, could be to separate your outgoing and incoming models. For pushing data to the view, you can use the composite view model above. But then when the data comes back from the view just use a normal Entity Framework model and a couple of additional parameters for your additional fields:
public ActionResult Edit(int id)
{
// build the view model with the EF model as a property
return View(someViewModel);
}
[HttpPost]
public ActionResult Edit(SomeEFModel model, string someOtherProperty)
{
// here you have an EF model from the view like normal
// plus the additional property (however many you need)
// you can even create a separate view model to collect the other properties
// as long as the names are well defined, the model binder should build both
}
First, you absolutely should NOT be passing your EF models directly to your view, and you should certainly NOT be posting directly to your EF models. This is essentially taking untrusted, unsanitized input and directly writing it to your data model with only bare minimal validation.
While this may work with simple models with no security or other ramifications, imagine a situation where you allowed a user to edit his profile information. Further, imagine that in his profile you also stored information relating to his subscription information. A specially crafted submit could alter his subscription information and give himself free access to your site, or worse...
Instead, you use view models. Apart from the security aspects, view models are good because other than in very simple CRUD style sites, your views requirements are typically different from your data models requirements. For instance, a particular field might be nullable in your data model, but you might want to make it required in your view. If you pass your model directly, then you can't do that easily.
Finally, Aggregate view models aggregate many different submodels to provide an overall model for the view, which is what you are getting at. You would then use a service layer, repository, or business logic layer to translate your view model to your data model, massaging data or applying logic as needed.

Using an MVC Model as a filter in the repository

I have a details view that is typed to IEnumerable. The view with a bunch of drop downs that let you add filters to the list of records rendered.
All these dropdowns correspond to properties on the MVC model:
public class Record
{
public string CustomerNumber { get; set; }
public string CustomerName { get; set; }
public string LineOfBusiness{ get; set; }
public DateTime? Date { get; set; }
}
Now, I'm using my model as my dto to shuffle data between my controller and my repo. Since all my drop down filters represent the model properties, I pass my model to a repo retrieval method, check its properties and filter based on its values? In other words:
public IEnumerable<TradeSpendRecord> Get(TradeSpendRecord record)
{
IQueryable<tblTradeSpend> query = _context.tblRecords;
if (!String.IsNullOrEmpty(record.CustomerName))
query = query.Where(x => x.CustomerNumber == record.CustomerNumber);
if (!String.IsNullOrEmpty(record.LineOfBusiness))
query = query.Where(r => r.LOB == record.LineOfBusiness);
SNIP
Hope this isn't too subjective, but I'm wondering if anyone has any input about whether this is a good/bad practice. I haven't seen a whole lot of examples of dynamic filtering like I need to do, and am looking for some guidance.
Thanks,
Chris
If you're doing what I think you're doing, I'm not sure this is the best way of doing it.
Keep your 'Models' in your MVC/presentation layer (whether this is one physical assembly or not) dedicated to your presentation layer. The only things that should be touching them are your Views and your Controllers. You don't want what should be independent entities to be so tightly coupled to your View Models.
I'd suggest creating a separate TradeSpendFilter class, which, at its simplest, exposes the filterable properties of your domain entity (likely more than any given View Model). You'd then pass this into your "filtering service" or whatever it may be. This also means you can extend your filtering functionality independent of both your domain models and your MVC app. For example, if you suddenly want to filter multiple objects, you can simply change...
public class TradeSpendFilter
{
public string CustomerName { get; set; }
...
}
...to...
public class TradeSpendFilter
{
public IEnumerable<string> CustomerNames { get; set; }
...
}
... without causing all sorts of problems for your MVC app.
Additionally, it will also mean you can make use of your filtering functionality elsewhere, without tying further components to your MVC app and ending up in a bootstrapped mess.

Dynamic form with indeterminite number of items

I have a class:
class Item
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
I have a view where I want objects for the class above created using inputs (so I have a textbox for Name and a date select type thing for Date). However, I want thev user to be able to click a link and through jquery/javascript another textbox and date select will be added to the form, and this can happen unlimited times.
How can I bind this to a model so that I can return it to my action method? Ideally the model would be something like:
class MyModel
{
public string AProperty { get; set; }
public List<Item> Items { get; set; }
}
Apologies for the poor wording, struggling to describe what I want but I think this should get the point across.
You want to use a client-side template and then return JSON to your controller. If you are using MVC 3, JSON model binding is built-in, but in MVC 2 you need to set up your own binder. There is an example here.
I recommend using KnockoutJS for your client side. It's very simple for working with dynamic collections and very well documented. You can see an example similar to what you're trying to do here as well as in the previous link.

Advice on structuring MVC ViewModel Classes (Parent with many children)

I'm writing a message board webpage. The page consists of a Topic item, then a list of Response and a form to add an additional response.
Im struggling to structure my page and viewdata classes in such a way that they are clean and allow me to take advantage for editor templates and validation attributes.
Currently I have one page to do all the above, and Im thinking my viewdata class will eventually look something like this:
public class TopicViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string Title { get; set; }
[ValidateNonEmpty("Please enter some text")]
public string TopicBody { get; set; }
public IList<TopicResponseViewsData> Responses { get; set; }
public TopicResponseViewsData NewResponse { get; set; }
}
public class TopicResponseViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string ResponseText{ get; set; }
}
My page is typed to a TopicViewsData, it just seems ugly that I have to have NewResponse property just so the page can have access to the validation attributes on TopicResponseViewsData. Is there a nicer way to do this?
Sounds like you are headed towards a massive and complex view, not to mention the issues you are already seeing with your model structuring. Rather than making trade offs to make what you have work I have a few recommendations on your overall view model design.
I tend to separate my models into ViewModels and FormModels. ViewModels are for displaying data and FormModels are for taking user input. Not only does this provide a clear designation of function it generally allows me to keep my FormModel properties typed to primitives, strings, and dates in addition to providing a single place for applying validation logic. While, in my ViewModels I am afforded the flexibility to use complex property types and do not have to worry about validation logic.
To make things even easier I follow Jimmy Bogard's suggestion that you should have only one view per model. By not mixing and matching models I have found my models stay focused and my views do not turn into spaghetti. To keep things tidy I name my models similarly to the Controller and View they are tied to. I might end up with a few extra models, but it is a small price to pay for a cleaner design.
I think that the Body property in the TopicViewsData model is redundant with the NewResponse property.
So your view is working with responses where each response has a body. So:
public class TopicResponseViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string Body { get; set; }
}
So far so good. Next you said that you have a list of responses to show and a new response to add, so:
public class TopicViewsData
{
public IList<TopicResponseViewsData> Responses { get; set; }
public TopicResponseViewsData NewResponse { get; set; }
}
For the moment, given your description that's all I see necessary in the view model. At least model reflects your scenario description.

How can I reuse Model Metadata for custom View Models?

I'm working on an ASP.NET MVC 2 project with some business entities that have metadata dataannotations attributes applied to them (Validation attributes, Display attributes, etc.).
Something like:
//User entity
public class User
{
[DisplayName("Vorname")]
[Required(ErrorMessage = "Vorname fehlt")]
[StringLength(MaxNameLength, ErrorMessage = "Vorname ist zu lang")]
public string FirstName { get; set; }
[DisplayName("Nachname")]
[Required(ErrorMessage = "Nachnamefehlt")]
[StringLength(MaxNameLength, ErrorMessage = "Nachname ist zu lang")]
public string LastName { get; set; }
[Required]
public string Password{ get; set; }
}
Using the metadata from different views is no problem, as long as I am using my business entities as viewmodels or as part of a viewmodel like this:
//custom viewmodel with a user entity
public class CustomViewModel
{
public User{get;set;}
//some more properties...
}
However, sometimes I need to code a view for editing some, but not all fields of an entity. For those fields I want to reuse the metadata already specified in my user entity. The other fields should be ignored. I'm talking about custom view models like this:
[MetadataType(typeof(User))]
public class UserNameViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
//no password on purpose, the user should only
//edit his first and last name in this view
}
That's where I am running into problems. The custom view model above leads to an exception when the view is generated, because it has no password property.
The associated metadata type for type
'Zeiterfassung.Models.ViewModels.Users.UserNameViewModel+UserModel'
contains the following unknown
properties or fields: Password. Please make sure
that the names of these members match
the names of the properties on the
main type.
Also, even if this exception did not occur, I expect to get into even more trouble with model validation on form submit because Password is marked as required in my business entity.
I can think of several workarounds, but none seem really ideal. In any case I can't change the database layout so that the password field would be in a separate entity in my example above.
How would you handle this scenario?
The only recommendation I could give you is to have view models specific to each view and have only the necessary properties and validation attributes on those view models. Don't worry if you repeat some validation attributes and properties on your view models. That's what they are meant for: reflect the logic of a given view.

Resources