Continuing where we left off, and before we add some custom validation to our model, let’s try to clean up things a bit.

If we take a look at the current form, we notice two things:

 edit view

First, we have some fields here that the user shouldn’t be allowed to change. One is the ID, which is an internal identifier that shouldn’t be visible to users, much less editable. The other one is the ContributorPoints which is something that the system handles automatically, and measures the importance of the Author in our system. We should remove these fields from the form, but we’ll get to that later.

Second, we are not allowing the user to change his assigned Community. If you look at the model we use, the Community is a separate entity and we have a navigation property that allows us to get it easily. However, as far as our object model go, the Community property of the Author object is not a scalar property, that’s why the view template didn’t render it.

So, let’s handle this first: first we need to change the action method so we include this information in our model. Entity Framework uses lazy loading, meaning it will only populate associated entities when you request it, but we can change our query to include that information:

public ActionResult Edit(int id)

 {

     using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

     {

         var author = ctx.Authors.Include("Community").First(a => a.Id == id);

         return View(author);

     }

 }


Showing the data looks straightforward, I can just add this to my view:

<div class="editor-label">

    <%= Html.LabelFor(model => model.Community.Name) %>

</div>

<div class="editor-field">

    <%= Html.TextBoxFor(model => model.Community.Name) %>

    <%= Html.ValidationMessageFor(model => model.Community.Name) %>

</div>


and get this result:

edit view with community data

But this is not really a good user experience. Not only we don’t know what to put in that field, we are actually running into a bigger problem. Since this field is “binded” to a property of the Community Entity, if we update our model, we will actually change the Name property of the Community entity, instead of changing the association between this Author and an existing community.

So, how do we update a one-to-many relationship AND provide a good user experience for it? Let’s start by changing our Edit action method:

public ActionResult Edit(int id)

{

    using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

    {

      var author = ctx.Authors.Include("Community").First(a => a.Id == id);

      var Communities = ctx.Communities.ToList();

 

      ViewData["Communities"] = new SelectList(Communities, "Id", "Name", author.Community.Id);

 

      return View(author);

    }

}


So, we are getting a list of all communities and preparing a SelectList using the Id property as the value and the Name property as the display. We are also traversing the relationship from our author object to find the current selected community, and using that as the selected value.

The SelectList is added to the ViewData dictionary so that it is available in our view. Let’s change the rendering of the view to display this information:

<div class="editor-label">

    <%= Html.LabelFor(model => model.Community) %>

</div>

<div class="editor-field">

   <%= Html.DropDownList("CommunityId", (IEnumerable<SelectListItem>)ViewData["Communities"])%>

</div>


If we try out the new view, we’ll get this nice result:

edit view with community drop down list

And we can confirm that the dropdown list is correctly implemented by doing “View Source” on IE:

 <select id="CommunityId" name="CommunityId">

  <option selected="selected" value="1">ASP.NET</option>

  <option value="2">ASP.NET MVC</option>

  <option value="3">Silverlight</option>

 </select>


We also need to take care of the [HttpPost] Edit method so we can correctly update the information:

[HttpPost]

public ActionResult Edit(Author updatedAuthor)

{

    if (!ModelState.IsValid)

        return View(updatedAuthor);

 

    using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

    {

        var author = ctx.Authors.Include("Community").First(a => a.Id == updatedAuthor.Id);

        ctx.ApplyCurrentValues("Authors", updatedAuthor);

        author.CommunityReference.EntityKey = new System.Data.EntityKey(

                                                "BulletProofDemoEntities.Communities",

                                                "Id",

                                                Int32.Parse(Request.Form["CommunityId"]));

        ctx.SaveChanges();

        return RedirectToAction("Index");

    }

}


We are accessing the CommunityId value directly from the Request.Form dictionary. That’s because the default Model Binder does not know what to bind this value to (we could probably get around this by implementing our own Model Binder, maybe I’ll try it later).

To change the association between the Author and the Community entities I use an auto-generated property called CommunityReference and set the EntityKey property. I could also do it like this:

int communityId = Int32.Parse(Request.Form["CommunityId"]);

Community c = ctx.Communities.First(com => com.Id == communityId);

author.Community = c;


This code is a bit more intuitive, however it is less efficient as it will force one extra round-trip to the database, which can be avoided by simply setting the EntityKey property.

So, we did a lot of stuff already… I did intend to cover protecting the model from “illicit binding” and adding validation based on data annotations, but since the post is already long as it is, I’ll add that to part 3 of the Quick Tip.

Until then, have fun!