Tuesday, July 17, 2012

Learning MVC: Vertical Pop-Out Menu

Adding a pop-out menu to the application turned out to be two major steps. The second step can be broken down into obvious sub-steps too:

  • Create HTML structure for the menu and apply the CSS
  • Create a ViewModel for the menu partial view and fill it with data
    • Create a ViewModel
    • Create a controller to fill it with data
    • Modify the view to render the ViewModel properly

The first step I'm not describing here because I used a well-written tutorial[1]. I will only record here the styles I added to Site.css:

#menu {
    width: 12em;
    background: #eee;
}

#menu ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

#menu a, #menu h2 {
    font: bold 11px/16px arial, helvetica, sans-serif;
    display: block;
    border-width: 1px;
    border-style: solid;
    border-color: #ccc #888 #555 #bbb;
    margin: 0;
    padding: 2px 3px;
}

#menu h2 {
    color: #fff;
    background: #000;
    text-transform: uppercase;
}

#menu a {
    color: #000;
    background: #efefef;
    text-decoration: none;
}

#menu a:hover {
    color: #a00;
    background: #fff;
}

#menu li {position: relative;}

#menu ul ul ul {
    position: absolute;
    top: 0;
    left: 100%;
    width: 100%;
}

div#menu ul ul ul,
div#menu ul ul li:hover ul ul
{display: none;}

div#menu ul ul li:hover ul,
div#menu ul ul ul li:hover ul
{display: block;}

The second step I'll write down in more detail.

1. Create a partial view for the left sidebar. I decided to render the partial view by calling Html.Action, so I modified the div that holds the left sidebar in _Layout.shtml to look like this:

<div id="left-sidebar">
 @Html.Action("MenuResult", "LeftMenu")
</div>

Then I created a partial view called MenuResult.shtml and placed it in the Shared folder. This is how the HTML structure looks like:

@model Recipes.Models.LeftMenuViewModel

@{ Layout = null; }

<div id="menu">
    <ul>
        <li><h2>Recipes Menu</h2>
            <ul>
                <li>@Html.ActionLink("Recipes", "../Recipe/Index")
                    <ul>
                         <li>Test menu item
                            <ul>
                                    <li>Test child menu item</li>
                            </ul>
                        </li>                           
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</div>

2. ViewModel. After I was satisfied with the way the "stub" menu works, I started working on the model for the partial view. My first attempt looked something like this, a pretty simple model:

public class LeftMenuViewModel
{
 public List<Category> Categories { get; set; }
}

And a pretty simple nested foreach iterator that attempts to render the view:

<ul>
 <li>@Html.ActionLink("Recipes", "../Recipe/Index")
  <ul>
   @foreach(var cat in Model.Categories)
   {
    <li>@Html.ActionLink(cat.CategoryName, @Html.CategoryAction(cat.CategoryID).ToString())
    <ul>
     @foreach(var subcat in cat.SubCategories)
     {
      <li>@Html.ActionLink(subcat.SubCategoryName, @Html.SubCategoryAction(subcat.SubCategoryID).ToString())</li>
     }
    </ul>
   </li>                           
   }
  </ul>
 </li>
</ul>

3. Controller. The initial version of the controller looked something like this:

public class LeftMenuController : Controller
{
 public PartialViewResult MenuResult()
 {
  LeftMenuViewModel viewModel = new LeftMenuViewModel();

  using (RecipesEntities db = new RecipesEntities())
  {
   viewModel.Categories = db.Categories.ToList();
   foreach (var cat in viewModel.Categories)
   {
    cat.SubCategories = db.SubCategories.Where(s => s.CategoryID == cat.CategoryID).ToList();
   }
  }
  return PartialView(viewModel);
 }
}

That was the initial attempt, but when I ran this, I was presented with the following exception: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

The ObjectContext instance has been disposed

That looked a bit weird to me - I can see the elements of the collection, but the application refuses to iterate through them, complaining about the context. I found a couple of thoughtful posts on the reason for such a behaviour. First post [2] suggested that most likely, the execution of the query was deferred and now, in the view, when it actually tries to execute the query, I have disposed of the DbContext already and it fails. The suggestion was to convert the query results to list using .ToList() so the query gets executed before disposing. That did not work. Another post [3] suggested replacing the foreach iterator with a for one for a number of reasons, but that did not help either.

I gave it some thought and chose an easy way out - remove the dependency on the LINQ and complex entity objects and create my own very simple class to use in the view model. Here is the final code, which worked for me:

View Model

public class LeftMenuViewModel
{
 public List<MenuElement> elements { get; set; }
}

public class MenuElement
{
 public int id { get; set; }
 public string name { get; set; }
 public List<MenuElement> children { get; set; }

}

Controller

public class LeftMenuController : Controller
{
 public PartialViewResult MenuResult()
 {
  LeftMenuViewModel viewModel = new LeftMenuViewModel();
  viewModel.elements = new List<MenuElement>();

  using (RecipesEntities db = new RecipesEntities())
  {
   List<Category> cats = db.Categories.ToList();
   foreach (var category in cats)
   {
                MenuElement element = new MenuElement() {id = category.CategoryID, name = category.CategoryName, children = new List<MenuElement>()};

    List<SubCategory> subcats =
     db.SubCategories.Where(s => s.CategoryID == category.CategoryID).ToList();

    foreach (var subcat in subcats)
    {
     element.children.Add(new MenuElement(){id = subcat.SubCategoryID, name = subcat.SubCategoryName} );
    }
    viewModel.elements.Add(element);
   }
  }
  return PartialView(viewModel);
 }
}

View

@model Recipes.Models.LeftMenuViewModel

@{ Layout = null; }

<div id="menu">
    <ul>
        <li><h2>Recipes Menu</h2>
            <ul>
                <li>@Html.ActionLink("Recipes", "../Recipe/Index")
                    <ul>

                        @for(int i=0; i<Model.elements.Count(); i++)
                        {
                         <li>@Html.ActionLink(Model.elements[i].name, @Html.CategoryAction(Model.elements[i].id).ToString())
                            <ul>
                                @for(int j=0; j<Model.elements[i].children.Count(); j++)
                                {
                                    <li>@Html.ActionLink(Model.elements[i].children[j].name, @Html.SubCategoryAction(Model.elements[i].children[j].id).ToString())</li>
                                }
                            </ul>
                        </li>                           
                        }
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</div>

While this looks a bit more complex compared to the initial attempt, I think there is really much less space for error. Here is how the menu looks like:

Left side pop-out menu

References:

CSS Pop-Out Menu Tutorial
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
MVC Razor view nested foreach's model by . Also posted on my website

Thursday, July 5, 2012

Learning MVC: Editing the Variable Length List

Continuing the recipe database example, the next step is to not just keep a list of ingredients that are required for a recipe, but also to allow specifying the quantity of each ingredient. Up to this point, I did not need any "mapping table" due to the MVC magic - the Recipe object kept a list of ingredients, and the Ingredient object kept a list of recipes. That was all that MVC needed to resolve the many-to-many relationship between recipes and ingredients and I could get away with just two models.

Now that I want to know how much of the ingredient is required, I don't have a place to save this information. I don't think I can get away without the RecipeIngredient model any longer. I'm adding that and also giving the user the ability to add and remove ingredients. This requires a technique to edit and save a list of variable length, which is referenced at the end of this post and which I applied, with small modifications.

To display the list, the following technique is used: a div element which contains one entry from the list is placed in a partial view. Each time the user adds an entry to the list, another such div is appended to the list using an Ajax call to a jQuery append() function. Each time the user removes an entry, a div is removed, which is even easier. To begin with, I added the following to the recipe's Edit view

<fieldset>
 <div id="editorRows">
 <ol>
  @foreach (var item in Model.RecipeIngredients)
  {
   @Html.Partial("_RIEditor", item)
  }
 </ol>
 </div>
 @Html.ActionLink("Add another ...", "Add", null, new {id = "addItem"})
</fieldset>

The same partial view _RIEditor is repeated once per each ingredient. The "Add another ..." link will later add an ingredient when the user clicks it. And here's a sample _RIEditor partial view ( the Layout=null part was added because otherwise my partial view was rendered with the header and the footer).

@using Recipes.HtmlHelpers
@model Recipes.Models.RecipeIngredient
           
@{ Layout = null; }

<div class="editorRow">
    @using (Html.BeginCollectionItem("RecipeIngredients"))
    {
        <li class="styled">
        <div class="display-label">Ingredient:</div>@Html.TextBoxFor(model => model.Ingredient.IngredientName)
        <div class="display-label-nofloat">Amount:</div>@Html.TextBoxFor(model => model.Quantity, new { size = 4 })
        <a href="#" class="deleteRow">delete</a>
        </li>
    }
</div>

The key part here is the Html.BeginCollectionItem, which renders a sequence of items that will later be bound to a single collection. In short, it keeps track of the items as they are added or deleted, and when the form is finally submitted, the neat collection of items is returned, ready to be saved to database.

Now to allow for adding or deleting elements, I need to add two functions. Here's the example:

<script type="text/javascript">
<!--
    $("#addItem").click(function () {
        $.ajax({
            url: this.href,
            cache: false,
            success: function (html) { $("#editorRows").append(html); }
        });
        return false;
    });

    $("a.deleteRow").live("click", function () {
        $(this).parents("div.editorRow:first").remove();
        return false;
    });
-->
</script>

I also need to reference a couple of JavaScript files to make it work

<script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/MicrosoftMvcValidation.debug.js")" type="text/javascript"></script>

Almost done, now I only need to take care of that "Add another ..." link that I added to the Edit view. To make it work, I only need a simple action added to the controller. which will return the partial view.

public ViewResult Add()
{
 return View("_RIEditor", new RecipeIngredient());
}

So, what have I achieved? Here's the first approximation of how my ingredient list may look like when I load the recipe from the database

Original List of Ingredients

And how it looks when I click "Add another ...": a line for a new ingredient is added, looking the same as other lines and I can enter some data

Modified List of Ingredients

And then I can verify that some data is returned back on Submit, so my changes are not being lost

Data Posted by the View

The concept is working at this point - I get back my three "RecipeIngredients" and the data I entered. It's only the proof of concept at this point, I need to make a number of modifications to make it functional.

References:

BeginCollectionItem source code
Editing a variable length list, ASP.NET MVC 2-styleby . Also posted on my website

Thursday, June 14, 2012

Learning MVC: Unit Testing Validation in MVC

This is a rather short post. A little "trick" is required to test validation. If the Create function is called directly from the unit test, and the entity is validated using DataAnnotation validation, the model binder will not be invoked and the validation will not take place. The test result, obviously, will not be the expected one. To deal with that, it is necessary to mimic the behavior of the model binder. The following test creates a ValidationContext and uses the DataAnnotations.Validator class to validate the model.

[TestMethod()]
public void ValidateNameIsTooShort()
{
 Ingredient ingredient = new Ingredient() { IngredientName = "a" };

 var validationContext = new ValidationContext(ingredient, null, null);
 var validationResults = new List<ValidationResult>();
 Validator.TryValidateObject(ingredient, validationContext, validationResults);

 string error = GetValidationError(validationResults);

 Assert.AreEqual(error, Constants.Constants.IngredientNameTooShort);
}

If any errors are caught by the validation, they will be added to the ModelState of the controller. To get it back and compare with my expected error message, I just need to retrieve the first error message from the list of ValidationResult.

public string GetValidationError(List<ValidationResult> results)
{
 foreach (var result in results)
 {
  if(!String.IsNullOrEmpty(result.ErrorMessage))
  {
   return result.ErrorMessage;
  }
 }
 return string.Empty;
}

References:

Testing DataAnnotation-based validation in ASP.NET MVC
Unit tests on MVC validation
by . Also posted on my website

Tuesday, June 12, 2012

Learning MVC: Unit Testing CRUD actions in MVC

Continuing the example with a recipe database, and having the most basic structure working (recipes belong to categories and consist of ingredients), it is time to remember about TDD (test driven development). To be honest, it is already too late, because in proper TDD tests are supposed to be writter before the code is. I'll do my best next time. This time, however, there are some simple tests I can think of which relate to the Ingredient entity

  • Test that an Ingredient can be inserted into the database
  • Test that an Ingredient can be edited
  • Test that an Ingredient can be deleted from the database
  • Test that an Ingredient can not be deleted if it is used by any Recipe
  • Test that an Ingredient with a name that is too short can not be created
  • Test that an Ingredient with a name that is too long can not be created
  • Test that an existing Ingredient can not be edited so that its name becomes too short
  • Test that an existing Ingredient can not be edited so that its name becomes too long

In fact, the last four tests are not related to database manipulations because the errors will be caught before the attempt to save data is made. It would be more appropriate to place them in a separate class, which will test the model and I'll return to them next time. The first four tests appear to test database operations.

Firstly, I would like to have each test start from a known state - for example, an almost empty database which has some minimal amount of seed data. To achieve that, I use the initializer class that inherits from DropCreateDatabaseAlways. For reference, here is the full listing - the only thing I care about is to override the Seed function. I will use the data to test that I can not delete the ingredient which is in use.

public class TestDatabaseInitializer : DropCreateDatabaseAlways<RecipesEntities>
{
 protected override void Seed(RecipesEntities context)
 {
  var category0 = new Category { CategoryName = "Mains", Description = "Main Dishes" };
  var category1 = new Category { CategoryName = "Desserts", Description = "Dessert Dishes" };
  var categories = new List<Category>() { category0, category1 };
  categories.ForEach(c => context.Categories.Add(c));

  var ingredient0 = new Ingredient { IngredientName = "Meat" };
  var ingredient1 = new Ingredient { IngredientName = "Fish" };
  var ingredient2 = new Ingredient { IngredientName = "Potato" };
  var ingredients = new List<Ingredient>() { ingredient0, ingredient1, ingredient2 };
  ingredients.ForEach(i => context.Ingredients.Add(i));

  var recipes = new List<Recipe>();
  recipes.Add(new Recipe { RecipeName = "Grilled fish with potatoes", Category = category0, Ingredients = new List<Ingredient>() { ingredient1, ingredient2 } });
  recipes.Add(new Recipe { RecipeName = "Grilled steak with potatoes", Category = category0, Ingredients = new List<Ingredient>() { ingredient0, ingredient2 } });
  recipes.ForEach(r => context.Recipes.Add(r));
 }
}

I added a small function to my test class to create a database.

[TestInitialize()]
public void SetupDatabase()
{
 Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
 Database.SetInitializer<RecipesEntities>(new TestDatabaseInitializer());
}

Now I think I'm ready to create a simple test.

[TestMethod()]
public void CreateTest()
{
 SetupDatabase();
 IngredientController target = new IngredientController();
 Ingredient ingredient = new Ingredient() { IngredientName = "test" };

 ActionResult actual = target.Create(ingredient);
 Assert.IsTrue(ingredient.IngredientID != 0);

 RecipesEntities db = new RecipesEntities();
 var newIngredient = db.Ingredients.Find(ingredient.IngredientID);
 Assert.AreEqual(ingredient.IngredientName, newIngredient.IngredientName);
}

I create the Ingredient, record it's IngredientID and make sure that I can retrieve it back from the database by ID after it's added. Deletion and editing tests are equally simple. Now the "negative" test: I'm not testing what I can do now, but rather what I can not do. I should not be able to delete the ingredient if it is used by any recipes - that would destroy referential integrity. Here is a simple test that passes:

[TestMethod()]
public void CanNotDeleteUsedIngredient2()
{
 SetupDatabase();
 IngredientController target = new IngredientController();
 RecipesEntities db = new RecipesEntities();
 var ingredient = db.Ingredients.Where(i => i.IngredientName == "Meat").FirstOrDefault();
 int id = ingredient.IngredientID;

 Assert.IsNotNull(ingredient);
 ActionResult actual = target.DeleteConfirmed(id);

 db = new RecipesEntities();
 var deletedIngredient = db.Ingredients.Find(id);
 Assert.IsNotNull(deletedIngredient);
}

Okay, I tried to delete the ingredient and then I verified and it is still in the database. That's expected, but that tells me nothing about the reason why the ingredient was not deleted. Maybe the whole database is offline or there is an uncommitted transaction. To improve the test, I would like to know something about the reason. I need to check for the errors in the ModelState. Fortunately, I can access the ModelState from the ActionResult. Here is what I could do to return the first error that is found in the ModelState:

public string GetFirstErrorMessage(ActionResult result)
{
 ViewResult vr = (ViewResult)result;

 foreach (ModelState error in vr.ViewData.ModelState.Values)
 {
  foreach (var innerError in error.Errors)
  {
   if (!string.IsNullOrEmpty(innerError.ErrorMessage))
   {
    return innerError.ErrorMessage;
   }
  }
 }
 return string.Empty;
}

Now I can modify the test to check the ErrorMessage. The check is rather lame at the moment - the error message is created dynamically to tell the user what recipes exactly use the ingredient. So I do not want to check the full error message and I'm satisfied with the fact that the first 10 characters are what I expect. Here is the slightly modified test:

[TestMethod()]
public void CanNotDeleteUsedIngredient()
{
 SetupDatabase();
 IngredientController target = new IngredientController();
 RecipesEntities db = new RecipesEntities();
 var ingredient = db.Ingredients.Where(i => i.IngredientName == "Meat").FirstOrDefault();
 int id = ingredient.IngredientID;

 Assert.IsNotNull(ingredient);

 ActionResult actual = target.DeleteConfirmed(id);
 Assert.AreEqual(GetFirstErrorMessage(actual).Substring(0, 10), "Cannot del");

 db = new RecipesEntities();
 var deletedIngredient = db.Ingredients.Find(id);
 Assert.IsNotNull(deletedIngredient);
}

References:

Code First Entity Framework Unit Test Examples
Exercise 2: Testing CRUD actions
How to get all Errors from asp.net mvc modelState?
How to get the Model from an ActionResult? by . Also posted on my website

Tuesday, May 15, 2012

Learning MVC: Updating the Many-to-many Relationship with MultiSelectList

Continuing the application from the last post, I was now going to use a MultiSelectList to update the many-to-many relationship. The use case is the following: suppose we have a recipe but want to update it - maybe the dish will benefit from adding a bit of pepper. So how do I go about adding something to the list of ingredients? The solution was not so straightforward and there were at least three "gotchas" on the way.

Gotcha 1. To actually populate the MultiSelectList. The easy (but not quite straightforward) task was to create the MultiSelectList and to populate it with all the ingredients. To do that I added the properties to the Recipe class called AllIngredients and SelectedIngredientIDs. The most basic Recipe class now looks like this:

public class Recipe
{
 [ScaffoldColumn(false)]
 public int RecipeID { get; set; }
 public string RecipeName { get; set; }

 public virtual ICollection<Ingredient> Ingredients { get; set; }
 public IEnumerable<int> SelectedIngredientIDs { get; set; }
 public ICollection<Ingredient> AllIngredients { get; set; }

 public Recipe()
 {
  Ingredients = new HashSet<Ingredient>();
 }
}

In the controller, I populate all ingredients from the database into the AllIngredients and then the IDs of the ingredients of the recipe into the SelectedIngredientIDs.

Recipe recipe = recipeDB.Recipes.Find(id);
recipe.AllIngredients = recipeDB.Ingredients.ToList();

recipe.SelectedIngredientIDs = Enumerable.Empty<int>();
foreach (Ingredient ing in recipe.Ingredients)
{
 recipe.SelectedIngredientIDs = recipe.SelectedIngredientIDs.Concat(new[] {ing.IngredientID});
}

Then I create a MultiSelectList as follows

@Html.ListBoxFor(model => model.SelectedIngredientIDs, new MultiSelectList(Model.AllIngredients, "IngredientID", "IngredientName"), new {Multiple = "multiple"})

Gotcha 2. Preselect the current ingredients in the list. The application works by now and the list is populated, but nothing is selected. Why is that? I checked the SelectedIngredientIDs and they are populated properly. The trick was to find out that MVC uses the ToString method as a way to determine if an item is selected or not, so I had to override it in the Ingredient class. Just added a piece of code below and it started working like magic.

public class Ingredient
{
 public int IngredientID { get; set; }
 
 ...
 
 public override string ToString()
 {
  return this.IngredientID.ToString();
 }
}

Gotcha 3. Finally, I had my list being populated and I also could change the selection to my content. However, no exceptions were thrown but also no updates were being saved to the database. The not-so-little trick was to find out how exactly to let the Entity Framework know what needs to be updated. Here is the slightly simplified HttpPost method (try/catch omitted etc) which worked, with comments.

[HttpPost]
public ActionResult Edit(Recipe recipe)
{
 if(ModelState.IsValid)
 {
  //get the id of the current recipe
  int id = recipe.RecipeID;
  //load recipe with ingredients from the database
  var recipeItem = recipeDB.Recipes.Include(r => r.Ingredients).Single(r => r.RecipeID == id);
  //apply the values that have changed
  recipeDB.Entry(recipeItem).CurrentValues.SetValues(recipe);
  //clear the ingredients to let the framework know they have to be processed
  recipeItem.Ingredients.Clear();
  //now reload the ingredients again, but from the list of selected ones as per model provided by the view
  foreach (int ingId in recipe.SelectedIngredientIDs)
  {
   recipeItem.Ingredients.Add(recipeDB.Ingredients.Find(ingId));
  }
  //finally, save changes as usual
  recipeDB.SaveChanges();
  return RedirectToAction("Index");
 }
 return View(recipe);
}

I'm anticipating the gotchas in the Create view already!

References I used

ASP.NET MVC MultiSelectList with selected values not selecting properly
MVC 3 Quirk: MultiSelect with selected items
Many-To-Many Relationship Basic Example (MVC3) by . Also posted on my website

Sunday, May 13, 2012

Learning MVC - Code First and Many-to-many Relationship

Learning MVC I came across a need to create a many-to-many relationship between entities using the code first approach. For an example, let's consider the Recipes database which holds recipes and ingredients. A recipe has multiple ingredients, such as meat, potatoes, pepper and so on. At the same time an ingredient may belong to multiple recipes, i.e. you can cook meat with potatoes but fish and chips will also require potatoes. That's a classic many-to-many relationship which has a classic mapping table solution, where one many-to-many relationship would convert to two one-to-many by adding a new table. The diagram would look like the following:

Typical database solution for many-to-many relationship

So my first guess was that I will have to create three classes while implementing a similar structure with MVC code first. Fortunately, so far it appears to be easier than that. Below is the small exercise that creates a most basic MVC project from scratch and illustrates the many-to-many relationship via code first.

Start Visual Studio 2010 and select File -> New Project, select ASP.NET MVC 3 Web Application. On the next screen I selected Empty application to make the example most simple.

Create an empty MVC 3 application

After the project was created, I added a HomeController to create a basic home page. Probably not necessary, but lets the application run without displaying an error. Right-click Controllers folder, select Add -> Controller and add an empty HomeController.

Add a HomeController

Inside the HomeController.cs, the Index() method, right-click and select Add View. Leave default values and click Add.

Create an Index page for the project

Run the project to verify that no errors are displayed. Now it is the time to add entities for the Recipe and Ingredient. Right-click Models folder, select Add -> Class and call the class Recipe.cs. Add another one called Ingredient.cs. The little trick with a many-to-many relationship is to create an ICollection within each of the two related entities. The collection will actually hold those "many" entities that are related to the particular instance. In our case - the Recipe holds a list of all Ingredients it uses.

Add new class in Models folder

public class Recipe
{
 public int RecipeID { get; set; }
 public string Name { get; set; }

 public virtual ICollection<Ingredient> Ingredients { get; set; }

 public Recipe()
 {
  Ingredients = new HashSet<Ingredient>();
 }
}

public class Ingredient
{
 public int IngredientID { get; set; }
 public string Name { get; set; }

 public virtual ICollection<Recipe> Recipes { get; set; }

 public Ingredient()
 {
  Recipes = new HashSet<Recipe>();
 }
}

And we'll need to have some sample data to verify that the application is working as expected. So add another class and call it SampleData.cs. Entity Framework allows to "seed" the newly created database. An implementation of DropCreateDatabaseIfModelChanges class will re-seed the database when model changes. This is handy for testing.

public class SampleData : DropCreateDatabaseIfModelChanges<RecipesEntities>
{
 protected override void Seed(RecipesEntities context)
 {
  var ingredient0 = new Ingredient{Name = "Meat"};
  var ingredient1 = new Ingredient{Name = "Fish"};
  var ingredient2 = new Ingredient{Name = "Potato"};

  var ingredients = new List<Ingredient>(){ingredient0, ingredient1, ingredient2};

  ingredients.ForEach(i => context.Ingredients.Add(i));

  var recipes = new List<Recipe>();
  
  recipes.Add(new Recipe{Name = "Grilled fish with potatoes", Ingredients = new List<Ingredient>() {ingredient1, ingredient2}});
  recipes.Add(new Recipe{Name = "Grilled steak with potatoes", Ingredients = new List<Ingredient>() {ingredient0, ingredient2}});

  recipes.ForEach(r => context.Recipes.Add(r));
 }
}

Next step was to create the database. Right-click Project, select Add -> Add ASP.NET Folder -> App_Data. The folder will be created. It will already have the correct security access settings.

Create an App_Data folder

Next, I added the following at the end of the web.config file just before the closing "configuration" tag. Now the Entity Framework will know how to connect to the database:

<connectionStrings>
 <add name="RecipesEntities"
 connectionString="Data Source=|DataDirectory|Recipes.sdf"
 providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>

Next step is to create a context class, which will represent the Entity Framework database context. It is very simple indeed, and I'll create it by adding a new class called RecipeEntities.cs in the model folder. This class will be able to handle database operations due to the fact that it is extending DbContext. Here is the code:

public class RecipesEntities : DbContext
{
 public DbSet<Recipe> Recipes { get; set; }
 public DbSet<Ingredient> Ingredients { get; set; }
}

Now, with the model, database, context and some sample data in place, it is time to verify that data is actually displayed properly by the application. First, the recipes. I'm going to check that the list is displayed properly and that I can display all the details I'm interested in (for now, just the list of ingredients). For that, I'll need a RecipeController and two views, List and Details. First, the controller, that I will create with default methods for displaying, editing, creating and deleting data. I'm only interested in two methods, which I'll modify as follows

RecipesEntities recipeDB = new RecipesEntities();

// GET: /Recipe/
public ActionResult Index()
{
 var recipes = recipeDB.Recipes.ToList();
 return View(recipes);
}

// GET: /Recipe/Details/5
public ActionResult Details(int id)
{
 var recipe = recipeDB.Recipes.Find(id);
 return View(recipe);
}

Now I'll right-click within the Index method and select Add View, which I will configure as follows:

Create the List view

Now if I run the application as it is and navigate to Recipe (http://localhost/Recipe), I should see the list of recipes.

Display the list of ingredients

Next, I want to see the details. I'll add another view by right-clicking within the Details method of the controller and select Add View, which I will configure in the similar fashion:

Create the Details view

The default contents of the view will look similar to this:

@model RecipesSample.Models.Recipe
@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>
<fieldset>
    <legend>Recipe</legend>
    <div class="display-label">Name</div>
    <div class="display-field">@Model.Name</div>
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.RecipeID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

This, however, will only display the name of the recipe, but not the ingredients. To check that the ingredients are returned from the database properly, I have to modify the view to look similar to this:

@model Recipes.Models.Recipe
@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<fieldset>
    <legend>Recipe</legend>
    <div class="display-label">Name</div>
    <div class="display-field">@Model.Name</div>
</fieldset>

<fieldset>
    <legend>Ingredients</legend>
    @foreach (Recipes.Models.Ingredient ingredient in Model.Ingredients)
    { 
        <div>@Html.DisplayFor(model => ingredient.Name)</div>
    }
</fieldset>

<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.RecipeID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

Now if I run the application, navigate to Recipe and click Details on any of them (the link will point to http://localhost:49606/Recipe/Details/1 or similar), I will see the following page:

Display the related data

The recipe ingredients were successfully extracted from the model and displayed. As an exercise, it's easy to perform reverse action - check that if the ingredient is displayed, the recipes where it is used can also be shown. Hint: the view code may look similar to this:

@model Recipes.Models.Ingredient
@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<fieldset>
    <legend>Ingredient</legend>
    <div class="display-label">Name</div>
    <div class="display-field">@Model.Name</div>
</fieldset>

<fieldset>
    <legend>Is used in the following recipes</legend>
    @foreach (Recipes.Models.Recipe recipe in Model.Recipes)
    { 
        <div>@Html.DisplayFor(model => recipe.Name)</div>
    }
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.IngredientID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

And the output will be

Display the related data

This is a very crude example without following "best practices" (such as creating the DbContext inside the using statement) and without any formatting, but it shows that an application that employs the many-to-many relationships between the entities can be created with MVC with just several lines of code.

References I used:

Part 4: Models and Data Access
Creating a Many To Many Mapping Using Code First
The type or namespace name 'DbContext' could not be found
DropCreateDatabaseIfModelChanges Class by . Also posted on my website

Wednesday, May 2, 2012

Converting a Physical PC to VM with VMWare Converter

When the software I'm working on is installed on the PC which is later shipped to the client, the PC has an exact configuration and the operating system is set up in a certain way, which is precisely documented. To make sure every workstation has exactly the same configuration, the images of the hard disk partitions are created once and then copied over to every PC. There are cases, however, when the same configuration needs to be applied to the Virtual Machine - for example, to simplify some of the testing tasks. In this case the desired action is to use the preconfigured PC and to convert it to the Virtual Machine. Since we use the VMWare products, the tool that I use also comes from VMWare and it's called VMware vCenter Converter (aka vConverter Standalone).

To start with, I downloaded and installed VMware vCenter Converter (aka vConverter Standalone). In my case, Local installation was sufficient. In local mode I can only create and manage conversion tasks from the PC on which I install the converter. The client-server installation allows to create and manage conversion tasks remotely in case you need that.

Choose "Local Installation".

Two important configurations have to be set up on the source PC (the PC that is converted to VM) before the conversion starts. First, file sharing has to be disabled.

Turn off file sharing

Also, the Windows firewall has to be disabled.

Disable Windows Firewall

You should also check some other things, such as the Windows version being supported, network access, no other conversion jobs running on the source machine and no VMware Converter installations existing on the source PC. After all that is taken care of, I ran the client application. In the application, I selected Convert machine and filled in the details.

Source PC details

When the connection succeeded, I saw the following message. Just to make sure that I will not forget to remove the agent and to free myself from extra hassle, I chose the automatic version.

Select the uninstallation method for Converter Standalone

Next step was to provide the location where the virtual machine will be saved.

Destination System

Next step was to select what I wanted to copy. I wanted to copy the whole machine, with one small exclusion: there was a disk drive that only contained some backup data and was irrelevant. So it was possible to save some time and disk space this way. I unchecked the drive I was not interested in. If you are an advanced user, you can apply further configuration changes.

Conversion task options

Uncheck the hard disk

Next step was to review the details and press Finish. Everything went well and a job was be added to the list, displaying some details and estimated time to completion. Now all I needed was some patience.

Conversion task

When the conversion task was complete, I opened the newly created VM.

Open Virtual Machine

I got a warning message, which, I assumed, was related to the fact that VMWare tools were not yet installed. After I installed the tools later, I never saw the warning again.

Warning message

Finally the VMWare tools improve graphic performance on the guest PC. So the last step was to install them.

Install VMWare tools

In my case, a message appeared and I did exactly as it advised - on the VM, logged in and ran E:\setup.exe from the command prompt. The installation started and I followed the prompts until VMWare tools were installed.

VMWare message

by . Also posted on my website