ASP.NET MVC: Ajax Dialog Form Using jQuery UI


In one of my recent projects I needed to display some information, allow the user to edit it utilizing a dialog window, post the updated information and reload it for the user using Ajax. I needed to perform the named operations for multiple models so I set out to create some generic code that could be reused over and over. Below is a picture of what I am trying to accomplish.

The project used for the post below can be downloaded here.

First, the model. For this example, we will use a simple model that contains some profile information for a user.

public class Profile
{
    [Required]
    public string Name { get; set; }

    [Required]
    [StringLength(10, MinimumLength=3)]
    [Display(Name="Nick name")]
    public string NickName { get; set; }

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

    [Required]
    public int Age { get; set; }
}

Second, we need to create three action methods. One will be used to display the profile information, another to display the form for editing the profile, and lastly another that will be used to save the edited profile object. The first two should always return a PartialView as each of these action methods will be called using Ajax and their result will be loaded into div elements; the first into a div used to display the saved profile and the second into the edit dialog. The third action method will return a PartialView if the ModelState is invalid so that the errors can be displayed to the user and a Json result indicating the save was successful if all went well. (Note that in this example I am just storing the profile information in Session but obviously this would be stored in a database or some other data store.)

public ActionResult Profile()
{
    Profile profile = new Profile();

    // Retrieve the perviously saved Profile
    if (Session["Profile"] != null)
        profile = Session["Profile"] as Profile;

    return PartialView(profile);
}

public ActionResult EditProfile()
{
    Profile profile = new Profile();

    // Retrieve the perviously saved Profile
    if (Session["Profile"] != null)
        profile = Session["Profile"] as Profile;

    return PartialView(profile);
}

[HttpPost]
public ActionResult EditProfile(Profile profile)
{
    // If the ModelState is invalid then return
    // a PartialView passing in the Profile object
    // with the ModelState errors
    if (!ModelState.IsValid)
        return PartialView("EditProfile", profile);

    // Store the Profile object and return
    // a Json result indicating the Profile 
    // has been saved
    Session["Profile"] = profile;
    return Json(new { success = true });            
}

Next we need to create the two partial views that correspond to the first two action methods we created above. In this example, the partial view for the EditProfile action is pretty much just the stock view created by the MVC frameowork except I have removed the input element to submit the form as we will use the buttons on the jQuery UI dialog to submit it.

The second partial view, the one that displays the saved Profile object again in this example is the stock view with a few added elements.

@using DialogFormExample.MvcHelpers

@model DialogFormExample.Models.Profile

<fieldset>
    <legend>Contact Info</legend>
    
    <div class="display-field">
        Name: @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-field">
        Nick name: @Html.DisplayFor(model => model.NickName)
    </div>
    
    <div class="display-field">
        Email: @Html.DisplayFor(model => model.Email)
    </div>
    
    <div class="display-field">
        Age: @Html.DisplayFor(model => model.Age)
    </div>

    <div class="right">
        @Html.DialogFormLink("Edit", Url.Action("EditProfile"), "Edit Profile", "ProfileContainer", Url.Action("Profile"))
    </div>
</fieldset>

The first portion of the partial view just displays the form elements required for editing the model. This is just the stock Edit view modified only slightly. The new code starts at line 25. Here I created an extension method for HtmlHelper named DialogFormLink that will create an anchor tag loaded with all the needed information to make the dialog form work. Here is the code for the extension method. You can read the comments to get an understanding of the parameters it requires.

/// <summary>
/// Creates a link that will open a jQuery UI dialog form.
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="linkText">The inner text of the anchor element</param>
/// <param name="dialogContentUrl">The url that will return the content to be loaded into the dialog window</param>
/// <param name="dialogTitle">The title to be displayed in the dialog window</param>
/// <param name="updateTargetId">The id of the div that should be updated after the form submission</param>
/// <param name="updateUrl">The url that will return the content to be loaded into the traget div</param>
/// <returns></returns>
public static MvcHtmlString DialogFormLink(this HtmlHelper htmlHelper, string linkText, string dialogContentUrl,
    string dialogId, string dialogTitle, string updateTargetId, string updateUrl)
{
    TagBuilder builder = new TagBuilder("a");
    builder.SetInnerText(linkText);
    builder.Attributes.Add("href", dialogContentUrl);            
    builder.Attributes.Add("data-dialog-title", dialogTitle);
    builder.Attributes.Add("data-update-target-id", updateTargetId);
    builder.Attributes.Add("data-update-url", updateUrl);

    // Add a css class named dialogLink that will be
    // used to identify the anchor tag and to wire up
    // the jQuery functions
    builder.AddCssClass("dialogLink");

    return new MvcHtmlString(builder.ToString());
}   

The above extension method that builds our anchor tag utilizes the HTML5 data attributes to store information such as the title of the dialog window, the url that will return the content of the dialog window, the id of the div to update after the form is submitted, and the url that will update the target div.

Now we need to add the container div to hold the Profile information in the Index view.

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>

<div id="ProfileContainer">
    @{ Html.RenderAction("Profile"); }
</div>

Lastly, I have listed the scripts and css files that need to be linked below in the _Layout page for everything to work correctly.

<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>   
    <script src="@Url.Content("~/Scripts/DialogForm.js")" type="text/javascript"></script>    
</head>

The last script reference above is to a script I wrote called DialogForm.js. This script (shown below) will make everything work.


$(function () {

    // Don't allow browser caching of forms
    $.ajaxSetup({ cache: false });

    // Wire up the click event of any current or future dialog links
    $('.dialogLink').live('click', function () {
        var element = $(this);

        // Retrieve values from the HTML5 data attributes of the link        
        var dialogTitle = element.attr('data-dialog-title');
        var updateTargetId = '#' + element.attr('data-update-target-id');
        var updateUrl = element.attr('data-update-url');

        // Generate a unique id for the dialog div
        var dialogId = 'uniqueName-' + Math.floor(Math.random() * 1000)
        var dialogDiv = "<div id='" + dialogId + "'></div>";

        // Load the form into the dialog div
        $(dialogDiv).load(this.href, function () {
            $(this).dialog({
                modal: true,
                resizable: false,
                title: dialogTitle,
                buttons: {
                    "Save": function () {
                        // Manually submit the form                        
                        var form = $('form', this);
                        $(form).submit();
                    },
                    "Cancel": function () { $(this).dialog('close'); }
                }
            });

            // Enable client side validation
            $.validator.unobtrusive.parse(this);

            // Setup the ajax submit logic
            wireUpForm(this, updateTargetId, updateUrl);
        });
        return false;
    });
});

function wireUpForm(dialog, updateTargetId, updateUrl) {
    $('form', dialog).submit(function () {

        // Do not submit if the form
        // does not pass client side validation
        if (!$(this).valid())
            return false;

        // Client side validation passed, submit the form
        // using the jQuery.ajax form
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {
                // Check whether the post was successful
                if (result.success) {                    
                    // Close the dialog 
                    $(dialog).dialog('close');

                    // Reload the updated data in the target div
                    $(updateTargetId).load(updateUrl);
                } else {
                    // Reload the dialog to show model errors                    
                    $(dialog).html(result);

                    // Enable client side validation
                    $.validator.unobtrusive.parse(dialog);

                    // Setup the ajax submit logic
                    wireUpForm(dialog, updateTargetId, updateUrl);
                }
            }
        });
        return false;
    });
}

Conclusion

I know that was a lot to take in but after you do the setup once, all you need to do is make one call to Html.DialogFormLink and everything will be taken care of for you. Hope this helps!

ASP.NET MVC: Unauthenticated User Always Redirected to ~/Account/LogOn Despite Custom Sign In Url


By default new ASP.NET MVC projects redirect any unauthenticated users who request resources that require authentication to the /Account/LogOn action. The log on page is defined in the web.config authentication section.

<authentication mode="Forms">
  <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

It is my preference for users to “Sign in” instead of “Log on” so I changed the web.config to the following and changed the action method name from LogOn to SignIn.

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" timeout="2880" />
</authentication>

After the change I tried accessing a page that required the user to be authorized with and unauthorized user and to my surprise, the site redirected me to /Account/LogOn and I got a 404 error saying the the resource could not be found. It turns out this is a known issue with the ASP.NET MVC when the WebMatrix.Data.dll and WebMatrix.DataWeb.dll are added to the deployable assemblies collection. This issue can be fixed by adding the following key to the appSettings section of the web.config.

<appSettings>
  ...
  <add key="loginUrl" value="~/Account/SignIn" />
</appSettings>

I presume this will be fixed in the next release of ASP.NET MVC as a bug has been logged on Microsoft Connect here.

Unit Testing: Verify a Method Call Ignoring the Parameters


I use Moq extensively when unit testing my applications. One of the nice features of Moq (and any other Mocking framework) is that you can verify which methods were called on a given interface during a test. By default, if you have a method on an interface that accepts parameters and you want to verify that it is called, you have to specify the exact parameters that should have been used in the method call for it to pass the test. Example:

public interface ICompanyService
{
    void Add(Company company);
}

[Test]
public void SignUp_Action_Calls_CompanyService_Add_Method()
{          
    // Arrange       
    var companyService = new Mock<ICompanyService>();     
    AccountController controller = new AccountController(companyService.Object);

    // Act
    var model = GetSignUpViewModel();
    var result = controller.SignUp(model);
    
    Company company = new Company();
    company.Name = model.Name;
    company.Address = model.Address;
    company.PhoneNumber = model.PhoneNumber;

    // Assert    
    companyService.Verify(x => x.Add(company));
}

If you need to test the actual data be passed to the Add method you can do this but if you just want to test whether or not the method was called regardless of the parameters supplied to the method, you can using Moq. To do this, just use It.IsAny method supplied by the Moq framework to indicate that you just want to see if the method was called with any type of Company object.

[Test]
public void SignUp_Action_Calls_CompanyService_Add_Method()
{          
    ...

    // Assert    
    companyService.Verify(x => x.Add(It.IsAny<Company>()));
}

Mocking frameworks are your friends and save hours of time if you know how to use them correctly.

Dependency Injection: Ninject – Registering Two Services to the Same Instance of an Object


In on of my recent ASP.NET MVC projects I was implementing the UnitOfWork pattern while utilizing the Entity Framework Code First approach for data access. I have seen many examples of the UnitOfWork pattern where the repositories are actually contained within the UnitOfWork class but since the Entity Framework already supports the UnitOfWork pattern out of the box, I decided to go with a different approach.

To jump to the solution to the problem, click here.

Using the Entity Framework Code First approach, a class inheriting from the DbContext class is created and is used to handle all the database interaction. Whenever you perform any work using the DbContext, the changes are not actually persisted to the database until you call the SaveChanges method. As I said, the UnitOfWork pattern already implemented. To enforce the UnitOfWork pattern, I created an IUnitOfWork interface and ensured that my DbContext class implemented that interface.

public interface IUnitOfWork
{
    void Commit();
}

public class DatabaseContext : DbContext, IUnitOfWork
{
    public DbSet<Company> Companies { get; set; }
    public DbSet<Profile> Profiles { get; set; }
    ...
 
    public DatabaseContext ()
    {
        
    }

    public DatabaseContext (string connectionString)
    {
        this.Database.Connection.ConnectionString = connectionString;
    }

    public void Commit()
    {
        base.SaveChanges();
    }
}

Each of the repositories in the application accepts a class that inherits from DbContext and uses that for all database interaction.

public class EntityFrameworkRepository<T> : IRepository<T> where T : class, IEntity
{
    private DbContext context;
    private DbSet<T> dbSet;

    public EntityFrameworkRepository(DbContext context)
    {
        if (context == null) { throw new ArgumentNullException("context"); }
            
        this.context = context;
        this.dbSet = this.context.Set<T>();
    }
}

I then have a service layer that accepts any number of repositories and lastly, each Controller accepts any number of services and a IUnitOfWork implementation.

public class AccountController : BaseController
{
    private IUnitOfWork unitOfWork;
    private IAccountService accountService;
    private IProfileService profileService;

    public AccountController(IUnitOfWork unitOfWork, IAccountService accountService, IProfileService profileService)
    {
        this.unitOfWork = unitOfWork;
        this.accountService = accountService;
        this.profileService = profileService;
    }
}

This allows the Controller to interact with any number of repositories and then after all operations are complete, to call the IUnitOfWork.Commit() method. Here is the issue. With this implementation, the IUnitOfWork instance supplied to the AccountController and the DbContext provided to the repositories all need to be the same instance of the DatabaseContext class for the UnitOfWork pattern to work. Here we my first attempt at doing this.

private static void RegisterServices(IKernel kernel)
{           
    kernel.Bind<IUnitOfWork>().To<DatabaseContext>();
    kernel.Bind<DbContext>().To<DatabaseContext>();
}   

This definitely didn’t work as it created new instances of the DatabaseContext class for each repository and each IUnitOfWork. Using Ninject, to ensure that the same DbContext instance is used for all dependencies in a single http request, you simply need to add InRequestScope() to the end of the registration. So I tried the following.

private static void RegisterServices(IKernel kernel)
{           
    kernel.Bind<IUnitOfWork>().To<DatabaseContext>().InRequestScope();
    kernel.Bind<DbContext>().To<DatabaseContext>().InRequestScope();
}   

This was closer as all the repositories were using the same DbContext but when the Controller requested an IUnitOfWork, Ninject created a new instance of the DatabaseContext class and thus the IUnitOfWork pattern did not work. I needed to somehow ensure that the same instance of the DatabaseContext class was used for all DbContext and IUnitOfWork requests. After some research, I figured out that the following code will resolve the problem.

private static void RegisterServices(IKernel kernel)
{       
    // This gives us the ability to perform multiple operations
    // on multiple repositories in a single transaction.
    kernel.Bind<DatabaseContext>().ToSelf().InRequestScope();
    kernel.Bind<IUnitOfWork>().ToMethod(ctx => ctx.Kernel.Get<DatabaseContext>());
    kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<DatabaseContext>());
}   

In the code above, we are first binding the DatabaseContext class to itself indicating that only one instance should be created per http request. This ensures that whenever a DatabaseContext instance is required, the same one is used. Then, to bind that same DatabaseContext instance to both IUnitOfWork and DbContext request, we need to bind those requests to a method which requests a DatabaseContext instance from the Ninject kernel. And that’s it!

ASP.NET MVC: UserProfile Using the SqlTableProfileProvider


Many have discussed the woes of working with the default ASP.NET SqlProfileProvider in a web application as the profile information is pretty much impossible to query. Gratefully another profile provider was developed, the SqlTableProfileProvider, that stores user profile information in a table format with one column per profile property. The code for that can be found here. But alas, there is still one more issue when using the SqlTableProfileProvider when developing an ASP.NET MVC application. Since it is a web application project and not a web site project, you lose the convenience of the strongly typed Profile object when coding. Again, another work around has been created that solves this problem here. Unfortunately the previously cited example assumes you are using the default SqlProfileProvider and not the SqlTableProfileProvider. I wanted to just name a few changes that need to be made when using the cited Profile Provider in an ASP.NET MVC application with the SqlTableProfileProvider.

First, since you are specifying the profile properties in the UserProfile class, you need to remove the profile property definitions from the web.config profile section. Instead of your profile section looking like this:

<profile enabled="true" defaultProvider="TableProfileProvider" inherits="App.Domain.Models.Account.UserProfile">
  <providers>
    <clear />
    <add name="TableProfileProvider" type="Microsoft.Samples.SqlTableProfileProvider" connectionStringName="Database" table="Profiles" applicationName="/" />       
  </providers>
  <properties>
    <add name="FirstName" type="string" defaultValue="" customProviderData="FirstName;nvarchar" />
    <add name="LastName" type="string" defaultValue="" customProviderData="LastName;nvarchar" />
  </properties>
</profile>

Your profile section will not have the properties section and will look like this:

<profile enabled="true" defaultProvider="TableProfileProvider" inherits="App.Domain.Models.Account.UserProfile">
  <providers>
    <clear />
    <add name="TableProfileProvider" type="Microsoft.Samples.SqlTableProfileProvider" connectionStringName="Database" table="Profiles" applicationName="\" />       
  </providers>
</profile>

If you don’t remove the properties section, you will get a ConfigurationErrorsException at run-time saying “This profile property has already been defined.”

Second, while the UserProfile class will define which properties are available in the profile, you still need to specify the CustomProviderData information that gives the provider the column name and type for each property. To do this, just add the CustomProviderData attribute to each property in the UserProfile class.

public class UserProfile : ProfileBase
{
    [SettingsAllowAnonymous(false), CustomProviderData("FirstName;nvarchar")]
    public string FirstName
    {
        get { return base["FirstName"] as string; }
        set { base["FirstName"] = value; }
    }

    [SettingsAllowAnonymous(false), CustomProviderData("LastName;nvarchar")]
    public string LastName
    {
        get { return base["LastName"] as string; }
        set { base["LastName"] = value; }
    }
}

With those two changes you should be up and running with a strongly typed Profile object using the SqlTableProfileProvider.

ASP.NET MVC: Ensure Session Variable is Always Populated


In a recent project, I needed to ensure that a session variable was populated for every View. Using ASP.NET WebForms one could perform a check for the session variable in the Page_Load event of a master page but this obviously doesn’t work for the MVC Framework as there is no such page life cycle. Despite the lack of a “page life cycle” like WebForms, there is still a request life cycle in which a number of events are fired before and after the controller action.

To solve our problem we need to do two things:

  1. Create a base controller from which all other controllers will be derived
  2. Override the OnActionExecuting event in the base controller

In the following code I have done both the named items above. I have created a class named BaseController that extends the Controller class and have overridden the OnActionExecuting event which will fire each time an action method is requested. Within this event, you simply check the session variable you need populated and if it does not have a value, populate it.

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        // If the current user is authenticated, check to make sure the user's
        // profile has been loaded into session
        if (User.Identity.IsAuthenticated && HttpContext.Session["UserProfile"] == null)
        {
            Session["UserProfile"] = UserProfile.GetUserProfile(User.Identity.Name, true);
        }
    }
}

Now when generating our Controllers for the application, instead of extending the Controller class, just extend the BaseController class we created above.

public class HomeController: BaseController
{
    public ActionResult Index()
    {
        // Since we are extending from our custom BaseController class
        // the OnActionExecuting event will be fired and the session variable
        // will be populated
        return View();
    }
}

You can also simply override the OnActionExecuting event for a single Controller if the logic doesn’t need to span multiple Controllers but be care not to duplicate code if doing this.

Windows Gadget: WordPress Statistics v1.0.4


It appears that WordPress has recently changed the way in which they deliver blog statistics through their API. This change caused the WordPress Statistics Windows Gadget that I created to break. I have submitted an update to the Windows Live Gallery and it is currently pending approval. From my experience it usually takes about one or two days for it to be approved. Please click here to check the gallery over the next few days to get an updated version of the gadget.

Follow

Get every new post delivered to your Inbox.

Join 82 other followers