Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

Wednesday, June 6, 2012

Editing complex entities within an MVC3 view

Recently I have been busy at work building this site to help us manage all our buildings access cards, helping assign/re-assign and generally keep track of who can access various levels of our building etc. We decided to go down the route of constructing the site using a combination of MVC3, Entity Framework, sql server 2008 and some other front end client side libraries such as bootstrap to help speed up the page design (we were told to build it quick with as less $$ as possible). Given that I myself have just recently started using the framework, I ran into a small issue to do with representing complex entities within our MVC views.
Our Db schema resembled something of the sort shown below (note: I have toned it down quite a bit to keep it simple):
Create Table Cards(
id int  Identity(1,1) Constraint pk_card_id primary key
, AssignedTo varchar(50));

create table Locations
(
id int Identity(1,1) Constraint pk_locations_id primary key
, Name varchar(20));

create table CardLocations
(
 CardId int not null Constraint fk_card_id Foreign key references Cards(id)
, LocationId int not null Constraint fk_location_id Foreign key references Locations(id)
, Constraint pk_CardLocations_id primary key(CardId, LocationId)
);
We have 2 primary entities, Cards which maintains a list of all access cards handed out to people and Locations which is a list of all locations in our building and other facilities. The CardLocations table maintains a mapping of all the Locations that a Card has access to (note: both columns of the table are part of a composite primary key). Let us assume that we have the following sample data inserted in the DB:

Cards
id          AssignedTo
----------- --------------------------------------------------
1           Employee A
2           Employee B
3           Employee C

Locations
id          Name
----------- --------------------
1           Level 1
2           Level 2
3           Level 3
4           Level 4
5           Level 5


CardLocations
CardId      LocationId
----------- -----------
1           1
1           2
2           3
2           4
3           5

Hence if we use EF to generate an Entity model for this DB schema we end up with the following:

image

Entity framework automatically detects the many-many association between a Card and a Location entity and does not see the need to create a separate entity for the CardLocations table instead maintaining this as an Entity Association. Let us assume that we wish to allow the user to edit an access card’s details such as who it is assigned to and what locations it has access to. The next listing shows the mock-up of a simple controller used to edit a Card:
   1:  public class CardsController : Controller
   2:      {
   3:   
   4:          CardsEntities model; //The Entity Model
   5:   
   6:          public CardsController()
   7:          {
   8:              model = new CardsEntities();
   9:          }
  10:   
  11:          public ViewResult Edit(int? id)
  12:          {
  13:              if (id == null)
  14:                  throw new ArgumentNullException("id");
  15:              var card = model.Cards.Single(c => c.id == id);
  16:              return View(card);
  17:          }
  18:      }
As seen, the Edit action method takes the id of the card that we wish to edit and using the reference to the entity model, retrieves the card and passes it to the strongly typed view Edit.cshtml (scripted below).
   1:  @model ComplexMvcEf.Library.Model.Card
   2:  @{
   3:      ViewBag.Title = "Edit";
   4:  }
   5:  @using (Html.BeginForm())
   6:  {
   7:      <h2>
   8:          Card</h2>
   9:   
  10:      @Html.HiddenFor(model => model.id)
  11:   
  12:      @Html.LabelFor(model => model.AssignedTo) @Html.EditorFor(model => model.AssignedTo)
  13:   
  14:   
  15:      <h2>
  16:          Locations</h2>
  17:      foreach (var location in Model.Locations)
  18:      {
  19:          @Html.EditorFor(model => location);
  20:      }
  21:   
  22:      <input type="submit" name="Submit" value="Edit" />
  23:  }

Now, does anyone see what is wrong here? The HTML helpers all do their jobs, we have created a strongly typed model (for the type of the Card entity object) where we simply create editors allowing the user to edit who the card is assigned to. However our problems start with being able to assign locations to this card. Navigating to this view renders the following:

image

Obviously binding to the list of locations only allows us bind to and edit the Location entity object, but it is only the 2 location entities that are associated with the card. What about adding a new location? Also in the true spirit of the view we really don't want to be able to edit a location and that functionality should be restricted to a different page. We would basically like some means of selecting from a list of locations, which ones we wish the card to have access to. I know a whole heap of you HTML cowboys out there are probably coming up with a zillion flash ideas constituting fancy JQuery dropdowns with a whole lot of Ajax magic to dynamically manage a cards location. Any other time, I would love to go down that path, however that would simply eat up project hours like popcorn. In retrospect, the number of locations in our building are always going to be few and finite, hence simply listing them on the page is not going to do anybody any harm. Our primary problem here is that we have tried to use what are primarily, our domain models (the Card and Location entities) as a view model, something which may not always work out, especially since without writing some unnecessary boilerplate code, there is no easy way we can represent that a location is selected/ not selected and not to mention any extra effort required to have the mvc model binders work correctly etc (remember that we are dealing with entity objects and model contexts). To rectify the issue, we introduce a couple of classes to use as our view-model

   1:  public class CardEditModel
   2:      {
   3:          //the card being edited
   4:          public Card Card { get; set; }
   5:   
   6:          //the list of locations to display on the view
   7:          public List<CardLocationModel> LocationsModel { get; set; }
   8:      }
   9:   
  10:      public class CardLocationModel 
  11:      {
  12:          //The location Id
  13:          public int Id { get; set; }
  14:          
  15:          //The location name
  16:          public string Name { get; set; }
  17:          
  18:          //Flag to indicate if card can access this location
  19:          public bool Selected { get; set; }
  20:      }

Notice that there are 2 classes created. A collection of CardLocationModel objects is maintained by the CardEditModel class. The use for this will become more apparent in the following sections. We can now integrate this within our Edit action method

   1:  public ViewResult Edit(int? id)
   2:          {
   3:              if (id == null)
   4:                  throw new ArgumentNullException("id");
   5:              //Create a new viewModel
   6:              var viewModel = new CardEditModel();
   7:              //read the card from the DB
   8:              var card = model.Cards.Single(c => c.id == id);
   9:              if (card != null)
  10:              {
  11:                  viewModel.Card = card;
  12:                  //set the locations
  13:                  viewModel.LocationsModel = model.Locations
  14:                      .AsEnumerable() 
  15:                      .Select(location => new CardLocationModel()
  16:                      {
  17:                          Id = location.id
  18:                          //Set flag if the access card has access to this location
  19:                          ,Selected = card.Locations.Any(loc => loc.id == location.id)
  20:                          ,Name = location.Name
  21:                      }).ToList();
  22:              }
  23:   
  24:              return View(viewModel);
  25:          }

The logic for the Edit action method in the shown above basically involves reading the card entity from the models context (line 8) and referencing it via our view model of type CardEditModel (line 11). Lines 13-21 basically involve selecting all the locations from the model’s context. We then use this list to generate a list of CardLocationModel objects which is then referenced by our view-model. Hence we now have a complete list of locations to display to the user. The Selected boolean flag of the CardLocationModel class allows us to represent if the access card being edited is allowed to access this location(line 19). Hence incorporating this into our Edit.cshtml view

   1:  @model ComplexMvcEf.Models.CardEditModel
   2:  @{
   3:      ViewBag.Title = "Edit";
   4:  }
   5:  @using (Html.BeginForm())
   6:  {
   7:      <h2>Card</h2>
   8:      @Html.HiddenFor(model => model.Card.id)
   9:      @Html.LabelFor(model => model.Card.AssignedTo) @Html.EditorFor(model => model.Card.AssignedTo)
  10:      
  11:      <h2>Locations</h2>
  12:      <p>
  13:      @for (int i = 0; i < Model.LocationsModel.Count; i++)
  14:              {
  15:               @Html.HiddenFor(model => model.LocationsModel[i].Id)
  16:               @Html.CheckBoxFor(model =>  model.LocationsModel[i].Selected) @(Model.LocationsModel[i].Name)
  17:              }
  18:      </p>
  19:      <input type="submit" name="Submit" value="Edit" />
  20:  }

Hence, our new view-model allows us to logically represent all the locations available to the user and easily change the mappings as part of the form

image

Additionally to handle the post-back we simply add another Edit action method to the CardController class like so

   1:         [HttpPost]
   2:          public ActionResult Edit(
   3:              int? id
   4:              , List<CardLocationModel> locationsModel)
   5:          {
   6:              //ensure that the entity object is attached to the model context
   7:              var card = model.Cards.Single(c => c.id == id);
   8:   
   9:              if (card != null
  10:                  //update the name if edited in the view
  11:                  && TryUpdateModel(card)
  12:                  )
  13:              {
  14:                  //remove all the cards locations that are no longer selected
  15:                  card.Locations
  16:                      .RemoveAll(l => !locationsModel.Any(crdLocationModel => crdLocationModel.Id == l.id && crdLocationModel.Selected));
  17:   
  18:                  card.Locations.AddAll(
  19:                      model.Locations
  20:                      .AsEnumerable()
  21:                      .Where(
  22:                      l => //ensure that the location is selected by user
  23:                          locationsModel.Any(crdModelLocation => crdModelLocation.Id == l.id && crdModelLocation.Selected)
  24:                          &&
  25:                          //ensure that the location is not already set
  26:                          !card.Locations.Contains(l)));
  27:   
  28:                  model.SaveChanges();
  29:              }
  30:              //simply redirecting to the same edit window
  31:              return RedirectToAction("edit");
  32:          }

Couple of things to note here. The action method is decorated with an mvc HttpPostAttribute filter (line 1), which ensures that this action only gets called on a post request. Also note that as part of our method parameter’s, we have a collection of CardLocationModel objects (line 4). This allows mvc’s model binders to search for and automatically populate the list from the form values. It may seem a little weird, but I purposely read out the card that we are interested in editing from the context first (line 7) followed by making a call to manually ask the controller to try and update the model’s values. This is done with the intent of ensuring that the Card entity object is always bound to the model’s context, and avoids any problems associated with attaching to the context. I am not suggesting that this is a recommended practise but I found that it saved me a lot of time.

Line’s 15-26 represent the code to synchronize the locations represented in our view-model with the actual Locations mapped to the card. The logic is quite simple. I simply remove any Location entity associated with the Card entity that does not have an equivalent CardLocationModel object in our locationsModel list (line 15-16) with its flag set to true (implying selected). I then go about adding mappings for all newly selected Locations to the Card (line 18-26). Note that I am using the following extension methods

   1:  public static class EntityCollectionExtensions
   2:      {
   3:          public static void RemoveAll<E>(this EntityCollection<E> collection
   4:              , Func<E, bool> itemSelector) where E : EntityObject
   5:          {
   6:              foreach (E item in collection.Where(entity => itemSelector(entity)).ToArray())
   7:                  collection.Remove(item);
   8:          }
   9:   
  10:          public static void AddAll<E>(this EntityCollection<E> collection
  11:              , IEnumerable<E> itemsToAdd) where E : EntityObject
  12:          {
  13:              foreach (var item in itemsToAdd)
  14:                  collection.Add(item);
  15:          }
  16:      }

We conclude by saving the changes made to the model context and because our application only has one view, we simply redirect back to the edit screen.

I hope this demonstrates the advantage behind using a view model inside all your MVC views. Binding directly to objects that represent your domain logic may not always be feasible and may limit the ability to logically represent a view to the user (for example, we were unable to provide a complete list of locations to the user). By creating a custom view model, we now have a clear control over how our domain objects can be represented to the user and also allows for the flexibility to represent the intent behind our view logically to the user.

Saturday, October 1, 2011

Defining metadata for Entities created using Entity Framework

Ive been using Entity framework for a couple of months now. I had this issue a while back, where I had an entity model designed using the entity framework designer  (note: I am currently using the 4.1 update). My task was to extend the entity model so that I could decorate an entity class with some data annotations.

For the sake of simplicity, let us assume that our entity data model has only the following entity, Product, defined like so:

If this product class was used, say as a model for an MVC3 controller, calling an html helper method to automatically render an editor for the model, (as shown below):


@model MyDomain.Entities.Product
 
@{
    ViewBag.Title = "Edit";
}
 
<h1>Edit @Model.Name</h2>
 
@using (Html.BeginForm())
{
    @Html.EditorForModel()
    <input type="submit" value="save" />
}

However this would result in the view being rendered with an html input element for each data member of the Product entity, including the Id field (of type int) which also happens to be the primary key. This may not be desired, as typically users should not be able to control setting the primary key for an entity. However we cannot simply extend the partial class and overwrite the field either. We could simply edit the code generated by EF for the Product EntityObject class, but any changes to the model would result in your data annotations being wiped, so this is not recommended either. As such the easiest way to solve this issue is to decorate your entity class with the System.ComponentModel.DataAnnotations.MetadataTypeAttribute 






First, we create a new class called ProductMetaData within which we define a single property called Id. Notice that the Id property is defined to be the same type as that of the Id field in the Product entity object, i.e. they are both of type Int. Once this is done, we can add a data annotation to instruct MVC's html helper methods to omit this field when rendering the view (note I am using the HiddenInput data annotation defined in the System.Web.Mvc namespace for this example):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
 
namespace MyDomain.Entities.Metadata
{

    public class ProductMetadata
    {
        [HiddenInput(DisplayValue=false)]
        public int Id { getset; }
    }
}


Following this, we can then simply create a partial class for the entity that we wish to annotate, in this case the Product entity. Once this is done, all that is left is to annotate the class with a MetadataTypeAttribute, passing in the type of the ProductMetaData class in the constructor and recompile.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MyDomain.Entities
{
    [MetadataType(typeof(MyDomain.Entities.Metadata.ProductMetadata))]
    public partial class Product
    {
 
    }
}


And that's basically it. During runtime, the html helper methods will now omit generating an editor for the Id field every time the view is rendered like so:




Algorithm for incrementing alphabetical version numbers


Scenario

Forgive me if this post seems a little strange & horribly formatted (haven't quite worked out how to get everything to look right yet). This basically constitutes my first ever blog post .... ever! so I thought I might start off with a slightly light topic. I had an odd request a while back to implement a versioning algorithm that incrementally helped maintain a version number every time a form was submitted/re-submitted using Ms infopath 2010. Long story short, the product is a small enterprise system that integrates with a third party workflow engine, allowing users to conduct and maintain an internal quality and review process. However the client asked for the versioning to be alphabetically indexed to follow a sequence like so:

a, b, c, d,...., z, aa, ab, ac, ...., az, ba, bb, bc, ...........,zy, zz, aaa, aab, .......
 After much googling, I must admit I could not find any code on the web that I could easily reuse. I did not (and still don't) know if this form of sequencing has an official name. In the end, I simply gave in and came up with my own solution. I must admit it was not very hard to come up with one, but thought I might share it all the same, should someone else face a similar request.

Solution & Code

Before we get under way, I imported the following .Net C# namespaces:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;


I was told to hard code every new form to have a default start version value of 'a'. Regardless, in an effort to implement error checking and ensure that my code was always dealing with strings containing alphabetical characters. This is nothing special, just using the .net reg-ex library to check for unwanted chars in the version string. I wrote the following method:

        /// <summary>
        /// Ensure that all versioning strings have an alphabetical version
        /// </summary>
        private static bool IsCharacterString(string currentVersionNumber)
        {
            if (string.IsNullOrEmpty(currentVersionNumber))
                throw new ArgumentNullException("currentId");
            //match to see if there are any non alphabetical chars in the string
            var pattern = @"[^a-z]";
            var regx = new Regex(pattern);
            //if there are no such chars, then this is an accepted character versioned string
            return !regx.IsMatch(currentVersionNumber.ToLower());
        }
Next I proceeded to define the following constants (at class level as they are used in more than one function):

        const string DEFAULT_FIRST_CHAR_VERSION = "a";
        //ascii representations of characters a,z
        const int ASCII_A = 97;
        const int ASCII_Z = 122;

This was followed by the actual algorithm that produces the next version:


       /// <summary>
        /// algorithm to generate char Id's in the format: 'a,b,c...z,aa,ab,ac,ad.... az, ba.....zz, aaa, aab, ....'
        /// </summary>
        private static string NextCharVersion(string currentVersion)
        {
            if (string.IsNullOrEmpty(currentVersion))
                throw new ArgumentNullException("currentVersion");
            //ensure that the string is in lower case
            currentVersion = currentVersion.ToLower();
            //get a vector of chars
            var charList = Encoding.ASCII.GetBytes(currentVersion.ToCharArray());
            //check the last character in the list
            byte lastChar = charList.Last();
            //if the last char is [a-y] &  not Z
            if (lastChar >= ASCII_A
                && lastChar < ASCII_Z)
            {
                // simply increment the last character
                charList[charList.Length - 1]++;
            }
            else
            {
                //check if the version number string needs incrementing (only happens if all chars in string are now 'z')
                if (charList.All(c => c == ASCII_Z))
                {
                    //make a new list
                    var tmpList = charList.Select(c => (byte)ASCII_A).ToList();
                    //add the next version increment
                    tmpList.Add(ASCII_A);
                    charList = tmpList.ToArray();
                }
                else
                {
                    //reset the last char to 'a'
                    charList[charList.Count() - 1] = ASCII_A;
                    //iterate backwasrds & keep incrementing the previous chars (except the last) untill you hit a char that is in the range [a-y]
                    for (int i = charList.Count() - 2; i >= 0; i--)
                    {
                        if (charList[i] >= ASCII_A && charList[i] < ASCII_Z)
                        {
                            charList[i]++;
                            break;
                        }
                        else
                        {
                            charList[i] = ASCII_A;
                        }
                    }
                }
            }
            return new string(Encoding.ASCII.GetChars(charList));
        }

All this is then followed up with a public facing function which accepts a string representing the current version (part of the submitted form), checks if it is a valid string and progresses to determine the next version that should be set (note: actual code that sets the value in the submitted form has been omitted):


public static string UpdateNextCharVersion(string currentVersion)
        {
            //if string is null then return first char
            if (string.IsNullOrEmpty(currentVersion))
                return DEFAULT_FIRST_CHAR_VERSION;
            //if is characters then increment as per character version
            else if (IsCharacterString(currentVersion))
            {
                return NextCharVersion(currentVersion);
            }
            else
            {
                //return the version as is if the string cannot be versioned
                return currentVersion;
            }
        }

Hopefully this should save a lot of people an hour or two if they ever have to implement something like this.