Ant
no notes

Collection Editor - Part 1 - Requirements

I need to be able to;

  1. edit sub-collections in a parent view with a strongly typed model
  2. add and remove items from the sub-collection
  3. a single point of commit for all changes made on the form, parent and sub-collections

This sort of thing is kinda of difficult in MVC, even with the open extensible architecture we hear so much about.

There are a number of established articles on the subject, but none give me all of the points above in a reusable way. I am sure that there are components available to help.. but surely its not that difficult is it?

The Model

What I have here is a simple one to many/parent-children object model graph, People and their Addresses.

Both objects enjoy the decoration with appropriate DataAnnotations so ensure the data is as required for the work-flow and the database.

e.g.

[Required]
public string Line1 { get; set; }

The Controller

Get/Post edit methods are responsible for marshalling the data from the database to the model and back via an entity framework data access layer (EF4.1).

Data To Model

model = (from p in _dataContext.People
            where p.Id == model.Id
            select new PersonModel
                {
                    Id = p.Id,
                    FirstName = p.FirstName,
                    LastName = p.LastName,
                    Email = p.Email,
                    Addresses = from a in p.Addresses
                                orderby a.Id
                                select new AddressModel
                                        {
                                            Id = a.Id,
                                            Name = a.Name,
                                            Line1 = a.Line1,
                                            Line2 = a.Line2,
                                            Town = a.Town,
                                            County = a.County,
                                            Postcode = a.Postcode
                                        }
                }).FirstOrDefault();

Model To Data (using a simple mapper)

DataContext.AddMap<PersonModel, Person>(
    (s, d, ctx) => {
        d.FirstName = s.FirstName;
        d.LastName = s.LastName;
        d.Email = s.Email;
        d.Addresses = ctx.Map(s.Addresses, d.Addresses);

        return d;
    });

and

DataContext.AddMap<AddressModel, Address>(
    (s, d, ctx) => {
        d.Name = s.Name;
        d.Line1 = s.Line1;
        d.Line2 = s.Line2;
        d.Town = s.Town;
        d.County = s.County;
        d.Postcode = s.Postcode;

        return d;
    });

so this can be done ...

if (ModelState.IsValid) {
    try {
        var data = _dataContext.People
            .Include(p => p.Addresses)
            .FirstOrDefault(d => d.Id == model.Id);
        if (data == null) return HttpNotFound();

        _dataContext.Map(model, data);

        _dataContext.SaveChanges();

        return RedirectToAction("Edit", new {data.Id});
    }
    catch (ValidationException vex) {
        ModelState.AddModelError("_form", vex.Message);
    }
    catch (Exception ex) {
        Trace.TraceError(ex.ToString());
        ModelState.AddModelError("_form", "unexpected error");
    }
}

The important thing here is that I have a very simple post method, validation is handled by MVC before I get here, all I have to do is;

  1. Check ModelState.IsValid
  2. Check for existence
  3. Map model to data
  4. Save and redirect to pick up id changes

All wrapped to catch data layer exceptions - lovely, this is the same thing I would expect to see in most my post methods.

The View

I want my view to be simple too, for this I am calling EditorFor, which picks up templates to generate a nice UI for editing the Person.

<form id="EditForm" class="form" 
        action="@Url.Action("Edit")" method="post">
    <div>
        @Html.EditorFor(m => m.FirstName)
        @Html.EditorFor(m => m.LastName)
        @Html.EditorFor(m => m.Email)
        @Html.EditorFor(m => m.Addresses)
    </div>

    <p class="submit">
        <input type="submit" value="Save" class="button" />
        <a href=".." class="linkButton">Cancel</a>
    </p>

    @Html.AntiForgeryToken()
    @Html.ValidationSummary()
</form>

So What is Wrong Here

Unfortunately, out of the box this will produce a number of errors, to deal with these you have to add a few extensions and a model binder, not too difficult and in the next post I will explain what these are and where they go.

In the meantime there is a demo app to whet your appetite.

Attachments

sandbox-collectionEditor.zip c# asp.net mvc vs 2010

* please note the demo uses the SQL Server compact framework, you can change the web.config connection string to point at your SQL Server instance instead

Post a Note

(required)

(required never shown)

On Twitter Follow MrAntix on Twitter

15/05/2012
WindowsAzure
Announcing the MEET Windows Azure Event! Streamed online June 7th. Register at http://t.co/bObzTAuL  #MEETAzure #WindowsAzure

10/05/2012
kevinwhinnery
Comparing Titanium and PhoneGap - A common question I get asked at developer events and conferences is how... http://t.co/Zq2eND6B

09/05/2012
brianleroux
PhoneGap goals and philosophy: http://t.co/wkq8wI2T

just now
rogerjenn
"The #Bing Search API on #WindowsAzure #Marketplace is Here!" but it will cost you money above the free quota: http://t.co/rkcpWzW5

just now
_romanovich
RT @WindowsAzure: The Bing Search API on #WindowsAzure Marketplace is Here!  http://t.co/GLILFMYe