سؤال

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top