Answer by Fabrizio Moscon:

This isn't a trivial question so I think it deserves an articulate answer. Few points have been already expressed in other answers but I think listing all of them can help clarity:

1) fast and easy to prototype: makes extremely easy to prototype (almost) any kind of web application in a shorter time when compared with others like for example JAVA and LAMP. Just to mention some of the savings in time: node.js provides the web server module itself, so you don't have to own knowledge about setting up Apache or NGINX unless you want to integrate in the existing platform or you have a complex system architecture, and the very active node.js community provides ready-to-use modules and deep knowledge that can save you time when it comes to develop commodity features (Facebook-Connect, RESTful API, templating)
2) npm: is THE node package manager, it represents the standard way to share code with the community and to manage external dependencies. It is only 2 years old and the number of module present has (almost) overcome the number of module published on RoR gems. Using community modules like express, socket.io, mongoose, mocha, everyauth – just to mention few of them – provide you with a powerful toolbox to build a running application "LAMP equivalent" within a matter of hours. It manages all the nested dependencies for you, so you can concentrate to what is your CORE application logic rather than spending time developing commodity features which the community is maintaining in a collaborative and effective way for you. The easy configuration file syntax let you also fix a module to a specific version so you don't have to worry about future updates breaking your software.
3) cloud platform: there are a number of Platform as a service solution like Joyent cloud, heroku, nodejitsu which identify node as first class technology and make deploy and scale of your application trivial. Other similar services like Pagoda let you develop PHP+MySQL application easily as well, but at the moment there is more choice and better support for node technologies in the cloud IMHO.
4) mongoDB and Redis: usually a node.js project comes hand in hand with a NoSQL solution. MongoDB and Redis are complementary technologies that are very well integrated with node.js. If you are doing a project without a relational database, node.js is definitely a good candidate.
5) javascript and coffescript: the fact that Javascript is the language of the browser made it necessary for all web developers to deal with it. This means that almost every developer can read Javascript, and I find more and more people capable of writing good, solid, robust client JavaScript applications. Having node.js in the backend brings other kind of problems like layered architecture, Domain Model Design, MVC and correct use of design patterns which developers are typically used to solve using other languages like PHP, JAVA, Python, RoR, but coffescript constitutes a valid bridging gap translator language between the clssical OO inheritance and prototypal ones like JavaScript, helping not savvy new re-discovered node.js developers to avoid silly mistakes and filtering out the bad part of JavaScript
6) architecture: node.js architecture helps you overcome performance and scalability problems thanks to its non-blocking interaction. Metaphorically someone at the Nodestack on-line conference compared Apache as a pizza over when you have to wait for the existing pizzas to be cooked before putting more in and node.js to a juggler which let requests to be handled in the air while is serving the new coming. This particular architecture that similarly to NGINX leverage directly the Operating System event system calls opens up the development of all those soft real-time application which couldn't be developed easily using other languages. 
7) costs: thanks to its high performance you can avoid costs for extra hardware. I manage to reach 5000 response per second on my laptop (measuring with ab -n 100000 -c an express application). Theoretically you also save in system administration time (unless you have a complex architecture), since there is no need to bridge HTTP or TCP requests to another server side language. So you avoid Apache or NGINX setup and maintenance costs.

There are still a branch of web applications that I would not advise being developed using node.js, although the community is growing so quickly that I wouldn't be surprise to see very soon node.js used at the centre of complex transactional systems. Most probably when you need a sandbox environment where separation of memory space for each requests is critical for the business and you can't afford a situation where a single exception takes down the server node.js is suboptimal.

Why choose Node.js for web applications?

Recently I wanted to find out how github works technically, with the intention of uploading a small personal project. After googling I found a presentation titled exactly that – How GitHub works. However it turns out that it was a presentation given by one of their employees about how the company itself works – how they communicate, how they do planning, development, product releases, etc. I found this very interesting and watched the whole things which is 55 minutes long. Here’s the presentation and a summary I’ve made:

http://www.infoq.com/presentations/How-GitHub-Works

– starts with company structure, ethos, hierarchy (no managers), flexible hours, team setup and size, etc
– @2m Working 9 to 5 doesn’t suit every company, best solutions happen in the zone
– @4m Embrace flexibility, let people work when they want and when they’re most productive
– @7m Trust the people you hire
– @8m Asynchronous workflow
– @10m Limit in person contact, use chat even in same room – lets others into the conversation;
– @12m Weekly tech talks, recorded and uploaded to internal server for future usage for new employees
– @13m Have face-time, once or twice per year get team together for day out / meal / drinks
– @14m Minimize distractions – the zone is difficult to re-enter
– @15m No technical meetings, no stand-up, daily, or planning meetings. Use chat to communicate asynchronously
– @16m 60 employees, no managers
– @19m Minimal process – plan it, build it, ship it
– @22m Use simple branching: master => branch => pull request => merge; master always ready to deploy
– @23m Deploy 5 to 30 times per day
– @23m Pull requests replace traditional code reviews;
– @25m Count number of assertions, currently 8000 taking 250 seconds
– @26m A slow test is a regression
– @28m Deploy => check exceptions => check performance => merge into master (if applicable) => celebrate!
– @32m Foster exploration, shared side projects
– @34m Continuous learning
– @35m Avoid burnout by encouraging new things

http://zachholman.com/

Source now available on github:

https://github.com/cbruen1/mvc4-many-to-many

So finally we are at the third part of our MVC EF many to many trilogy. Part 1 was the initial setup of the solution where we could add new users without any courses. In part 2 I then showed how to add courses and save one or more courses as part of a user profile. In this part I’ll show how to edit a user and change their name and the courses they’ve been registered for, display a user’s details in read only mode, and delete a user from the app.

The first thing we have to do for this is add 2 new controller actions for Edit, one Get and one Post (the Get retrieves the UserProfile for editing and the Post posts it back for saving to the db). Paste the following code into the UserProfileController file after the Create actions (created in part 1 and present in the code solution):

public ActionResult Edit(int id = 0)
{
    // Get all courses
    var allDbCourses = db.Courses.ToList();

    // Get the user we are editing and include the courses already subscribed to
    var userProfile = db.UserProfiles.Include("Courses").FirstOrDefault(x => x.UserProfileID == id);
    var userProfileViewModel = userProfile.ToViewModel(allDbCourses);

    return View(userProfileViewModel);
}

[HttpPost]
public ActionResult Edit(UserProfileViewModel userProfileViewModel)
{
    if (ModelState.IsValid)
    {
        var originalUserProfile = db.UserProfiles.Find(userProfileViewModel.UserProfileID);
        AddOrUpdateCourses(originalUserProfile, userProfileViewModel.Courses);
        db.Entry(originalUserProfile).CurrentValues.SetValues(userProfileViewModel);
        db.SaveChanges();

        return RedirectToAction("Index");
    }

    return View(userProfileViewModel);
}

Next we need to update our existing AddOrUpdateCourses method. When saving new courses for an existing user we first delete the already saved ones and add any newly selected ones:


private void AddOrUpdateCourses(UserProfile userProfile, IEnumerable<AssignedCourseData> assignedCourses)
{
    if (assignedCourses == null) return;

    if (userProfile.UserProfileID != 0)
    {
        // Existing user - drop existing courses and add the new ones if any
        foreach (var course in userProfile.Courses.ToList())
        {
            userProfile.Courses.Remove(course);
        }

        foreach (var course in assignedCourses.Where(c => c.Assigned))
        {
            userProfile.Courses.Add(db.Courses.Find(course.CourseID));
        }
    }
    else
    {
        // New user
        foreach (var assignedCourse in assignedCourses.Where(c => c.Assigned))
        {
            var course = new Course { CourseID = assignedCourse.CourseID };
            db.Courses.Attach(course);
            userProfile.Courses.Add(course);
        }
    }
}

We also update our UserProfileViewModel class in our Models folder to initialise the Courses collection in the constructor:


public class UserProfileViewModel
{
    public UserProfileViewModel()
    {
        Courses = new Collection<AssignedCourseData>();
    }

    public int UserProfileID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AssignedCourseData> Courses { get; set; }
}

Next we update our ViewModelHelper class to allow us add and update courses for a user:


public static class ViewModelHelpers
{
    public static UserProfileViewModel ToViewModel(this UserProfile userProfile)
    {
        var userProfileViewModel = new UserProfileViewModel
        {
            Name = userProfile.Name,
            UserProfileID = userProfile.UserProfileID
        };

        foreach (var course in userProfile.Courses)
        {
            userProfileViewModel.Courses.Add(new AssignedCourseData
            {
                CourseID = course.CourseID,
                CourseDescription = course.CourseDescripcion,
                Assigned = true
            });
        }

        return userProfileViewModel;
    }

    public static UserProfileViewModel ToViewModel(this UserProfile userProfile, ICollection<Course> allDbCourses)
    {
        var userProfileViewModel = new UserProfileViewModel
        {
            Name = userProfile.Name,
            UserProfileID = userProfile.UserProfileID
        };

        // Collection for full list of courses with user's already assigned courses included
        ICollection<AssignedCourseData> allCourses = new List<AssignedCourseData>();

        foreach (var c in allDbCourses)
        {
            // Create new AssignedCourseData for each course and set Assigned = true if user already has course
            var assignedCourse = new AssignedCourseData
            {
                CourseID = c.CourseID,
                CourseDescription = c.CourseDescripcion,
                Assigned = userProfile.Courses.FirstOrDefault(x => x.CourseID == c.CourseID) != null
            };

            allCourses.Add(assignedCourse);
        }

        userProfileViewModel.Courses = allCourses;

        return userProfileViewModel;
    }

    public static UserProfile ToDomainModel(this UserProfileViewModel userProfileViewModel)
    {
        var userProfile = new UserProfile
        {
            Name = userProfileViewModel.Name,
            UserProfileID = userProfileViewModel.UserProfileID
        };

        return userProfile;
    }
}

Next we add the Edit view that allows us to edit each user and change their name and assigned courses. In the 2nd Edit controller action created previously right click and select “Add View”:

– Name the View “Edit”
– Select the Razor View engine
– Click “Create strongly typed view” and select the UserProfileViewModel class as the Model class
– In the scaffold template select “Edit”. Check the “Use a layout or master page” check box

In the resulting Edit.cshtml change the line “@Html.EditorFor(model => model.UserProfileID)” to “@Html.DisplayFor(model => model.UserProfileID)”. This is the unique id field and we don’t want to change this, only display it. We also need to add the lines “@Html.HiddenFor(x => x.UserProfileID)” which makes sure our UserProfileID is posted back, and “@Html.EditorFor(x => x.Courses)” which renders the courses for a user. Here’s the full Edit.cshtml view:


@model MVC4ManyToMany.Models.ViewModels.UserProfileViewModel
@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm("Edit", "UserProfile", new { id="formEdit", name="formEdit" })) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>UserProfileViewModel</legend>

        @Html.HiddenFor(x => x.UserProfileID)

        <div>
            @Html.LabelFor(model => model.UserProfileID)
        </div>
        <div>
            @Html.DisplayFor(model => model.UserProfileID)
        </div>
        <div>
            @Html.LabelFor(model => model.Name)
        </div>
        <div>
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        @* Render the check boxes using the Editor Template *@
        @Html.EditorFor(x => x.Courses)

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

We also change our UserProfile\Index.cshtml view to pass the correct id’s to the controller – update the following lines:


@Html.ActionLink("Edit", "Edit", new { id=item.UserProfileID }) |
@Html.ActionLink("Details", "Details", new { id=item.UserProfileID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.UserProfileID })

Now we have a view that will allow us to edit users after initial creation. Run the application which takes us to the main Index page and click on the link to the UserProfile Index page (if you get a timeout error the first time click refresh). If there are already users present click on any Edit link which will take us to the Edit page that we added earlier (otherwise create a user first):

MVC4Edit1

After selecting different courses and saving we can see that the courses were updated when we edit the user again:

MVC4Edit2

So far so good. To complete our functionality we need to add Details and Delete views and actions. In our UserProfileController add a new controller action called Details:


public ActionResult Details(int id = 0)
{
    // Get all courses
    var allDbCourses = db.Courses.ToList();

    // Get the user we are editing and include the courses already subscribed to
    var userProfile = db.UserProfiles.Include("Courses").FirstOrDefault(x => x.UserProfileID == id);
    var userProfileViewModel = userProfile.ToViewModel(allDbCourses);

    return View(userProfileViewModel);
}

In the Details controller action right click and select “Add View”:

– Name the View “Details”
– Select the Razor View engine
– Click “Create strongly typed view” and select the UserProfileViewModel class as the Model class
– In the scaffold template select “Details”. Check the “Use a layout or master page” check box

Update the Details.cshtml view with the following code:


@model MVC4ManyToMany.Models.ViewModels.UserProfileViewModel
@{
    ViewBag.Title = "Details";
}
<h2>Details</h2>

<fieldset>
    <legend>UserProfileViewModel</legend>

    <div>
        @Html.DisplayForModel(Model)

        <p></p>

        @foreach (var course in Model.Courses)
        {
            <div>
                <span>@Html.CheckBox("Blah", course.Assigned, new { disabled = "disabled" })</span>&nbsp;
                <span>@course.CourseDescription</span>
            </div>
        }
    </div>
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.UserProfileID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

After compiling refresh the app and click on the Details link of any user in our Index view, which will display a read only view of the user details and the courses they are subscribed to.

Finally the Delete view – this requires a little bit more work on the controller but nothing too hairy. In our UserProfileController add a new controller action called Delete:


public ActionResult Delete(int id = 0)
{
    var userProfileIQueryable = from u in db.UserProfiles.Include("Courses")
        where u.UserProfileID == id
        select u;

    if (!userProfileIQueryable.Any())
    {
        return HttpNotFound("User not found.");
    }

    var userProfile = userProfileIQueryable.First();
    var userProfileViewModel = userProfile.ToViewModel();

    return View(userProfileViewModel);
}

This is the initial Get action that retrieves the user we’re deleting and displays the details in a Details view (that we will add last). We need another controller action to handle the deletion when the user clicks the Delete button on our view, so add the following code:


[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
    var userProfile = db.UserProfiles.Include("Courses").Single(u => u.UserProfileID == id);
    DeleteUserProfile(userProfile);

    return RedirectToAction("Index");
}

private void DeleteUserProfile(UserProfile userProfile)
{
    if (userProfile.Courses != null)
    {
        foreach (var course in userProfile.Courses.ToList())
        {
            userProfile.Courses.Remove(course);
        }
    }

    db.UserProfiles.Remove(userProfile);
    db.SaveChanges();
}

Next right click in the Delete action and select “Add View”, as we’ve previously done for Edit:

– Name the View “Delete”
– Select the Razor View engine
– Click “Create strongly typed view” and select the UserProfileViewModel class as the Model class
– In the scaffold template select “Delete”. Check the “Use a layout or master page” check box

The resulting Razor code should be fine as is, so when we re-compile and re-load the Index page we should have a link to our Delete page.

So with the addition of edit and delete functionality that gives us a fully working CRUD (CReate /Update / Delete) application. To recap again on what we’ve achieved with these 3 posts:

– Multi tier MVC4 project utilising Entity Framework 5.
– Creating a many to many table structure in our database using code first
– Adding child collections to our objects that lets Users have multiple Courses and Courses have multiple Users
– Using an Editor Template to render child collections and naming the fields in the correct format for the MVC model binder

As mentioned in a previous post this isn’t a fully fledged enterprise level application but is a good foundation for a CRUD application. To make the code more robust and complete and have it closer to industry standards we would add unit testing, dependency injection / IoC, error handling, logging, etc.

EDIT 1

To display the courses for each user in the Index.cshtml view update the foreach loop like this:

@foreach (var item in Model) {
  <tr>
    <td>
        @Html.DisplayFor(modelItem => item.UserProfileID)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Name)
    </td>
    <td>
        <ul>
        @foreach (var course in item.Courses)
        {
            <li>@course.CourseDescription</li>
        }
        </ul>
    </td>
    <td>
        @Html.ActionLink("Edit", "Edit", new { id=item.UserProfileID }) |
        @Html.ActionLink("Details", "Details", new { id=item.UserProfileID }) |
        @Html.ActionLink("Delete", "Delete", new { id=item.UserProfileID })
    </td>
  </tr>
}

Edit 2

To only remove existing courses that have been unchecked and add new ones (instead of deleting all courses each time) we can do this:

private void AddOrUpdateKeepExistingCourses(UserProfile userProfile, IEnumerable<AssignedCourseData> assignedCourses)
{
    var webCourseAssignedIDs = assignedCourses.Where(c => c.Assigned).Select(webCourse => webCourse.CourseID);
    var dbCourseIDs = userProfile.Courses.Select(dbCourse => dbCourse.CourseID);
    var courseIDs = dbCourseIDs as int[] ?? dbCourseIDs.ToArray();
    var coursesToDeleteIDs = courseIDs.Where(id => !webCourseAssignedIDs.Contains(id)).ToList();

    // Delete removed courses
    foreach (var id in coursesToDeleteIDs)
    {
        userProfile.Courses.Remove(db.Courses.Find(id));
    }

    // Add courses that user doesn't already have
    foreach (var id in webCourseAssignedIDs)
    {
        if (!courseIDs.Contains(id))
        {
            userProfile.Courses.Add(db.Courses.Find(id));
        }
    }
}

Source now available on github:

https://github.com/cbruen1/mvc4-many-to-many

So for part 2 of our saving many to many data we’re going to continue where we left off in part 1. I’ll show how to add courses to the system, how to list all available courses when creating a new user, and how to save one or more courses as part of a user’s profile.
First off in the MVC4ManyToManyContext class we override the OnModelCreating method. This is how we map the many to many relationship between UserProfile and Course:


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<UserProfile>()
    .HasMany(up => up.Courses)
    .WithMany(course => course.UserProfiles)
    .Map(mc =>
    {
        mc.ToTable("T_UserProfile_Course");
        mc.MapLeftKey("UserProfileID");
        mc.MapRightKey("CourseID");
    });

    base.OnModelCreating(modelBuilder);
}

Add a class called MockInitializer to the db project that will give us some courses to start with and means we don’t have to manually add them every time our db model changes:


using System.Data.Entity;
using MVC4ManyToManyDomain;
//...

public class MockInitializer : DropCreateDatabaseIfModelChanges<MVC4ManyToManyContext>
{
    // Note: you can also use DropCreateDatabaseAlways to force a re-creation of your database
    protected override void Seed(MVC4ManyToManyContext context)
    {
        base.Seed(context);

        var course1 = new Course { CourseID = 1, CourseDescripcion = "Bird Watching" };
        var course2 = new Course { CourseID = 2, CourseDescripcion = "Basket weaving for beginners" };
        var course3 = new Course { CourseID = 3, CourseDescripcion = "Photography 101" };

        context.Courses.Add(course1);
        context.Courses.Add(course2);
        context.Courses.Add(course3);
    }
}

Add this line to Application_Start() in Global.asax to kick start the initializer, also add the necessary using statements:

using MVC4ManyToManyDatabase;
using System.Data.Entity;
//...

Database.SetInitializer(new MockInitializer());

Next, to populate the course data and the Courses object of the UserProfileViewModel, add a private method to the controller called PopulateCourseData:


private ICollection<AssignedCourseData> PopulateCourseData()
{
    var courses = db.Courses;
    var assignedCourses = new List<AssignedCourseData>();

    foreach (var item in courses)
    {
        assignedCourses.Add(new AssignedCourseData
        {
            CourseID = item.CourseID,
            CourseDescription = item.CourseDescripcion,
            Assigned = false
        });
    }

    return assignedCourses;
}

Then update the Create action of the controller (the first one not the one decorated with HttpPost):


public ActionResult Create()
{
var userProfileViewModel = new UserProfileViewModel { Courses = PopulateCourseData() };

return View(userProfileViewModel);
}

Next we create an Editor Template – Editor Templates are very useful and are especially suited for rendering collections of objects and naming the fields correctly for the MVC model binder. I have a write up about them here:

https://codenodes.wordpress.com/2012/04/27/mvc-3-editor-templates/

Editor Templates are by convention named the same as the object model they operate on (it’s also possible to name them differently to their model but let’s stick with convention). In your Views\Shared folder create a new folder called EditorTemplates if you don’t already have one. Add a new partial view called AssignedCourseData and paste the code below. This is the bit of magic that renders and names all your check boxes correctly – you don’t need a for each loop as the Editor Template will create all the items passed in a collection:


@model AssignedCourseData
@using MVC4ManyToMany.Models.ViewModels;

<fieldset>
    @Html.HiddenFor(model => model.CourseID)
    @Html.CheckBoxFor(model => model.Assigned)
    @Html.DisplayFor(model => model.CourseDescription)
</fieldset>

Add the following line to the UserProfile\Create.cshtml view just before the submit button:


@* Render the check boxes using the Editor Template *@
@Html.EditorFor(x => x.Courses)

This will render the field names correctly so they are part of the UserProfileViewModel when the form is posted back to the Controller. When rendered if you view source you will see that the fields have been named as such:


<fieldset>
    <input data-val="true" data-val-number="The field CourseID must be a number." data-val-required="The CourseID field is required." id="Courses_0__CourseID" name="Courses[0].CourseID" type="hidden" value="1" />
    <input data-val="true" data-val-required="The Assigned field is required." id="Courses_0__Assigned" name="Courses[0].Assigned" type="checkbox" value="true" /><input name="Courses[0].Assigned" type="hidden" value="false" />
    Bird Watching
</fieldset>

The fields for the other courses will be named similarly except will be indexed with 1, 2 etc. So here’s our Create form with a Name field and the available courses rendered as check boxes:

Create view

Create view

Once the form is posted back our AddOrUpdateCourses method gets called into action. This was already present but wasn’t utilised because we didn’t have any courses. Once the courses are part of the user profile EF takes care of the associations. It will add a record for each course selected to the T_UserProfile_Course table created in OnModelCreating. Here’s the Create action result method showing the courses posted back :

Courses posted back

Courses posted back

I selected 2 courses and you can see that the courses have been added to the new user profile object, the name is what we entered in the Name text box, and the UserProfileID is 0 meaning that EF will create a new record for us:

UserProfile object with courses attached

UserProfile object with courses attached

Here’s the records after saving to the DB:

UserProfiles table

UserProfiles table

T_UserProfile_Course table

T_UserProfile_Course table

So that ends part 2 of saving many to many records in MVC with Entity Framework. What we’ve seen:

– Multi tier MVC4 project utilising Entity Framework.
– Creating a many to many table structure in our database using code first
– Adding child collections to our objects that lets Users have multiple Courses and Courses have multiple Users
– Using an Editor Template to render child collections and naming the fields in the correct format for the MVC model binder

 

Source now available on github:

https://github.com/cbruen1/mvc4-many-to-many

This post stems from an answer I gave to a question on stackoverflow.com about saving many to many relationship data in MVC using Entity Framework 4.1 code first. The scenario is an application that creates a user and allows that user to register for one or more courses, with the courses presented as a list of check boxes. A user is able to register for many courses and a course can have many users which is where the many to many scenario comes in. The difficulty the original poster had was rendering and naming the check boxes correctly and getting them posted back in the Create view.

In this first part I will set up the overall solution and get it to a state where it will add users to the system without any courses. In part 2 I will add the necessary updates to allow one or more courses to be saved as part of a user’s profile. In part 3 I’ll show how to edit a user and change their name and the courses they’ve been registered for, display a user’s details in read only mode, and delete a user from the app.

For the project I used Visual Studio 2012 but if you don’t have this you can download the express version from here:

http://www.asp.net/mvc

or

http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products

I created a new solution containing 3 individual projects – web, domain model, and database. I used the intranet template for the web app – by default this will add a Home Controller and Home folder within the Views folder with Index, About, and Contact views. Complete the following steps:

  • Open visual Studio and create a new ASP.NET MVC4 intranet application project called MVC4ManyToMany
  • Right click the solution and add a new Class Library project called MVC4ManyToManyDomain
  • Right click the solution again and add another Class Library project called MVC4ManyToManyDatabase

In the MVC4ManyToManyDomain project the classes involved are a UserProfile class and a Course class. Delete the Class1 class and add 2 classes called Course and UserProfile.


public class Course
{
    public int CourseID { get; set; }
    public string CourseDescripcion { get; set; }
    public virtual ICollection<UserProfile> UserProfiles { get; set; }
}

public class UserProfile
{
    public UserProfile()
    {
        Courses = new List<Course>();
    }
    public int UserProfileID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Course> Courses { get; set; }
}

To set up the database and the EF DbContext delete the default Class1 class from the MVC4ManyToManyDatabase project and add a class called MVC4ManyToManyContext. You will need to reference EntityFramework and the MVC4ManyToManyDomain project and add using directives for both. To do this right click References and add MVC4ManyToManyDomain from the Solution section and Browse to {Your solution directory}\MVC4ManyToMany\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll.


using System.Data.Entity;
using MVC4ManyToManyDomain;

namespace MVC4ManyToManyDatabase
{
    public class MVC4ManyToManyContext : DbContext
    {
        public DbSet<UserProfile> UserProfiles { get; set; }
        public DbSet<Course> Courses { get; set; }
    }
}

By default, the Entity Framework looks for a connection string named the same as the object context class. It automatically creates a SQL Server Express database in the App_Data folder using the same name as the DB Context class.

Next in the main web project add a folder called ViewModels to the Model folder. In this folder add 3 classes – UserProfileViewModel, CourseViewModel, and AssignedCourseData.


public class UserProfileViewModel
{
    public int UserProfileID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AssignedCourseData> Courses { get; set; }
}

public class CourseViewModel
{
    public int CourseID { get; set; }
    public string CourseDescripcion { get; set; }
    public virtual ICollection<UserProfileViewModel> UserProfiles { get; set; }
}

public class AssignedCourseData
{
    public int CourseID { get; set; }
    public string CourseDescription { get; set; }
    public bool Assigned { get; set; }
}

To help us transform our domain model to view model and vice versa we can create a little helper. In the web project’s Model\ViewModel folder add a static class called ViewModelHelpers (you’ll need to add “using MVC4ManyToManyDomain;”):


using MVC4ManyToManyDomain;
//...

public static class ViewModelHelpers
{
    public static UserProfileViewModel ToViewModel(this UserProfile userProfile)
    {
        var userProfileViewModel = new UserProfileViewModel
        {
            Name = userProfile.Name,
            UserProfileID = userProfile.UserProfileID
        };

        return userProfileViewModel;
    }

    public static UserProfile ToDomainModel(this UserProfileViewModel userProfileViewModel)
    {
        var userProfile = new UserProfile();
        userProfile.Name = userProfile.Name;
        userProfile.UserProfileID = userProfile.UserProfileID;

        return userProfile;
    }
}

Next, right click the Controllers folder in the web project and select Add Controller. In the dialog box name it UserProfileController and Select Empty MVC Controller from the Template drop down.
Add a reference to the MVC4ManyToManyDomain project and add these using statement to the top of the page:


using MVC4ManyToMany.Models.ViewModels;
using MVC4ManyToManyDatabase;
using MVC4ManyToManyDomain;

In the Controller paste the folowing code:


public class UserProfileController : Controller
{
    private readonly MVC4ManyToManyContext db = new MVC4ManyToManyContext();

    public ActionResult Index()
    {
        var userProfiles = db.UserProfiles.ToList();
        var userProfileViewModels = userProfiles.Select(userProfile => userProfile.ToViewModel()).ToList();

        return View(userProfileViewModels);
    }

    public ActionResult Create()
    {
        var userProfileViewModel = new UserProfileViewModel { };

        return View(userProfileViewModel);
    }

    [HttpPost]
    public ActionResult Create(UserProfileViewModel userProfileViewModel)
    {
        if (ModelState.IsValid)
        {
            var userProfile = new UserProfile { Name = userProfileViewModel.Name };

            AddOrUpdateCourses(userProfile, userProfileViewModel.Courses);
            db.UserProfiles.Add(userProfile);
            db.SaveChanges();

            return RedirectToAction("Index");
        }

        return View(userProfileViewModel);
    }

    private void AddOrUpdateCourses(UserProfile userProfile, IEnumerable<AssignedCourseData> assignedCourses)
    {
        if (assignedCourses != null)
        {
            foreach (var assignedCourse in assignedCourses)
            {
                if (assignedCourse.Assigned)
                {
                    var course = new Course { CourseID = assignedCourse.CourseID };
                    db.Courses.Attach(course);
                    userProfile.Courses.Add(course);
                }
            }
        }
    }
}

Next we will add a link to the Create user profile View from our main Index view. In the Home\Index.cshtml file add the following code:


<p>
@Html.ActionLink("User Profile Page", "Index", "UserProfile")
</p>

Next we need to add 2 Views for our user profile, Index and Create. In the Views folder of the web project right click and select Add folder. Name the folder “UserProfile”. In the Index action of the UserProfileController right click and select “Add View”:

– Name the View “Index”
– Select the Razor View engine
– Click “Create strongly typed view” and select the UserProfileViewModel class as the Model class
– In the scaffold template select “List”. Check the “Use a layout or master page” check box

This adds a view that will display all the users in our system and also adds a link to our Create view which we will add next. In the Create action (NOT the one decorated with the “HttpPost” attribute) of the UserProfileController right click and select “Add View”:

– Name the View “Create”
– Select the Razor View engine
– Click “Create strongly typed view” and select the UserProfileViewModel class as the Model class
– In the scaffold template select “Create”. Check the “Use a layout or master page” check box
– Open the Create.cshtml view and delete the fields that refer to UserProfileID if present

Now we have a view that will allow us to add a user to the system. Run the application which takes us to the main Index page. Click on the link to the Create user profile page that we added earlier. We are now able to add users to the system.  All we have is one field for a name but it demonstrates the concept and you’re free to add other fields to the model as you see fit.

That’s it for the first part. This is a pretty basic and simplified example to get up and running with this scenario. In the real world we would ideally do this using Test Driven Development (TDD) and have a test project in the solution, and also add error handling, dependency injection, and other such stuff to make it more robust and testable.

In part 2  I’ll show how to add courses to the system, how to list all the courses when creating a new user, and how to save one or more courses as part of a user’s profile.

 

Here’s how I set up a Continuous Integration server using IIS7 and Web Deploy 2.0 for deploying an application remotely. Our technology stack is Asp.net MVC4, jQuery, knockout, Entity Framework 4.1 code first, and Oracle. On your IIS server do the following:

– Create new folders C:\[YourAppName] and C:\logs\[YourAppName] (for logging)
– Create local Windows User “MyUser” and grant him full control on directory C:\[YourAppName]
– Add “Management Service” Role Service (Right click Add role on Web server (IIS) in server manager)
– Add all “Common HTTP Features”
– Add all Application Development Features EXCEPT “CGI” and “Server Side Includes”

– Add Basic, Windows and Digest Authentication below Security
– Add all Management Tools Features
– Install Web Deploy 2.0 (WebDeploy_2_10_amd64_en-US.msi – Use Custom installation – select all by hand, complete has not all features enabled). Link:
http://www.microsoft.com/en-us/download/details.aspx?id=25230
– Create new website in IIS with name [YourAppName] and physical path C:\[YourAppName] – delete default website
– Configure the Default Web Site’s Web-Deployment permission list
– Right click Website and choose Deploy – Configure Web Deploy Publishing (in case that Deploy is not visible you need a server restart)
– Select local “MyUser” user
– Copy URL for the publishing server connection for later use
– Set .NET Framework version on the newly created Application Pool [YourAppName] to v4.0 (in Web Site Advance Settings) and Classic mode
– Restart server
– In the IIS Management on IIS level in “Management service delegation” add two new rules (Template “Deploy applications with Content”)
– Provider: contentPath, iisApp; Path: C:\[YourAppName]; Identity Type: Current user
– Provider: setAcl; Path: C:\[YourAppName]; Identity Type: Current user
– Enable Windows Authentication in the [YourAppName] Authentication Settings.
– Activate Remote Access in “Management Service” in IIS Management Console (IIS level)
– Active .NET 64 Bit in IIS => c:/windows/microsoft.net/.net 4.x/aspnet_regiis –i
– Allow ISAPI/CGI use for the .NET framework in IIs (ISAPI & CGI restrictions)
– Set the Web Management Service to “auto” in the Services management console
– Create c:/logs/[YourAppName] directory and give write permission to IIS account

Recently I had a scenario where I needed to pass a value to a web service when a user selected an option in a drop down list. I had the drop down in a Razor view and when an item was selected an associated value needed to be passed to a web service as a parameter. However the value to be passed was not the id or value in the drop down but a different value associated with the selected item. So I needed a way to store this associated value in the drop down along with each displayed item.

In the database each item had 3 values associated with it – the database id, short name (for display), and long name (for passing to a service). The drop down needed to show the short name, so I populated the drop down with the database id as the value and the short name as the text.

As an example the drop down had values such as Cortina, Boxster, and Viper. When Cortina is seleted “Ford Cortina 1979 Blue” needed to be passed to the service. The solution I came up with was to store the long name as a data dash attribute in each item in the drop down. So here’s the data:

CARID SHORT_NAME LONG_NAME
1     Viper     Dodge Viper 1982
2     Boxster   Porsche Boxster 2009 Black
3     Cortina   Ford Cortina 1979 Blue

I added a JsonResult method on the Controller to return an anonymous object with the 3 properties that I wanted:


public class VehicleController : Controller
{
    // etc.
    public JsonResult GetSubTypesForTypeType(string typeTypeName)
    {
        var cars = repository.GetTypeWithSubTypes(typeTypeName);

        return cars == null
        ? Json(new object[0], JsonRequestBehavior.AllowGet)
        : Json(cars.SubTypes.OrderBy(s => s.Name).Select(
            s => new { s.SubTypeID, s.Name, s.Description }).ToArray(),
            JsonRequestBehavior.AllowGet);
    }
    // etc.
}

Here’s the code I use to create the drop down:

    <div class="editor-field">
        @Html.DropDownListFor(model => model.CarID,
            new SelectList(ViewBag.Cars, "Value", "Text", "1"))
        @Html.ValidationMessageFor(model => model.CarShortName)
    </div>

Then in js:

Populate the drop down:


// populate the cars drop down when the select list is available
if ($('select#SubTypeID').length) {
    var carsSelect = $('select#SubTypeID');
    var carsList = populateCarsList("CARS");
    var carsListHtml = createCarsSelectList(carsList);
    carsSelect.html('');
    carsSelect.append(carsListHtml);
    $('#SubTypeID').change(function (e) {
        clearFormData();
    });
}

Call a function to get the subtypes (cars) via an ajax call:

function populateCarsList(typeTypeName) {
    var carsList;
    $.ajax({
        url: '/Vehicle/GetSubTypesForTypeType',
        data: { typeTypeName: typeTypeName },
        async: false
    }).done(function (data) {
        carsList = data;
    }).error(function (msg, url, line) {
            alert("Error - error message: " + line);
    });

    return carsList;
}

Function to create the select list with the added description as a “data-*” attribute:

function createCarsSelectList(selectData) {
    var html = '',
    len = selectData.length,
    selected,
    description;

    for (var i = 0; i &lt; len; i++) {
        // "Viper" should be selected by default
        if (selectData[i].Name.toLocaleUpperCase() === "VIPER") {
            selected = ' selected="selected" ';
        } else {
            selected = '';
        }

        // Add the description (as a "data-" attribute), some are null
        if (selectData[i].Description != null) {
            description = selectData[i].Description;
        } else {
            description = '';
        }

        html += '<option value="' + selectData[i].SubTypeID + '" data-description="' + description + '"' + selected + '>' + selectData[i].Name + '</option>';
    }

    return html;
}

I recently had a problem where our continuous integration build started failing out of the blue. The last message in the console output was:

Info: Using ID '913622f8-8063-47e5-9eef-44510c5b00ca' for connections to the remote server.
Info: Adding createApp (MyApp).
Error: (6/15/2012 3:06:01 PM) An error occurred when the request was processed on the remote computer.
Error: Unable to perform the operation. Please contact your server administrator to check authorization and delegation settings.

There was nothing in the IIS log files about this but there was an entry in the Event viewer saying that the password had expired for user “WDeployConfigWriter”:

Not able to log on the user '.\WDeployConfigWriter'. ---> System.Runtime.InteropServices.COMException (0x8007052E): Logon failure: unknown user name or bad password. (Exception from HRESULT: 0x8007052E)

So I reset the password and restarted IIS and the Application Pool and tried to build again. Same problems. I checked the Event viewer again and there was the same error as above except this time the user was “WDeployAdmin”. So I reset the pw again and also set both accounts to “password never expires”, followed by the same restarting of IIS and the App pool. Still the same problem though. So I reboot the server to see if this would help but the build still failed after this with the same errors.

After some more digging I found what these user accounts were actually used for. The Web Deploy installer has an option to “Configure for Non-administrator Deployments”. If this option is selected Web Deploy will automatically create Management Service Delegation rules for certain providers, as well as the accounts needed for providers like createApp and recycleApp that need elevated privileges:

Image

There are a set rules in the Management Service Delegation UI in IIS Manager after you install this component. Some of these rules operate under our 2 users above WDeployAdmin and WDeployConfigWriter. Double clicking on these rules allows them to be edited, and when I chose the Set button I could see that the user field was empty, so for some reason the user assignations to these rules were lost meaning I had to re-assign the users to their respective roles. After this the build started working again:

 

On my current project I’ve been working with a concept called a time point, which allows a user to schedule certain events that need to happen at specific points of time. Up until now I had been using the same partial view rendered from different parent views (which were also partial views within an overall view). This caused problems when rendering fields using EditorFor and DropDownListFor methods, as these methods use the model name to create the field names and ids. Because the model was the same, fields called from different parent views were being rendered with the same names and ids. To work around this I had to use an if-else to decide on the prefix that we needed, depending on a certain property in the model which was set in the parent view. This approach was quite clunky and Editor templates negate the need for this as they render the correct ids and names automatically.

An Editor template is a partial view that’s placed by convention in a directory called “EditorTemplates” in Views/Shared, and again by convention is named the same as the view model containing the fields we want to render:

 

Image

 

So in this case my ViewModel is called DiscreteTimepointViewModel.cs:

/// <summary>
/// ViewModel for discrete timepoints
/// </summary>
public class DiscreteTimepointViewModel
{
/// <summary>
/// Gets or sets the TimePointType associated with this StudyEvent
/// </summary>
public int TimePointType { get; set; }
/// <summary>
/// Gets or sets a time points string which can serve as an input for generating an array of time points. This property
/// is only used by the razor views to generate HTML elements using the HTML helpers.
/// </summary>
public double? TimePoint { get; set; }
/// <summary>
/// Gets or sets the ID of the time unit for the specified TimePoint.
/// </summary>
[Required]
public int? TimeUnitID { get; set; }
/// <summary>
/// Gets or sets the model name
/// </summary>
public string ModelName { get; set; }
/// <summary>
/// Gets or sets the left hand field binding name
/// </summary>
public string LeftHandFieldBindingName { get; set; }
}

After adding the template view and view model I then add a view model property to each parent view that I want to render it from:

/// <summary>
/// Gets or sets the view model for discrete time points
/// </summary>
public DiscreteTimepointViewModel DiscreteTimePoint { get; set; }

So using the above I render the time point fields from a view as such:


@{Model.defaultStudyEvent = new DiscreteTimepointViewModel { TimeUnitID = 1, ModelName = "defaultStudyEvent", LeftHandFieldBindingName = "leftHandStudyEvent" }; }
@Html.EditorFor(x => x.defaultStudyEvent, "DiscreteTimepointViewModel")

 

If you’re reading this you might be wondering why in the name of all that’s good and holy would you want to use the same fields in different sections of an accordion control? Surely the whole point of an accordion is that you display different content in different sections? Well read on…

I needed to do exactly this very recently, the main reason being that the screen I was working on would fit with corporate design guidelines. I’m currently working on a large C# .Net MVC3 application that utilises Razor, jQuery, and knockout.js on the front end. The interface is made up of a main partial view containing several tabs that present the data. The company has a corporate interface team that defines the GUI standards that every application must adhere to. Some of the tabs have accordions that show different information in each section, but the tab I’m currently working on has an accordion with 2 sections, each one containing the same fields except one. To me it doesn’t make sense to use an accordion for this, just use a radio button to toggle between the 2 differing fields. However I was overruled on this and told I must use the accordion as it fits in with the other tabs.

So to do it I initially started playing with the code below:

$('#accordion').append($('#div1')).accordion('destroy').accordion()

But this wouldn’t work for me at all, it kept adding a new section to the end of the accordion rather than recreating it. So I started to look at another approach and came up with the jQuery append() method which worked a treat.

Here’s the rendered Razor code:

<div id="accordion">
  <h3 class="acc-header"><a href="#">First header</a></h3>
  <div>
    <fieldset id="input-fields">
      <label for="defaultStudyEvent_GroupName">Group Name</label>
      <input id="defaultStudyEvent_TimePoints" name="defaultStudyEvent.TimePoints" type="text" value="" />
    </fieldset>
  </div>
  <h3 class="acc-header"><a href="#">Second header</a></h3>
  <div>
    (Move "input-fields" fieldset to here when "Second header" is clicked)
  </div>
</div>

Then in jQuery do the following:

$(document).ready(function () {
  $('#accordion').accordion();
  $('.acc-header').click(moveFields);

  function moveFields() {
    $(this).next().append($('#input-fields'));
  }
});

So this approach worked well for my situation. It will move the fields rather than create copies which is what I wanted. It’s suitable for an accordion if you have the same fields in different sections and don’t want to have duplicate fields all over the shop. It will also work in other scenarios if you just want to move fields dynamically depending on your particular scenario. Other variations of this approach are appendTo() and prepend() which do slightly different things.

%d bloggers like this: