Skip to content
Archive of posts filed under the ASP.NET MVC category.

Using SSRS In ASP.NET MVC Application

In this blog I will describe my ideas on how to integrate SQL Server reports in ASP.NET MVC applications.  I would like to have as seamless of an integration as possible given the constraints in place.  For example, the only web based report viewer for SSRS is the one that has been shipping with Web Forms (ASPX).  Now that we have problem statement down, let’s get on with a high level solution I would like to implement.

  • I would like to integrate reports into the existing application
  • I would like to show them in an overlay, not wanting to popup additional browser window and having to deal with popup issues in general
  • I would like to make the report viewing safe, trying to reveal as little as possible to the user or technical observer who could use Fiddler for example.

Here is high level outline of my answer to these issues

  • I will have a parameter values collecting view in MVC app
  • I will log the data about selected report into the database along with parameter values, using ajax call to my controller. 
  • I will associate that information with a GUID
  • Controller will return a url with the guid back to my java script method.  Then the method will popup jQuery dialog with an embedded iFrame and set its source to the url passed back from controller
  • I will pass that GUID as a query parameter to ASPX page that will host Web Form report viewer control.
  • I will have web form get the report information from the database along with parameters, then configure report viewer.

That is it.  Now, let’s see some code.

To log report request into the database we just need a couple of tables: request header and request parameters.  I am using entity framework code first, so my tables could look something like this:

    public class ReportRequest
    {
        public int ReportRequestId { get; set; }

        [StringLength(50)]
        public string ReportFileName { get; set; }

        public Guid UniqueId { get; set; }

        public ICollection<ReportRequestParameter> Parameters { get; set; }
    }

 

    public class ReportRequestParameter
    {
        public int ReportRequestParameterId { get; set; }

        [StringLength(50)]
        public string ParameterName { get; set; }

        public string ParameterValue { get; set; }

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

        [ForeignKey("ReportRequestId")]
        public ReportRequest ReportRequest { get; set; }
    }

This part is pretty easy.  Let me now build report center screen that lists all the reports in the system and allows user to print a report.  To support this I just need to have a list of all reports in the system and their parameters.  Again, two tables will suffice.  In parameters table I need the following information:

  • Parameter name (title I will use in a view)
  • SSRS parameter name(s).  I can just separate them via a pipe for example.  This allows me to build complex parameter partial views that can return multiple parameters and their values.
  • Partial view I can use to get the data for a parameter.

So, my plan is to let a user select a report, then build a UI by looking at each parameter and building a partial view for it.  Here is an example of one of those partial views:

@model MyApp.Reports.ReportParameter
<div class="display-label">
    @Model.ParameterName
</div>
<div class="editor-field">
    <select id="@(Model.ReportParameterNames)" name="@(Model.ReportParameterNames)" style="min-width: 200px">
        <option value="1" selected="selected">Option 1</option>
        <option value="2">Option 2</option>
        <option value="3">Option 3</option>
    </select>
</div>

This builds a drop down list control with three options.  One key thing to notice is that I am using ID and name that corresponds to a parameter name,  I will later use it to build parameters.

Now the report center view (I am coming in with a report already selected) may look something like the following

 

@model MyApp.Reports.Report
@using (Html.BeginForm())
{
    @Html.HiddenFor(model => model.ReportID)
    @Html.HiddenFor(model => model.ReportFileName)
    foreach (var parameter in Model.ReportParameters)
    {
        { Html.RenderPartial(parameter.PartialViewName, parameter); }
    }
    <br/>
    <button id="runReportButton" >Report Preview</button>

<div id="reportPreviewDiv" style="display: none">
    <iframe id="reportViewFrame" width="100%" height="100%" ></iframe>
</div>
<script src="@Url.Content("~/Scripts/app.js")" type="text/javascript"></script> 

As you can see, I am building a form from multiple partial views, then I am using jQuery ajax to submit that to my controller:

reports: {
                submitReportRequest: function () {
                    $.ajax({
                        url: "/Report/RunReport",
                        type: "POST",
                        dataType: 'json',
                        data: $('form').serializeArray(),
                        success: function (data, textStatus, jqXhr) {
                            var closeButton = {};
                            closeButton[‘Close’] = function () {
                                $(this).dialog("close");
                                $("#reportViewFrame").attr("src", "about:blank");
                            };
                            $('#reportPreviewDiv').dialog({
                                autoOpen: true,
                                title: ‘Report Preview’,
                                width: 880,
                                height: 800,
                                modal: true,
                                resizable: true,
                                autoResize: false,
                                buttons: closeButton,
                                open: function (event, ui) { $("#reportViewFrame").attr("src", data); }
                            });
                        },
                        error: app.handleAjaxError
                    });
                    return false;
                }
            }

Report class in scripts above is simply following best practices for java script, using name spaces to separate all the functions.

Now my controller method looks something like this:

            [HttpPost]
            public JsonResult RunReport(ReportData formData)
            {
                Guid id = Guid.NewGuid();
                using (var context = new ReportContext())
                {
                    var reportRequest =
                    new ReportRequest
                    {
                        ReportFileName = formData.ReportFileName,
                        UniqueId = id
                    };
                    context.ReportRequests.Add(reportRequest);

                    foreach (var key in formData.Keys)
                    {
                        var parameter =
                              new ReportRequestParameter
                              {
                                  ParameterName = key,
                                  ParameterValue = formData[key],
                                  ReportRequest = reportRequest
                              };
                        context.ReportRequestParameters.Add(parameter);
                    }
                    context.SaveChanges();
                }

                return Json(string.Format("/Reports/ReportForm.aspx?r={0}", id.ToString()));
            }

My form data class that contains all the information submitted by the form.  I created a custom class for it and a custom model binder to automate some data binding and keep me from referring to context inside the controller.

        [ModelBinder(typeof(ReportDataModelBinder))]
        public class ReportData : Dictionary<string, string>
        {

            public int ReportID { get; internal set; }

            public string ReportFileName { get; internal set; }

        }

And here is the binder

    public class ReportDataModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var request = controllerContext.HttpContext.Request;
            var returnValue = new Program.ReportData();
            foreach (var key in request.Form.AllKeys)
            {
                if (key.StartsWith("@"))
                {
                    returnValue.Add(key, request.Form[key]);
                }
                else if (key == "ReportID")
                {
                    returnValue.ReportID = int.Parse(request.Form[key]);
                }
                else if (key == "ReportFileName")
                {
                    returnValue.ReportFileName = request.Form[key];
                }
            }
            return returnValue;
        }
    }

As use can see, I am following convention to start all report (RDL) parameters with an @ sign.  Now all the data submitted through he form that starts with it must be a parameter.  In general, using Model binders is highly encouraged in MVC applications because it gives you an ability to cleanly incorporate custom data that is sent from client to the server in any controllers to avoid writing the same code many times.

 

This ends the MVC portion of the solution.  Now, I just create a brand new ASPX page and add it to the project.  Just use Add New Item menu.  Once form is added, you must update the routes in your MVC applications to ignore things that end with .aspx.  You do so in global.asax

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");

            routes.MapRoute(
                    "Default", // Route name
                    "{controller}/{action}/{id}", // URL with parameters
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

The form itself is pretty simple, it just has report viewer control in it:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReportForm.aspx.cs" Inherits="MyApp.Reports.ReportForm" %>

<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="reportForm" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div>
        <rsweb:ReportViewer ID="mainReportViewer" runat="server" Width="700px" Height="600px">
        </rsweb:ReportViewer>
    </div>
    </form>
</body>
</html>

Make sure to include ScriptManager – report viewer control needs it.  The code behind is just a simple, I am getting the report data and parameter data then setting up my control


       protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                var requestID = Request.QueryString["r"];

  ReportRequest request;

                using (var context = new ReportContext())                {    request = context.ReportRequests.Include("Parameters").Where(one => one.UniqueId == id).FirstOrDefault();                }


                mainReportViewer.ServerReport.ReportServerUrl =
                    new Uri(ConfigurationManager.AppSettings["ReportServerUrl"]);
                mainReportViewer.ServerReport.ReportPath =
                    string.Format(ConfigurationManager.AppSettings["ReportPath"], (data.ReportFileName));
                mainReportViewer.ProcessingMode = ProcessingMode.Remote;
                mainReportViewer.ShowParameterPrompts = false;
                mainReportViewer.ShowRefreshButton = false;
                mainReportViewer.ShowWaitControlCancelLink = false;
                mainReportViewer.ShowBackButton = false;
                mainReportViewer.ShowCredentialPrompts = false;
                var parametersCollection = new List<ReportParameter>();
                foreach (var parameter in request.Parameters)
                {
                    var parameterName = parameter.ParameterName;
                    if (parameterName.StartsWith("@"))
                    {
                        parameterName = parameterName.Substring(1);
                    }
                    parametersCollection.Add(new ReportParameter(parameterName, parameter.ParameterValue, false));
                }
                mainReportViewer.ServerReport.SetParameters(parametersCollection);
                mainReportViewer.ServerReport.Refresh();
            }
        }

Now, we have to add a Http handler to the web.config file:

    <system.webServer>
        <validation validateIntegratedModeConfiguration="false"/>
        <modules runAllManagedModulesForAllRequests="true"/>
        <handlers>
            <add name="ReportViewerWebControlHandler"
            preCondition="integratedMode"
            verb="*" path="Reserved.ReportViewerWebControl.axd"
            type="Microsoft.Reporting.WebForms.HttpHandler,
      Microsoft.ReportViewer.WebForms, Version=10.0.0.0,
      Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
      />
        </handlers>

        </system.webServer>

Wow, that was a lot of steps, but you are not ready to run reports inside your MVC application.  There are a few enhancements I would also like to suggest

  • Delete report request data once it is retrieved.  This makes our approach more secure, where the same url cannot be used multiple times
  • Play with height / width.  You cannot use 100% height/width on the report control because this does not work properly in all the browsers.
  • Remove direct dependency on DbContext by introducing a service or a repository between forms and data.
  • You can probably refine ReportData class, maybe convert it into an object with a dictionary property.
  • You want to actually implement global error handler, I just left a stub in my script
  • You want to implement some sort of ‘please wait’ window while your ajax is running.

Enjoy and let me know what you think.

Post to Twitter

Implementing Error Handling in ASP.NET MVC

In this post I am going to try to document the solution to the following problem. I would like to implement custom global error handling in my ASP.NET MVC application using entity framework to log errors into the database.  Once the error is logged, I want to redirect the user to the custom page that will contain the newly generated error number so that they can contact support and give them more information about the error.

I am going to start with defining a custom error table in my DbContext.  In my case I am going to keep it simple, but you can add any custom columns you would like as long as you can get to the data from your executing application.

Here is how I am going to define my table and context

 

using System;

using System.ComponentModel.DataAnnotations;

 namespace MvcEFCodeFirst.Data

{

    public class AppError

    {

        public int AppErrorID { get; set; }

        public DateTime ErrorDateTime { get; set; }

        [StringLength(250)]

        public string UserIdentity { get; set; }

        public string ErrorText { get; set; }

    }

}

Of course, you can add any other columns you would like that suits your use case.  Then I add this table to my context via a new property:

public DbSet<AppError> AppErrors { get; set; }

Now, I am going to write the key part of the solution – my global error handler object.  It must do the following tasks:

  • Get the last error information
  • Log it into the table above
  • Redirect to the controller that will show a message to the user along with newly generated error (incident) number

The class is pretty simple, and works mostly on HttpContext.Current class to get the information it needs:

using System;

using System.Web;using System.Web.Mvc;

using System.Web.Routing;

using MvcEFCodeFirst.Controllers;

using MvcEFCodeFirst.Data;

using MvcEFCodeFirst.DataAccess;

namespace MvcEFCodeFirst.Helpers

{

    public static class ErrorHandler

    {

        public static void HandleError()

        {

            try

            

                // get the last error

                var exception = HttpContext.Current.Server.GetLastError();

                // clear out error information and any pending responses

                HttpContext.Current.Server.ClearError();

                HttpContext.Current.Response.Clear();

                // create new error to log

                var error = new AppError

                {

                    ErrorDateTime = DateTime.Now

                    ErrorText = exception.ToString(),

                    UserIdentity = HttpContext.Current.User.Identity.Name

                }

                // log error

                using (var context = new CodeStockContext())

                {

                    error = context.AppErrors.Add(error)

                    context.SaveChanges()

                }

                // redirect to error ErrorController.Index method

                // and pass in application error object so that the controller

                // could add error number to the page

                var routeData = new RouteData();

                routeData.Values.Add("controller", "Error");

                routeData.Values.Add("action", "Index");

                routeData.Values.Add("appError", error);

                ((IController)new ErrorController())

                    .Execute(new RequestContext(

                        new HttpContextWrapper(HttpContext.Current), routeData));

            }

            // ReSharper disable EmptyGeneralCatchClause

            catch (Exception)

            // ReSharper restore EmptyGeneralCatchClause

            

                //Ignore errors that occur during loggin

            

        }

    }

}

I added comments to demonstrate what I am doing in this class.

Now. let me show you my error controller, which is very simple and the Index view for it:

using System.Web.Mvc;

using MvcEFCodeFirst.Data;

namespace MvcEFCodeFirst.Controllers

{

    public class ErrorController : Controller

    {

        public ActionResult Index(AppError appError)

        {

            return View(appError);

        }

    }

}

Now, the view

@using MvcEFCodeFirst.Data

@model AppError

<h2>

    Error has occurred</h2>

<h4>

    An error has occurred and has been logged. Error number is @Model.AppErrorID.ToString().

    Please contact technical support.

</h4>

<br /

@Html.ActionLink("Main Menu", "", "")


And a few last steps.  I turn off custom errors in web config since I an implementing them differently now:

<system.web>

    <customErrors mode="Off"/>

</system.web>

And the last step is to call my error handler from Global.asax error handling routine:

using System;

using System.Web.Mvc;

using System.Web.Routing;

using System.Data.Entity;

using MvcEFCodeFirst.DataAccess;

using MvcEFCodeFirst.Helpers;

 
namespace MvcEFCodeFirst

{

    public class MvcApplication : System.Web.HttpApplication

    {

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)

        {

            filters.Add(new HandleErrorAttribute());

        }

 
        public static void RegisterRoutes(RouteCollection routes)

        {

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 
            routes.MapRoute(

                "Default",

                "{controller}/{action}/{id}",

                new { controller = "Home", action = "Index", id = UrlParameter.Optional }

            );

 
        }

 
        protected void Application_Start()

        {

            AreaRegistration.RegisterAllAreas();

 
            RegisterGlobalFilters(GlobalFilters.Filters);

            RegisterRoutes(RouteTable.Routes);

 
            Database.SetInitializer(new DbInitializer());

        }

 
 

        protected void Application_Error(object sender, EventArgs e)

        {

            ErrorHandler.HandleError();

        }


    }

}

And that is all there is to it.  I know there is a bunch of other solutions, such as ELMAH, I saw while investigating my possible answers to the problem, but I think this one suits my goals the best.  Of course, you can also retrieve the error code from the exception by casting it to HttpException, but in my case I did not need to do that.  Another option is to use Response.Redirect instead of Controller.Execute, but this is a small variation of the same solution.

Thanks.

Post to Twitter

Detecting Pending Changes in ASP.NET MVC

In this blog I am going to describe a pretty common problem that web applications have to deal with along with one way to solve this issue.

Here is the issue at hand.  Say, user pulled up an entry form and made some changes.  Then the above mentioned user clicked browser back button, navigating away from the current page, and thus loosing all the pending changes.  A friendly software application should warn the user, shouldn’t it?

So, how do we detect pending changes?  In typical desktop application, say the one that uses CSLA – http://lhotka.net/cslanet/, you can very easily check existing bound objects and see if there are pending changes.  CSLA, for example, takes a snapshot of business objects, then compare the snapshot to current state of the same object.  What if we could do the same?

To our benefit, there is a very useful function in jQuery, called serialize
http://api.jquery.com/serialize/

So, could we use that to solve the problem?  Let’s try something like

initialFormData = $(‘form’).serialize();

Seems pretty easy.  Now we need to create a JavaScript object that can help us to all the steps

  • Create snapshot of the data when it is loaded
  • Compare the snapshot to current data
  • Intercept navigation in progress and warn the user

I am going to create a brand new script file with my application in it, calling it App.js and define my object in it as following

if (!window.App) {
    window.App = {
        init:
function
() {
            window.onbeforeunload =
this
.onBeforeUnloadWindow;
           
this.initialFormData = $(‘form’
).serialize();
           
this.submitting = false
;
        },
        onBeforeUnloadWindow:
function
() {
           
return
window.App.hasPendingChanges();
        },
        submitting:
false
,
        initialFormData:
""
,
        hasPendingChanges:
function
() {
           
if (!window.App.submitting && 
                window.App.initialFormData !== $(
‘form’
).serialize()) {
               
return "You have pending changes. \n\rIf you leave current page, those changes will be lost."
;
            }
           
return
undefined;
        }
    };
}

Very quick and easy.  One small object, neatly organized that will do the work for me.  Now in my MVC application I locate _Layout.cshtml and include my script in the list of scripts.  The last step is to call init method on my App object after form loads, for example in Edit.cshtml:

<script type="text/javascript">
        window.App.init();
</script>

Now, if you try this solution, you will notice that your Save button does something strange.  It pops up the warning message for no reason.  So, we have to do one more thing in our Save button click event:

<input type="submit" onclick="window.App.submitting = true;" 
 
value=
@ViewBag.Action />

We have to let the App object know that we are navigating away because the user hit Save button.  That was our last step.  One of the guys on our team came up with this idea, and he and I refined it a bit to make the code cleaner.

Please let me know what you think.

Thanks.

Post to Twitter

jqGrid ajax Operations in ASP.NMET MVC

I am using jqGrid in my current project, and I already blogged prior on how impressed I am with the functionality available in this control.  I am using it in ASP.NET MVC application, but it can be used in any web app.  One of the key features is that it supports ajax based asynchronous server operations.

Upon my research I stumbled onto this article that greatly simplifies the code required to process search requests in ASP.NET MVS that are generated by the grid

http://www.codeproject.com/KB/aspnet/AspNetMVCandJqGrid.aspx

I had to make a small addition to it though.  The code works great in multi-search scenarios, but if you turn off multisearch option in search options, than you will get an error.  I also remove empty try/catch blocks which I personally find to be a bad practice.  Here is the updated code I used for Filter class:

using System.Runtime.Serialization;
using
System.Runtime.Serialization.Json;
using
System.Text;
 
[
DataContract
]
public class Filter

{
    [
DataMember]
   
public string groupOp { get; set
; }
    [
DataMember
]
   
public Rule[] rules { get; set
; }
 
   
public static Filter
Create(
       
string
jsonData, 
       
string
searchField, 
       
string
searchString, 
       
string
searchOper)
    {
       
Filter returnValue = null
;
       
if (!string
.IsNullOrEmpty(jsonData))
        {
           
var serializer = new DataContractJsonSerializer(typeof(Filter
));
           
using (var ms = new System.IO.MemoryStream(Encoding
.Default.GetBytes(jsonData)))
            {
                returnValue = serializer.ReadObject(ms)
as Filter
;
            }
 
        }
       
else

        {
            returnValue =
                   
new Filter()
                    {
                        groupOp =
"AND"
,
                        rules =
new[] { new Rule
() 
                        { data = searchString, field = searchField, op = searchOper } }
                    };
        }
       
return
returnValue;
    }
}

 

I added else statement that checks the json data variable, which will be null in cases of single field search.  The code will fall into same patch though, which is convenient because you process data code can stay the same whether or not you are using single or multi-search. 

Of course, I had to update the model binder to match:

using System.Web.Mvc;
 

public class GridModelBinder : IModelBinder

{
   
public object BindModel(
       
ControllerContext
controllerContext,
       
ModelBindingContext
bindingContext)
    {
       
var
request = controllerContext.HttpContext.Request;
       
return new GridSettings

        {
            IsSearch =
bool.Parse(request["_search"] ?? "false"),
            PageIndex =
int.Parse(request["page"] ?? "1"
),
            PageSize =
int.Parse(request["rows"] ?? "10"
),
            SortColumn = request[
"sidx"] ?? ""
,
            SortOrder = request[
"sord"] ?? "asc"
,
            Where =
Filter
.Create(
                request[
"filters"
],
                request[
"searchField"
],
                request[
"searchString"
],
                request[
"searchOper"
])
        };
    }
}

 

I want to thank Ilya Builuk for sharing his code.  I hope you will find my enhancements useful.  Another side thought.  There is a string based IQueryable extensions library that you can use to construct queries against this interface based on syntax such as “Where SomeColumn =- 2”.  Not that I am a giant fan of leaking my data access code into controllers or anything.  You can read more about dynamic linq on Scott Guthrie’s blog here

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Thanks.

Post to Twitter

Client and Server Side Validation in MVC 3

In this post I would like to examine how to create integrated client and server side validation in MVC 3.  The example is a bit contrived, but here is the just of it.  I have a person class and I want to make sure that email address does not contain first or last name and also cannot be longer than 50 characters.  Here is why I picked this example.  I want to be able to pass in value (maximum length) and also create parallel client / server side validation that enforces this rule.  This rule will dynamically get the values from last and first name fields and compare it to email field.  OK, now that the goal is stated, time to work on implementation.

First, let’s work on server side.  We can never trust the client, so server must enforce all client rules again.  Server side coding is very easy and can be easily accomplished via an attribute.  There is already a number of attribute, such as Required that we can use, but I want to create a custom rule in this case.  So, I will create brand new attribute, and take advantage of existing MVC functionality by inheriting from ValidationAttribute.  To make it more versatile, I am going to rely on a custom interface:

    public interface IPersonWithEmail
    {
       
string FirstName { get; set; }
       
string LastName { get; set
; }
       
string Email { get; set
; }
    }

 

Now, the attribute itself:

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
 
public class AdvancedEmailValidationAttribute : ValidationAttribute

  {
   
public
AdvancedEmailValidationAttribute()
      :
this
(0)
    {
    }
 
   
public AdvancedEmailValidationAttribute(int
maximumLength)
    {
      MaximumLength = maximumLength == 0 ?
        50 : maximumLength;
      ErrorMessage =
string.Format("Email cannot contain first or last name and cannot be longer than {0} characters."
, MaximumLength);
    }
   
public int MaximumLength { get; private set
; }
 
   
protected override ValidationResult IsValid(object value, ValidationContext
validationContext)
    {
     
var objectToValidate = validationContext.ObjectInstance as IPersonWithEmail
;
     
ValidationResult returnValue = ValidationResult
.Success;
 
     
if (objectToValidate != null
)
      {
       
if (value != null
)
        {
         
if
(
            value.ToString().ToUpper().Contains(objectToValidate.LastName.ToUpper()) ||
            value.ToString().ToUpper().Contains(objectToValidate.LastName.ToUpper()) || 
            value.ToString().Length > MaximumLength)
          {
            returnValue =
new ValidationResult
(ErrorMessage);
          }
        }
      }
     
else

      {
        returnValue =
new ValidationResult("You must implement IPersonWithEmail on your object to use this rule");
      }
     
return
returnValue;
    }
 
  }

 

As you can see above, I am have a custom property called MaximumLength that I am populating via constructor.  This promotes attribute reuse.  Then, I am overriding IsValid method.  This method simply checks the rule.  If rule succeeds, it returns ValidationResult.Success.  Otherwise it returns instance of ValidationResult with a specific error message.  That is all.  To use the attribute, I simply decorate the email field with this attribute as below:

    [AdvancedEmailValidation()]
   
public string Email { get; set
; }

 

Now, if I run the sample, I will see this in action.  Because I do not have client rules yet, I will see the postback, but when my screen comes back, the error will be shown.

To make this functionality more interactive, I am now going to add client side validation.  TO start with, I will extend my attribute with client side validation using IClientValidatable interface.  Here is what it looks like:

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
 
public class AdvancedEmailValidationAttribute : ValidationAttribute, IClientValidatable

  {
   
public AdvancedEmailValidationAttribute()
      :
this
(0)
    {
    }
 
   
public AdvancedEmailValidationAttribute(int
maximumLength)
    {
      MaximumLength = maximumLength == 0 ?
        50 : maximumLength;
      ErrorMessage =
string.Format("Email cannot contain first or last name and cannot be longer than {0} characters."
, MaximumLength);
    }
   
public int MaximumLength { get; private set
; }
 
   
protected override ValidationResult IsValid(object value, ValidationContext
validationContext)
    {
     
var objectToValidate = validationContext.ObjectInstance as IPersonWithEmail
;
     
ValidationResult returnValue = ValidationResult
.Success;
 
     
if (objectToValidate != null
)
      {
       
if (value != null
)
        {
         
if
(
            value.ToString().ToUpper().Contains(objectToValidate.LastName.ToUpper()) ||
            value.ToString().ToUpper().Contains(objectToValidate.LastName.ToUpper()) || 
            value.ToString().Length > MaximumLength)
          {
            returnValue =
new ValidationResult
(ErrorMessage);
          }
        }
      }
     
else

      {
        returnValue =
new ValidationResult("You must implement IPersonWithEmail on your object to use this rule");
      }
     
return
returnValue;
    }
 
   
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext
context)
    {
     
var rule = new ModelClientValidationRule

      {
        ErrorMessage = ErrorMessage,
        ValidationType =
"advancedemail"
      };
      rule.ValidationParameters.Add(
"maxlength", MaximumLength);
      rule.ValidationParameters.Add(
"firstname", "FirstName"
);
      rule.ValidationParameters.Add(
"lastname", "LastName"
);
     
yield return
rule;
 
    }
  }

 

This interface only has one member – method called GetClientValidaitonRules.  In my case I am only returning one rule.  I can create a custom rule by inheriting from ModelClientValidationRule, but my use case is simple, so I am not doing it.  I am also adding validation parameters (all strings) that I will need on client side.  It is obviously maximum length, and first and last name property names.  I will use all three at the client to compare email to first and last name as well as maximum length.  The next step in the process is to use jQuery  validation and write methods that inject validation as well as enforce it.

Here is my script (I am including entire partial view for clarity)

@if (false)

 
<reference src="../../Scripts/jquery-1.5.1-vsdoc.js" type="text/javascript" />

 
<reference src="../../Scripts/jquery.validate.js" type="text/javascript" />
 
<reference src="../../Scripts/jquery.validate.unobtrusive.js" type="text/javascript" />
}
@model MvcEFCodeFirst.Data.
Attendee
@
using (Html.BeginForm(new { Id = "AttendeeForm" }))
{
  @Html.ValidationSummary(
true
)
 
<fieldset>

   
<legend>Attendee</legend>
   
<div class="editor-label">
      @Html.LabelFor(model => model.FirstName)
   
</div>
   
<div class="editor-field">
      @Html.EditorFor(model => model.FirstName)
      @Html.ValidationMessageFor(model => model.FirstName)
   
</div>
   
<div class="editor-label">
      @Html.LabelFor(model => model.LastName)
   
</div>
   
<div class="editor-field">
      @Html.EditorFor(model => model.LastName)
      @Html.ValidationMessageFor(model => model.LastName)
   
</div>
   
<div class="editor-label">
      @Html.LabelFor(model => model.Email)
   
</div>
   
<div class="editor-field">
      @Html.EditorFor(model => model.Email)
      @Html.ValidationMessageFor(model => model.Email)
   
</div>
   
<div class="editor-label">
      @Html.LabelFor(model => model.Notes)
   
</div>
   
<div class="editor-field">
      @Html.EditorFor(model => model.Notes)
      @Html.ValidationMessageFor(model => model.Notes)
   
</div>
    @
if (Model.AttendeeID > 0)
    { 
      @Html.HiddenFor(model => model.AttendeeID)
    }
 
</fieldset>

}
<script type="text/javascript">
 
 
  (
function ($) {
 
    $.validator.addMethod(
‘advancedemail’, function
(value, element, param) {
     
if (!value) return false
;
     
var lastName = $(‘#’
+ param.lastname).val();
     
var firstName = $(‘#’
+ param.firstname).val();
     
var
maxlength = param.maxlength;
     
if
(value.toString().toLowerCase().indexOf(lastName.toString().toLowerCase()) >= 0 ||
        value.toString().toLowerCase().indexOf(firstName.toString().toLowerCase()) >= 0 ||
          value.toString().length > maxlength) {
       
return false
;
      }
     
return true
;
    });
 
 
    $.validator.unobtrusive.adapters.add(
     
‘advancedemail’
,
      [
'maxlength', 'firstname', 'lastname'
],
     
function
(options) {
 
       
var
params = {
          maxlength: options.params.maxlength,
          firstname: options.params.firstname,
          lastname: options.params.lastname
        };
        
        options.rules[
'advancedemail'
] = params;
       
if
(options.message) {
          options.messages[
'advancedemail'
] = options.message;
        }
 
      });
  } (jQuery));

</script
>

 

As you can see from above, my code to inject the rule consists of two components – function that performs validation (follows validator.addMethod call) and function that injects it into validation infrastructure (validator.unobtrusive.adapters.add call).  First, let me examine the second function that does injection.  I am adding new unobtrusive adapater with the name of advancedemail.  This matches what I put into my attribute – this is how the attribute and client side work together.  I am pumping the parameters into function that will return validation method options as a class called “params”, as well as message.

The actual validation function is interesting as well.  I am getting the values from the form by using jQuery selector for the form input control name that is actually an option passed in by my attribute.  Then I am actaully enforcing the rule by doing basic string manipulation.  ALl I need to do is return true or false from the actual validation function, where false means the rule failed.  I am injecting the entire set of functionality into partial view by including a script tag that executes right away.  If you view source of this page, you will see how my attribute data got injected into the view:

    <div class="editor-label">

      <label for="Email">Email</label>

    </div>

    <div class="editor-field">

      <input class="text-box single-line" data-val="true" data-val-advancedemail="Email cannot contain first or last name and cannot be longer than 50 characters." data-val-advancedemail-firstname="FirstName" data-val-advancedemail-lastname="LastName" data-val-advancedemail-maxlength="50" data-val-length="The field Email must be a string with a maximum length of 250." data-val-length-max="250" data-val-regex="Email is not in a correct format" data-val-regex-pattern="^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$" data-val-remote="Email address is duplicate" data-val-remote-additionalfields="*.Email,*.AttendeeID" data-val-remote-url="/MvcEFCodeFirst/Attendee/DuplicateEmail" data-val-required="Email is required" id="Email" name="Email" type="text" value="" />

      <span class="field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"></span>

    </div>

I know, a little hard to read, but you can certainly see the result of my attribute by looking at the data that starts with data-val-advancedemail.

One thing to note is that my validator name is all lower case, and this is because jQuery validation may generate the error – method name must be in lower case without digits.

To summarize, client and server side validation should both be covered when it comes to rules.  Server side can be easily enforced with ValidationAttribute.  Client side validation required three moving parts – IClientValidatable on the server side, function that performs validation at the client side and code that creates unobtrusive validation adapter that points to that function.

You can download entire sample here.

Post to Twitter

ASP.NET MVC Custom Authentication

The other day I was working on a sample application and was trying to come up with an efficient way to handle authentication and authorization.  I decided to consolidate this code by implementing custom principal and identity objects.  I find this approach very flexible and concise.  I wanted to document it for my own benefit and to gather some feedback if anyone is willing to share.

First of all, I am going to setup a brand new MVC project.  I will pick internet template to get me a head start on creating basic authentication database and classes.  Next I will run the application one time and register a user.  Next, I am going to select Project->ASP.NET Configuration. I am going to enable roles, create a few and assign them to my user.  Now my preparation work is done and it is rime to write some classes.  If you ever looked at the interfaces IIdentity and IPrincipal, you probably already know what I am going to write, but the trick is that I am going to use membership provider to do authentication and roles provider to do authorization.

First of all, I am going to create some simple interfaces for both classes for general cleanliness and ability to mock both classes for tests:

using System.Security.Principal;
 

namespace
MvcFormsAuth.Security
{
   
public interface ICustomIdentity : IIdentity

    {
       
bool IsInRole(string role);
       
string
ToJson();
    }
}

using System.Security.Principal;
 

namespace
MvcFormsAuth.Security
{
   
public interface ICustomPrincipal : IPrincipal

    {
 
    }
}

 

This part was simple.  Now, the actual implementation.  To authenticate I am going to use membership provider, but you can just as easily plug in custom implementation.

        /// <summary>
       
/// Authenticate and get identity out with roles
       
/// </summary>
       
/// <param name="userName">User name</param>
       
/// <param name="password">Password</param>
       
/// <returns>Instance of identity</returns>
       
public static CustomIdentity GetCustomIdentity(string userName, string password)
        {
           
CustomIdentity identity = new CustomIdentity
();
           
if (Membership
.ValidateUser(userName, password))
            {
                identity.IsAuthenticated =
true
;
                identity.Name = userName;
               
var roles = System.Web.Security.Roles
.GetRolesForUser(userName);
                identity.Roles = roles;
               
return
identity;
            }
           
return
identity;
        }

 

As you can see, I am creating an instance of the object, but only setting the properties if membership provider confirms identity.  I am also getting a list of roles and saving it off with identity.  Of course, you can add some custom properties to it as well.  My next step is come up with scheme to save this information in a cookie, thus being able to add custom information to the authentication cooking.  To do so, I am going to use json based serialization.  I am also going to create a custom class to pump my information into such as :

using System;
 

namespace
MvcFormsAuth.Security
{
   
/// <summary>

   
/// Private members have short names to preserve space using json serialization
   
/// </summary>
   
public class IdentityRepresentation
    {
       
private bool ia;
 
       
public bool
IsAuthenticated
        {
           
get { return
ia; }
           
set { ia = value
; }
        }
 
       
private string
n;
 
       
public string
Name
        {
           
get { return
n; }
           
set { n = value
; }
        }
 
       
private string
r;
 
       
public string
Roles
        {
           
get { return
r; }
           
set { r = value
; }
        }
        
    }
}

 

You noticed that field names are very short.  I am doing this to save space during serialization process.  There is a limit to how much information can be stored in a cookie, and every byte matters in this case.  Here is what my serialization code looks like:

        /// <summary>
       
/// Create serialized string for storing in a cookie
       
/// </summary>
       
/// <returns>String representation of identity</returns>
       
public string ToJson()
        {
           
string returnValue = string
.Empty;
           
IdentityRepresentation representation = new IdentityRepresentation
()
            {
                IsAuthenticated =
this
.IsAuthenticated,
                Name =
this
.Name,
                Roles =
string.Join("|", this
.Roles)
            };
           
DataContractJsonSerializer
jsonSerializer = 
               
new DataContractJsonSerializer(typeof(IdentityRepresentation
));
           
using (MemoryStream stream = new MemoryStream
())
            {
                jsonSerializer.WriteObject(stream, representation);
                stream.Flush();
               
byte
[] json = stream.ToArray();
                returnValue =
Encoding
.UTF8.GetString(json, 0, json.Length);
            }
 
           
return
returnValue;
        }

 

My complete identity class follows:

using System;
using
System.IO;
using
System.Linq;
using
System.Runtime.Serialization.Json;
using
System.Text;
using
System.Web.Security;
 

namespace
MvcFormsAuth.Security
{
   
public class CustomIdentity : ICustomIdentity

    {
       
/// <summary>
       
/// Authenticate and get identity out with roles
       
/// </summary>
       
/// <param name="userName">User name</param>
       
/// <param name="password">Password</param>
       
/// <returns>Instance of identity</returns>
       
public static CustomIdentity GetCustomIdentity(string userName, string password)
        {
           
CustomIdentity identity = new CustomIdentity
();
           
if (Membership
.ValidateUser(userName, password))
            {
                identity.IsAuthenticated =
true
;
                identity.Name = userName;
               
var roles = System.Web.Security.Roles
.GetRolesForUser(userName);
                identity.Roles = roles;
               
return
identity;
            }
           
return
identity;
        }
 
       
private
CustomIdentity() { }
 
       
public string
AuthenticationType
        {
           
get { return "Custom"
; }
        }
 
       
public bool IsAuthenticated { get; private set
; }
 
       
public string Name { get; private set
; }
 
       
private string[] Roles { get; set
; }
 
       
public bool IsInRole(string
role)
        {
           
if (string
.IsNullOrEmpty(role))
            {
               
throw new ArgumentException("Role is null"
);
            }
           
return
Roles.Where(one => one.ToUpper().Trim() == role.ToUpper().Trim()).Any();
        }
 
       
/// <summary>

       
/// Create serialized string for storing in a cookie
       
/// </summary>
       
/// <returns>String representation of identity</returns>
       
public string ToJson()
        {
           
string returnValue = string
.Empty;
           
IdentityRepresentation representation = new IdentityRepresentation
()
            {
                IsAuthenticated =
this
.IsAuthenticated,
                Name =
this
.Name,
                Roles =
string.Join("|", this
.Roles)
            };
           
DataContractJsonSerializer
jsonSerializer = 
               
new DataContractJsonSerializer(typeof(IdentityRepresentation
));
           
using (MemoryStream stream = new MemoryStream
())
            {
                jsonSerializer.WriteObject(stream, representation);
                stream.Flush();
               
byte
[] json = stream.ToArray();
                returnValue =
Encoding
.UTF8.GetString(json, 0, json.Length);
            }
 
           
return
returnValue;
        }
 
       
/// <summary>

       
/// Create identity from a cookie data
       
/// </summary>
       
/// <param name="cookieString">String stored in cookie, created via ToJson method</param>
       
/// <returns>Instance of identity</returns>
       
public static ICustomIdentity FromJson(string cookieString)
        {
 
           
IdentityRepresentation serializedIdentity = null
;
           
using (MemoryStream stream = new MemoryStream(Encoding
.UTF8.GetBytes(cookieString)))
            {
               
DataContractJsonSerializer
jsonSerializer = 
                   
new DataContractJsonSerializer(typeof(IdentityRepresentation
));
                serializedIdentity = jsonSerializer.ReadObject(stream)
as IdentityRepresentation
;
            }
           
CustomIdentity identity = new CustomIdentity
()
            {
                IsAuthenticated = serializedIdentity.IsAuthenticated,
                Name = serializedIdentity.Name,
                Roles = serializedIdentity.Roles
                    .Split(
new string[] { "|" }, StringSplitOptions
.RemoveEmptyEntries)
            };
           
return
identity;
        }
 
    }
}

 

Principal class is much lighter, and it mostly responsible for setting User property on HttpContext:

using System;
using
System.Security.Principal;
using
System.Web;
using
System.Web.Security;
 

namespace
MvcFormsAuth.Security
{
   
public class CustomPrincipal : ICustomPrincipal

    {
       
private CustomPrincipal() { }
 
       
private CustomPrincipal(ICustomIdentity
identity) 
        {
           
this
.Identity = identity;
        }
 
       
public IIdentity Identity { get; private set
; }
 
       
public bool IsInRole(string
role)
        {
           
if (string
.IsNullOrEmpty(role))
            {
               
throw new ArgumentException("Role is null"
);
            }
           
return ((ICustomIdentity
)Identity).IsInRole(role);
        }
 
 
       
public static void
Logout()
        {
           
HttpContext
.Current.User = 
               
new GenericPrincipal(new GenericIdentity(""), new string
[] { });
        }
 
       
/// <summary>

       
/// Login
       
/// </summary>
       
/// <param name="userName">User name</param>
       
/// <param name="password">Password</param>
       
/// <param name="rememberMe">True, if authentication should persist between browser sessions
       
/// </param>
       
/// <returns>True if login succeeds</returns>
       
public static bool Login(string userName, string password, bool rememberMe)
        {
           
var identity = CustomIdentity
.GetCustomIdentity(userName, password);
           
if
(identity.IsAuthenticated)
            {
               
HttpContext.Current.User = new CustomPrincipal
(identity);
               
FormsAuthenticationTicket
ticket =
                      
new FormsAuthenticationTicket
(
                           1, identity.Name,
DateTime.Now, DateTime
.Now.AddMinutes(30), rememberMe,
                           identity.ToJson(),
FormsAuthentication
.FormsCookiePath);
               
string encryptedTicket = FormsAuthentication
.Encrypt(ticket);
 
               
var cookie = new HttpCookie(FormsAuthentication
.FormsCookieName, encryptedTicket);
                cookie.Path =
FormsAuthentication
.FormsCookiePath;
               
if
(rememberMe)
                {
                    cookie.Expires =
DateTime.Now.AddYears(1);// good for one year

                }
 
               
HttpContext.Current.Response.Cookies.Add(cookie);
            }
           
return
identity.IsAuthenticated;
        }
 
       
public static bool Login(string
cookieString)
        {
           
ICustomIdentity identity = CustomIdentity
.FromJson(cookieString);
           
if
(identity.IsAuthenticated)
            {
               
HttpContext.Current.User = new CustomPrincipal
(identity);
            }
           
return
identity.IsAuthenticated;
        }
    }
}

 

The most interested code is in Login methods.  Primary login method delegates authentication to identity class, then just sets User on HttpContext.  You may notice how “remember me” is implemented.  I am doing it by setting expiration date on authentication cookie.  I am making it valid for one year since login.

So far so good.  Now what do we do when authenticated user hits a page?  We will manually implement sliding expiration and call the secondary login method of out principal.  The most logical place for that is in a base controller we are going to create by overriding OnAuthorization method:

using System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.Mvc;
using
System.Web.Security;
using
MvcFormsAuth.Security;
 

namespace
MvcFormsAuth.Controllers
{
    [
Authorize
]
   
public class AuthorizedController : Controller

    {
       
protected override void OnAuthorization(AuthorizationContext filterContext)
        {
           
base
.OnAuthorization(filterContext);
           
HttpCookie cookie = Request.Cookies[FormsAuthentication
.FormsCookieName];
           
if (cookie != null
)
            {
               
FormsAuthenticationTicket ticket = FormsAuthentication
.Decrypt(cookie.Value);
               
var newTicket = FormsAuthentication
.RenewTicketIfOld(ticket);
               
if
(newTicket.Expiration != ticket.Expiration)
                {
                   
string encryptedTicket = FormsAuthentication
.Encrypt(newTicket);
 
                    cookie =
new HttpCookie(FormsAuthentication
.FormsCookieName, encryptedTicket);
                    cookie.Path =
FormsAuthentication
.FormsCookiePath;
                    Response.Cookies.Add(cookie);
                }
               
CustomPrincipal
.Login(ticket.UserData);
            }
        }
    }
}

 

Now all my controllers that need to be authenticated and authorized can just inherit from this base class.  The login call simply uses user data I added to start with to rehydrate the identity and principal objects.

To summarizer, because I am saving some necessary data in the cooking I am saving an extra call or two to the database to get user and roles information on subsequent requests.

Here is a little bit more information on the subject that will be helpful

To enforce authentication rules on your entire site, add the following to web.config

           <system.web>

 
                      <authentication mode="Forms">
                                 <forms loginUrl="~/Account/LogOn" timeout="2880" />
                      </authentication>
 
                      <authorization>
                                 <deny users="?"/>
                      </authorization>

 

Make sure to exclude your styles and scripts from authentication.  Otherwise you will notice that your login page is not styled.  To do so, just add location configuration to your web.config as follows.  The same applies to your login/register, etc… methods.

           <!– allow unauthenticated users to get CSS data –>
           <location path="Content">
                      <system.web>
                                 <authorization>
                                            <allow users="?"/>
                                 </authorization>
                      </system.web>
           </location>
           <location path="Account">
                      <system.web>
                                 <authorization>
                                            <allow users="?"/>
                                 </authorization>
                      </system.web>
           </location>
</configuration>

 

 

You can download entire sample application here.

Please share your opinions on this subject.

Post to Twitter

CodeStock Talks

I am doing two talks at CodeStock event on Saturday, June  2011.  Tomorrow in other words Smile.

I am talking on two topics. 

Since I would like to share my slides and sample code, you can download two zip files – one for each session.  You can download each one by clicking on the title above.

Thank you.

Post to Twitter

More on Client Validation in ASP.NET MVC 3

I posted a while ago on asynchronous saves in MVC 3.  One thing to note that after the new content is loaded into the browser, client side validation is broken.  The reason being is that content is only parsed once by unobtrusive jQuery validation.  To fix this issue I would need to force the validator to parse the content again.  Here is what my JavaScript to submit the form would look like now (changes from previous post are in bold)

@if (false)

{   <script src="../../Scripts/jquery-1.5.1-vsdoc.js" type="text/javascript"></script>}

@model MvcEFCodeFirst.Data.Attendee

@{

  Layout = "~/Views/Shared/_Layout.cshtml";

}

<h2>

  @ViewBag.Title</h2>

<div id="formDiv">

  @{Html.RenderPartial("AttendeePartial", Model);}

</div>

<div>

  <button id="saveNewAttendee">

    @ViewBag.ButtonText

  </button>

</div>

<div>

  @Html.ActionLink("Back to List", "Index")

</div>

<script type="text/javascript">

  $().ready(function () {

    $(‘#saveNewAttendee’).click(function () {

      if (!$(‘form’).valid()) {

        return false;

      }

      $.blockUI({ message: ‘<h1>Updating…</h1>’ });

      $.ajax({

        type: "POST",

        url: "./CreateUpdateAjax",

        data: $(‘form’).first().serialize(),

        success: function (data, status, xhr) {

          $(‘#formDiv’).html(data);

          if ($(‘#AttendeeID’).val() != undefined) {

            $(‘#saveNewAttendee’).html(‘Save’);

          };

          $.unblockUI();

          jQuery.validator.unobtrusive.parse(‘form’);

        },

        error: function (xhr, status, error) {

          $.unblockUI();

          alert("error");

        }

      });

    })

 

  })

 

</script>

 

I also would like to spend a bit of time on custom client side validation that is not built based on attributes in POCO classes of Entity Framework.  There are two types of this kind of validation.  One that can be done completely on client side and another that requires a database access or some other information from the server.  I am going to chat about latter first.

I can accomplish this task via Remote attribute.  All I need to do is decorate a property with this attribute to force validation to invoke a controller method when data changes for that property.  This is still accomplished via unobtrusive JavaScript validation. What you need to do is decorate a property you are validating with Remote attribute and specify what method on controller needs to be invoked as well as additional fields to pass to the method and error message to show.  The method just needs to return true or false, where or not the validation passes.  Here is an example of the property decorated with the attributes.

 

    [Remote(

      "DuplicateEmail",

      "Attendee",

      AdditionalFields = "AttendeeID",

      ErrorMessageResourceName = "DuplicateEmail",

      ErrorMessageResourceType = typeof(Resource))]

    public string Email { get; set; }

 

What I am trying to do above is validate to see if the email is a duplicate.  I am passing Attendee ID to the method as well to make sure email is not a duplicate, but I have to filter our current record.  Word attendee is the name of the controller, of course you have to omit word controller out of the call, since this is how MVC is building ajax call.  Here is what my controller method looks like

    public JsonResult DuplicateEmail(string email, int? attendeeID)

    {

      int id = attendeeID.HasValue ? attendeeID.Value : 0;

      return Json(!db.Select<Attendee>()

        .Where(one => one.Email == email && one.AttendeeID != id).Any(),

        JsonRequestBehavior.AllowGet);

    }

 

 

The query using Entity Framework Code First is very simple and does not need much explanation.  What I notice is that my parameters are matched based on names and automatically parsed for me.  Attendee ID is nullable because for brand new attendee entry is not populated yet.  Here is code from my view to show why.

    @if (Model.AttendeeID > 0)

    {

      @Html.HiddenFor(model => model.AttendeeID)

    }

 

I am only dumping ID for existing rows, i.e. for editing, not new attendee creation.  I also use this fact to conditionally edit or create new row.  You can see that in the JavaScript method on top if this post.  All this data gets translated into unobtrusive JavaScript validation calls on the client.  In the next post I will talk about the other side of custom client validation based on IClientValidatable interface.

Thanks.

Post to Twitter

Asynchronous Controllers Calls to Save Data in ASP.NET MVC 3

On a few occasions I wondered if it is possible to simulate Silverlight Save behavior in ASP.NET MVC.  What I mean is to asynchronously call a service from an edit form, asynchronously process the results, and update view based on saved data.  This process is a bit more complicated in ASP.NET MVC because we have to process potential errors and update UI to include errors.  Here is the workflow I am trying to implement:

Create new entry screen –> Hit Save –> Submit data to the controller –> Save data –> Return updated view –> Update UI based on updated View, including populating newly generated ID for subsequent saves.

Here is my solution outline

  • Create partial view for the form to enter data for an entity, Attendee in my case.
  • Create Controller method to accept Attendee data from UI to save
  • Return new partial view from this method
  • Push new view into DOM.

I am now going to break down my solution.  I am using the following Attendee class as the entity for entry screen.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel.DataAnnotations;

using MvcEFCodeFirst.Resources;

 

namespace MvcEFCodeFirst.Data

{

  public class Attendee

  {

    [Key]

    public int AttendeeID { get; set; }

 

    [StringLength(50,

      ErrorMessageResourceName = "FirstName50Chars",

      ErrorMessageResourceType = typeof(Resource))]

    [Required(

      ErrorMessageResourceName = "FirstNameRequired",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "FirstName", ResourceType = typeof(Resource))]

    public string FirstName { get; set; }

 

    [StringLength(50,

      ErrorMessageResourceName = "LastName50Chars",

      ErrorMessageResourceType = typeof(Resource))]

    [Required(

      ErrorMessageResourceName = "LastNameRequired",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "LastName", ResourceType = typeof(Resource))]

    public string LastName { get; set; }

 

    [StringLength(250)]

    [Required(

     ErrorMessageResourceName = "EmailRequired",

     ErrorMessageResourceType = typeof(Resource))]

    [RegularExpression(

      @"^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$",

      ErrorMessageResourceName = "EmaiInvalidFormat",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "Email", ResourceType = typeof(Resource))]

    public string Email { get; set; }

 

    [StringLength(int.MaxValue)]

    [Display(Name = "Notes", ResourceType = typeof(Resource))]

    public string Notes { get; set; }

 

    public virtual ICollection<Session> Sessions { get; set; }

  }

}

 

Now I am going to use scaffolding to create new controller that is linked to Attendee class:  I am right-clicking on Controllers folder and selecting new Controller item

 

image

The create, display, etc… views have been created.  Now, I am going to extract form screen for Attendee edit into a partial view.  Here is what this wizard screen looks like.

image

 

The view is pretty simple, and here is what it looks like:

@model MvcEFCodeFirst.Data.Attendee

@using (Html.BeginForm(new { Id = "AttendeeForm" }))

{

  @Html.ValidationSummary(true)

  <fieldset>

    <legend>Attendee</legend>

    <div class="editor-label">

      @Html.LabelFor(model => model.FirstName)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.FirstName)

      @Html.ValidationMessageFor(model => model.FirstName)

    </div>

    <div class="editor-label">

      @Html.LabelFor(model => model.LastName)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.LastName)

      @Html.ValidationMessageFor(model => model.LastName)

    </div>

    <div class="editor-label">

      @Html.LabelFor(model => model.Email)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.Email)

      @Html.ValidationMessageFor(model => model.Email)

    </div>

    <div class="editor-label">

      @Html.LabelFor(model => model.Notes)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.Notes)

      @Html.ValidationMessageFor(model => model.Notes)

    </div>

    @if (Model.AttendeeID > 0)

    {

      @Html.HiddenFor(model => model.AttendeeID);

    }

  </fieldset>

}

 

Now, I am going to integrate this new partial view into Create/Edit shell views for Attendee.  I am going to consolidate Create and Edit views into single view.  This is more complicated now, and I am going to document the code below in more details

@if (false)

{   <script src="../../Scripts/jquery-1.5.1-vsdoc.js" type="text/javascript"></script>}

@model MvcEFCodeFirst.Data.Attendee

@{

  Layout = "~/Views/Shared/_Layout.cshtml";

}

<h2>

  @ViewBag.Title</h2>

<div id="formDiv">

  @{Html.RenderPartial("AttendeePartial", Model);}

</div>

<div>

  <button id="saveNewAttendee">

    @ViewBag.ButtonText

  </button>

</div>

<div>

  @Html.ActionLink("Back to List", "Index")

</div>

<script type="text/javascript">

  $().ready(function () {

    $(‘#saveNewAttendee’).click(function () {

      $.blockUI({ message: ‘<h1>Updating…</h1>’ });

      $.ajax({

        type: "POST",

        url: "./CreateUpdateAjax",

        data: $(‘form’).first().serialize(),

        success: function (data, status, xhr) {

          $(‘#formDiv’).html(data);

          if ($(‘#AttendeeID’).val() != undefined) {

            $(‘#saveNewAttendee’).html(‘Save’);

          };

          $.unblockUI();

        },

        error: function (xhr, status, error) {

          $.unblockUI();

          alert("error");

        }

      });

    })

 

  })

 

</script>

 

 

The top of the file is imply there to get IntelliSense for jQuery.  The next interesting code is RenderParital call.  This is only used during initial call to the view.  The form that is used for data entry is within partial view.  The last portion is javascript that is used to post the form.  First of all I need to block UI while the save is proceeding.  I downloaded and am using blockUI jQuery extension. You can download it here.  Next, I am using ajax method to submit the form data.  I am using convenient serialize method to serialize the form’s fields to be sent to the server.  I am using POST method to submit the data.  I am also specifying error and success functions.  In success function I have to first check to see if the save succeeded to create new Attendee.  I am checking this by inspecting a hidden field for Attendee ID that should have been inserted after successful save.  Then, I unblock UI.

Now, let’s see what the controller method CreateUpdateAjax looks like:

    [HttpPost]

    public ActionResult CreateUpdateAjax(Attendee attendee)

    {

      if (ModelState.IsValid)

      {

        if (attendee.AttendeeID == 0)

        {

          db.Insert(attendee);

        }

        else

        {

          db.SetModified(attendee);

          db.Save();

        }

      }

      return PartialView("AttendeePartial", attendee);

    }

 

This method is very simple.  I am relying on MVC to convert form data magically into an Attendee object.  Then I am checking for errors.  If everything succeeds, I am inserting or updating the attendee using my repository class, then returning a partial view.  I am skipping repository for now, you can read about it in my previous posts.  Just search for repository in top right corner of the blog.

In conclusion as you can see it does not take much more code to simulate Silverlight behavior in ASP.NET MVC.  Of course, if you try to create a one-page MVC application, you will find that you will need to write a lot more code.  Then again, do your users expect a single page web application?  Anyway, my goal in this post was to show how to do asynchronous saves, and I thinks this approach works pretty well.

Thanks.

Post to Twitter

More on Asynchronous Processing in ASP.NET MVC 3

In this post I would like to cover a couple of asynchronous patterns that are available in ASP.NET MVC and jQuery as it is used in MVC.

In all my previous examples related to MVC 3.0 I used regular controller class that is automatically created for a developer when New Item –> Controller is chosen from right-click menu on Controller folder in MVC application project.  Now, there is another type of controller that is available in MVC that is not often used –  AsyncControllerThis controller allows you to break your controller actions into two parts – Begin and End methods used in a typical asynchronous patter.  Just like a lot of other functionality in MVC, this pattern follows the same naming conventions.  For example, if you would like to break “Index” controller action into two methods – begin and end, you would end up with IndexAsync and IndexCompleted methods.  As a result, if you navigate to Index url, the former method will automatically be invoked, and when it flags action as completed, IndexCompleted method will be invoked.  Here is code the illustrates this point, calling database, and more specifically EF Code First, asynchronously.

  public class AttendeeController : AsyncController

  {

    private CodeStockContext db = new CodeStockContext();

 

    //

    // GET: /Attendee/

 

    public void IndexAsync()

    {

      AsyncManager.OutstandingOperations.Increment();

      BackgroundWorker worker = new BackgroundWorker();

      worker.DoWork += (o, e) =>

      {

        using (var context = new CodeStockContext())

        {

          AsyncManager.Parameters["attendees"] = context.Attendees.OrderBy(_ => _.LastName).ThenBy(_ => _.FirstName).ToList();

        }

      };

      worker.RunWorkerCompleted += (o, e) => { AsyncManager.OutstandingOperations.Decrement(); };

      worker.RunWorkerAsync();

 

    }

 

 

    public ViewResult IndexCompleted(IEnumerable<Attendee> attendees)

    {

      return View(attendees);

    }

 

As you can see, there is also parameter matching going on, where parameters of AsyncManager are automatically matched to parameters to Completed method.  The only other interesting point, is Increment and Decrement methods of AsyncManager that keep track of pending operations.  If you have multiple operations, you call increment with a parameter corresponding to a number of asynchronous operations.

Why use this pattern?  It has something to do with how IIS processes requests.  When a request comes in, IIS grabs a worker thread from the pool and processes incoming request on this thread.  If you have too many requests, IIS may run out of available threads.  So, if you have a long running IO bound operation, you can dispatch it on a background thread, thus releasing worker thread back into the IIS pool until it is needed, increasing throughput of your web site.  You can read more on this subject here.

Now, there is one more use for asynchronous processing in MVC.  I would like to demonstrate how to use ajax methods on jQuery.  You can use ajax method or post/get methods.  In my case, I am going to demonstrate how to use post method to submit a form’s data to a controller’s method without postback.  Why do this?  At the expense of complexity, you can develop a user interface very similar to windows forms interface.  So, I would likely not go this route without specific user requirements. 

So, now back to the code.  Here is what my view would look for Create action for a class called Attendee.  Frist, let me show you Attendee class:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel.DataAnnotations;

using MvcEFCodeFirst.Resources;

 

namespace MvcEFCodeFirst.Data

{

  public class Attendee

  {

    [Key]

    public int AttendeeID { get; set; }

 

    [StringLength(50,

      ErrorMessageResourceName = "FirstName50Chars",

      ErrorMessageResourceType = typeof(Resource))]

    [Required(

      ErrorMessageResourceName = "FirstNameRequired",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "FirstName", ResourceType = typeof(Resource))]

    public string FirstName { get; set; }

 

    [StringLength(50,

      ErrorMessageResourceName = "LastName50Chars",

      ErrorMessageResourceType = typeof(Resource))]

    [Required(

      ErrorMessageResourceName = "LastNameRequired",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "LastName", ResourceType = typeof(Resource))]

    public string LastName { get; set; }

 

    [StringLength(250)]

    [Required(

     ErrorMessageResourceName = "EmailRequired",

     ErrorMessageResourceType = typeof(Resource))]

    [RegularExpression(

      @"^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$",

      ErrorMessageResourceName = "EmaiInvalidFormat",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "Email", ResourceType = typeof(Resource))]

    public string Email { get; set; }

 

    [StringLength(int.MaxValue)]

    [Display(Name = "Notes", ResourceType = typeof(Resource))]

    public string Notes { get; set; }

 

    public virtual ICollection<Session> Sessions { get; set; }

  }

}

 

Now, in my view I have regular Html form, but without submit input element:

 

@model MvcEFCodeFirst.Data.Attendee

@{

  ViewBag.Title = "Create";

  Layout = "~/Views/Shared/_Layout.cshtml";

}

<h2>

  Create</h2>

@using (Html.BeginForm())

{

  @Html.ValidationSummary(true)

  <fieldset>

    <legend>Attendee</legend>

    <div class="editor-label">

      @Html.LabelFor(model => model.FirstName)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.FirstName)

      @Html.ValidationMessageFor(model => model.FirstName)

    </div>

    <div class="editor-label">

      @Html.LabelFor(model => model.LastName)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.LastName)

      @Html.ValidationMessageFor(model => model.LastName)

    </div>

    <div class="editor-label">

      @Html.LabelFor(model => model.Email)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.Email)

      @Html.ValidationMessageFor(model => model.Email)

    </div>

    <div class="editor-label">

      @Html.LabelFor(model => model.Notes)

    </div>

    <div class="editor-field">

      @Html.EditorFor(model => model.Notes)

      @Html.ValidationMessageFor(model => model.Notes)

    </div>

  </fieldset>

}

<div>

  <button id="saveNewAttendee">

    Create</button>

</div>

<div>

  @Html.ActionLink("Back to List", "Index")

</div>

<script type="text/javascript">

  $().ready(function () {

    $(‘#saveNewAttendee’).click(function () {

      $.post(‘./CreateAjax’, $(‘form’).first().serialize(), function (o) {

        alert(o);

        if (o === ‘OK’)

          window.location.href = ‘./Index’;

      });

 

    })

  })

</script>

 

Now, instead of submit button, you will see a regular button with ID of saveNewAttendee.  In jQuery ready method, I am attaching a click event handler to this button, in which I am simulating the submit method.  I am calling CreateAjax method on my controller, and I am passing data array created by serializing data collected in the form.  All this is available in jQuery.  I am also passing in function that will be fired when method completes.  Here is the code for the controller method:

    [HttpPost]

    public string CreateAjax(Attendee attendee)

    {

      if (ModelState.IsValid)

      {

        db.Attendees.Add(attendee);

        db.SaveChanges();

        return "OK";

      }

 

      return string.Concat(

        ModelState.SelectMany((state) => state.Value.Errors

          .Select(e => e.ErrorMessage + Environment.NewLine)).ToArray());

    }

 

The code is pretty simple.  I am checking to make sure the model is valid, and if it is, I am saving new attendee.  Otherwise, I am create a string containing all error messages using Linq.  ModelState class contains all the validation data you could possibly need to parse the errors related to validation of your model.  It uses attributes I placed on my Attendee class to do so.  As I mentioned before, attributes are used for UI validation as well as database design when you use EF Code First with ASP.NET MVC, which is pretty cool.  If data is saved, I am returning “OK.”  As you can see in my javascript above, I am looking for string “OK” and redirecting to the Index action/Url – list of attendees.  Alternatively, I could stay on the same page.  There is a lot of room for clean up on this page, of course.  I could add some animations to play during submit, disable all the controls, save newly created ID into a hidden field for subsequent updates, show error messages, maybe even replace entire Html of the form, replacing OK string with something like return View(attendee) or RenderPartial call.

Thank you.

Post to Twitter

Localizing / Customizing Entity Framework and ASP.NET MVC 3

In most demos that you see that involve entity framework code first and ASP.NET MVC 3, localization is not mentioned.  What if you want to make your data multi-language friendly?  Here is a simple example: I want to have title column to be at most 30 characters in my entity:

    [StringLength(30)]

    public string Title { get; set; }

 

If you run the form with this configuration and you enter more than 30 characters, you will get the following message: The field Title must be a string with a maximum length of 30.  This may be OK for some folks, but it does not follow proper English grammar.  So, let’s fix the problem.  First thing we need is create a resource file.  I typically put resources into its own assembly, so I am going to create new class library project, add new item of type Resource, and create new string key Title30Characters and appropriate value:

image

Now, let’s customize the attribute in the following manner:

    [StringLength(30,

      ErrorMessageResourceName = "Title30Characters",

      ErrorMessageResourceType = typeof(Resource))]

 

That is it.  One important thing to notice is that most attributes in Data Annotations namespace have the same two parameters used for localization: ErrorMessageResourceName and ErrorMessageResourceType.

 

    [StringLength(30,

      ErrorMessageResourceName = "Title30Characters",

      ErrorMessageResourceType = typeof(Resource))]

    [Required(

      ErrorMessageResourceName = "TitleRequired",

      ErrorMessageResourceType = typeof(Resource))]

    [Display(Name = "Title", ResourceType = typeof(Resource))]

    public string Title { get; set; }

 

Above you see the final version of the Title field attributes.  I am specifying maximum length, required field and display name, all coming from resources, specifically Resource class.  One interesting thing to notice is that I am using the same attributes to define messages in ASP.NET MVC and database structure.  Database does not care about messages I am adding to StringLength attribute, but it is using the value of 30 to define the size of the field.

Now, here is one more use case:  I want to handle parsing errors.  For example I have a Session Date field that maps to date time picker control in jQuery.  If I enter the following text 112121, here is what message will be generated:  The value ’112121′ is not valid for Session Date.  Not friendly.  Here is how I can also customize all error messages in ASP.NET MVC.  I can do it in Controller (Create or Update) method.  In the example below I am using Create method, testing for errors, and putting in custom error for Session Date property:

 

    public ActionResult Create()

    {

      var view = View(new Session());

      view.ViewBag.Action = "Create";

      return view;

    }

 

   

    [HttpPost]

    public ActionResult Create(Session session)

    {

      if (ModelState.IsValid)

      {

        db.Sessions.Add(session);

        db.SaveChanges();

        return RedirectToAction("Index");

      }

      else

      {

        if (ModelState["SessionDate"].Errors.Count == 1)

        {

          string date = ModelState["SessionDate"].Value.AttemptedValue;

          DateTime test;

          if (!DateTime.TryParse(date, out test))

          {

            ModelState["SessionDate"].Errors.Clear();

            ModelState["SessionDate"].Errors.Add(new ModelError(Resource.DateInvalidInvalidFormat));

          }

        }

      }

      var view = View(session);

      view.ViewBag.Action = "Create";

      return view;

    }

 

As you can see above, I am checking for errors in ModelState, removing default error only if my parsing fails on proposed value, and putting in custom message from resources.

 

Alternatively, you can use “buddy classes” or MetadataType attribute to define your metadata in a “buddy” class.  I personally do not like this approach, it feels awkward to me.

That is all there is to it.

Thanks.

Post to Twitter

Developing ASP.NET MVC 3 Application with EF CTP (Part 5)

In this post I will describe how to use MVC with a jquery grid control to display tabular data.  To start with, there is a number of grids available, some even integrate with ASP.NET MVC by providing an MVC assembly that generates the javascript for the grid.  One of these components in jqGrid.  I am not going to use MVC helper for this demo primarily because MVC component is not a free download.  As a result, I am going to just download jqGrid itself from this site.  One of the files in download is a text file that describes what script and CSS files you need to include in your project.  I am going to just include a screenshots of my project.  I am only using one theme – swanky-purse.

image

 

image

Now, we need to incorporate those scripts into my master page (_Layout.cshtml) so that I do not have to download them in each view:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<
html xmlns
="http://www.w3.org/1999/xhtml">
<
head>
    <title>@View.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.1.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
    <link href="@Url.Content("~/Content/swanky-purse/jquery-ui-1.8.6.custom.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/ui.jqgrid.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery.ui.core.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.ui.datepicker.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/grid.locale-en.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.jqGrid.min.js")" type="text/javascript"></script
>
</
head
>
<
body>
    <div class="page">
        <div id="header">
            <div id="title">
                <h1>
                    My MVC Application</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial"
)
           
</div>
            <div id="menucontainer">
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Blogs", "Index", "Blogs")</li>
                </ul>
            </div>
        </div>
        <div id="main">
            @RenderBody()
           
<div id="footer">
            </div>
        </div>
    </div
>
</
body
>
</
html>
 

 

Now, we are going to plug in query grid script into the index page that shows blog entries – Blogs\Index.cshtml.  Here is the entire cshtml file:

@{
    View.Title = "Blogs";
    Layout =
"~/Views/Shared/_Layout.cshtml"
;
}

<h2>
    Index</h2
>
<
p>
    @Html.ActionLink("Create New", "Create")
</p
>
<
table id="blogsList" class="scroll" cellpadding="0" cellspacing
="0">
</
table
>
<
div id="blogsPager" class="scroll" style="text-align
: center;">
</
div>
 
<script type="text/javascript">
    $().ready(function
() {
        $(
"#blogsList"
).jqGrid({
            url:
‘./Blogs/BlogsGridData/’
,
            datatype:
‘json’
,
            mtype:
‘GET’
,
            colNames: [
'', '', 'Title', 'Posted On'
],
            colModel: [
            { name:
'Edit', index: 'Edit', width: 80, align: 'left', sortable: false
},
            { name:
'Delete', index: 'Delete', width: 80, align: 'left', sortable: false
},
            { name:
'Title', index: 'Title', width: 300, align: 'left'
},
            { name:
'PostedOn', index: 'PostedOn', width: 100, align: 'left'
}],
            pager: $(
‘#blogsPager’
),
            rowNum: 2,
            rowList: [2, 5, 10, 20, 50],
            sortname:
‘PostedOn’
,
            sortorder:
"desc"
,
            viewrecords:
true
,
            caption:
‘Blogs’
        });
    });

</script
>

 

Let me describe the most interesting settings here.  Url is the most notable one.  It will be used to return json data for the grid.  I know I need json because my datatype is set as such.  The actual Url will be intercepted by the routing in ASP.NET MVC to my Blogs controller’s BlogGridData method.  I will show it momentarily.  I will have 4 columns (colNames) – two without headers.  Those two columns with contain edit and delete links (anchors).  The colModel property describes the layout of the columns.  Pager property simply points to a div that will be formatted as pager control for my grid.  I also specify default sort column name and direction.  The entire grid will be attached to my table with the name of blogist (see HTML portion of the cshtml file above).

Next, let me show you what my BlogGridData looks like.  I will start with the code for entire method:

        /// <summary>
        /// Get Json data for the grid
        /// </summary>
        /// <param name="sidx">Column to osrt on.  Do not change parameter name</param>
        /// <param name="sord">Sort direction.  Do not change parameter name</param>
        /// <param name="page">Current page.  Do not change parameter name</param>
        /// <param name="rows">Number of rows to get.  Do not change parameter name</param>
        /// <returns></returns>
        public ActionResult BlogsGridData(string sidx, string sord, int? page, int
? rows)
        {
           
using (BlogContext context = new BlogContext
())
            {
               
int
pageSize = rows ?? 2;
               
int
totalRecords = context.Entries.Count();
               
int totalPages = (int)Math.Ceiling((float)totalRecords / (float
)pageSize);
               
int
currentPage = page ?? 1;
               
string sortByColumnName = sidx ?? "PostedOn"
;
               
string sortDirection = sord ?? "desc"
;
 
               
var blogs = (from one in
context.Entries
                            
select
one);
               
if (sortByColumnName == "PostedOn"
)
                {
                   
if (sortDirection.Equals("desc"
))
                        blogs = blogs.OrderByDescending(blog => blog.PostedOn);
                   
else
                        blogs = blogs.OrderBy(blog => blog.PostedOn);
                }
               
else
                {
                   
if ("desc"
.Equals(sortDirection))
                        blogs = blogs.OrderByDescending(blog => blog.Title);
                   
else
                        blogs = blogs.OrderBy(blog => blog.Title);
                }
               
var
data = blogs.Skip((currentPage – 1) * pageSize).Take(pageSize).ToArray();
 
               
var jsonData = new jqGridJson
()
                {
                    total = totalPages,
                    page = currentPage,
                    records = totalRecords,
                    rows = (
                     
from blog in
data
                     
select new jqGridRowJson
                      {
                          id = blog.BlogEntryId.ToString(),
                          cell =
new string
[] { 
                               
"<a href=’" + string.Format("./Blogs/Edit/{0}", blog.BlogEntryId.ToString()) + "’>Edit</a>"
,
                               
"<a href=’" + string.Format("./Blogs/Delete/{0}", blog.BlogEntryId.ToString()) + "’>Delete</a>"
,
                                blog.Title, 
                                blog.PostedOn.Value.ToShortDateString() }
                      }).ToArray()
                };
 
               
return Json(jsonData, JsonRequestBehavior.AllowGet);
            }
        }

 

As you can see, I am creating Url’s for edit and delete links.  One important thing to remember is that parameters to any method (BlogsGridData) will be automatically parsed from query string, so you cannot change their names.  The names are hard coded in jqGrid.  In the beginning of the method I am parsing out page size, current page, etc…  Them I am selecting the data from my blogs table.  More specifically, I am creating IQueryable<BlogEntry>, which is my blogs variable.  This query actually is not executed until I access results, which is the line in bold. So, there on that line I am constructing classes that will be later serialized using json serializer.  The classes are simply helper classes because jqGrid again expects the result set in certain format.  Here are those two helper classes:

    public class jqGridJson
    {
       
public int total { get; set
; }
       
public int page { get; set
; }
       
public int records { get; set
; }
       
public jqGridRowJson[] rows { get; set; }
    }

    public class jqGridRowJson
    {
       
public string id { get; set
; }
       
public string[] cell { get; set; }
    }

 

At the end, my last line returns JsonResult class.  That is all there is to using jqGrid with MVC 3.0.  One important note is that Url’s that point to Controller methods are relative.  It took me a while to figure out why they are not invoked in IIS, but with in web server in Visual Studio.  Here is the screenshot of the final version running in IIS:

image

Please let me know if you have any questions.  You can download full solution here.

 

Thanks.

Post to Twitter

Developing ASP.NET MVC 3 Application with EF CTP (Part 4)

In this post I will examine a couple of things.  I am going to use jQuery calendar UI functionality to improve user interface.  I will also add client side validation to my input form.

First, let’s add calendar control.  To do so, we have to download jQuery UI from .http://jqueryui.com/

Once this is completed, I will copy jquery.ui.core.js and jquery.ui.datepicker.js into Scripts folder of my application and include them in my project.  I will also copy calendar.gif as image for calendar button.  Next, I will add links to those scripts to my master page – in case of Razor view engine it is _Layout.cshtml.  Here is what it looks like:

<head>
    <title>@View.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.1.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
    <link href="@Url.Content("~/Content/jquery-ui-1.8.6.custom.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery.ui.core.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.ui.datepicker.js")" type="text/javascript"></script>
 
</head
>

 

It appears that if I just drag the script into _Layout.cshtml, then it does not work.  I had to add @Url.Content to make it work.  Now that this is completed, I have to update my partial page for blog entry and update my date textbox to use date picker from jQuery.  Here is what my code looks like now:

            <div class="editor-label">
                @Html.LabelFor(model => model.PostedOn)
           
</div>
            <div class="editor-field">
                @if
(Model.PostedOn.HasValue)
                {
                   
<input type="text" name="PostedOn" id="PostedOn" class="uidatepicker" 
value=@string.Format("{0:MM/dd/yyyy}", Model.PostedOn) />
                }
               
else
                { 
                   
<input type="text" name="PostedOn" id="PostedOn" class="ui-datepicker" />
                }
                @Html.ValidationMessageFor(model => model.PostedOn)
           
</div
>

 

As you can see, the code is pretty simple, but I will explain it in more detail just in case.  Since I am using formatting for the value, I would like to account for blank date.  If I do not do conditional control, my blank date will show up as “/”.  This is pretty ugly, so I am using different input controls – one with formatting, the other without.  Now, I need to call datepicker function  to setup my input control for Posted On date.  I am going to use ready function and add a small script to the bottom of my partial view I am using to edit blog:

<script type="text/javascript">
    $().ready(function
() {
        $(
‘.ui-datepicker’
).datepicker({
            dateFormat:
‘mm/dd/yy’
,
            buttonImage:
@Url.Content("~/Content/calendar.gif")
,
            buttonImageOnly:
true
,
            showOn:
"button"
        });
    });

</script
>

 

A few configuration options for date picker as pretty simple.  I am using month/day/year format, I am using the image I got from jQuery, and I showing image only, not the button and image on it.  That is all there is to it.  Now when I click on calendar image, I get calendar popup.

 

Next step is to get client validation to work.  I need a couple of scripts I am including in the code above: jquery, jquery.validate, jquery.validate.unobtrusive.  Next step is to simply enable validation on my view by calling EnableCleintValidation as in below (in bold):

@model MvcSampleApp.Models.BlogEntry
@{Html.EnableClientValidation();}
@
using
(Html.BeginForm())
{
@Html.ValidationSummary(
true
)
 
       
<fieldset
>

Here is the final version of my view:

@model MvcSampleApp.Models.BlogEntry
@{Html.EnableClientValidation();}
@
using (Html.BeginForm())
{
@Html.ValidationSummary(
true
)
 
       
<fieldset>
            <legend>Blog Post</legend>
            <div class="editor-label">
                @Html.LabelFor(model => model.Title)
           
</div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.Title) 
                @Html.ValidationMessageFor(model => model.Title)
           
</div>
            <div class="editor-label">
                @Html.LabelFor(model => model.Title)
           
</div>
            <div class="editor-field">
                @Html.DropDownListFor(model => model.CategoryID, new SelectList(Model.Categories, "CategoryId", "CategoryName", Model.CategoryID), "– Select Category –"
)
                @Html.ValidationMessageFor(model => model.CategoryID)
           
</div>
            <div class="editor-label">
                @Html.LabelFor(model => model.PostText)
           
</div>
            <div class="editor-field">
                @Html.TextAreaFor(model => model.PostText) 
                @Html.ValidationMessageFor(model => model.PostText)
           
</div>
            <div class="editor-label">
                @Html.LabelFor(model => model.PostedOn)
           
</div>
            <div class="editor-field">
                @if
(Model.PostedOn.HasValue)
                {
                   
<input type="text" name="PostedOn" id="PostedOn" class="ui-datepicker" value=@string.Format("{0:MM/dd/yyyy}", Model.PostedOn) />
                }
               
else
                { 
                   
<input type="text" name="PostedOn" id="PostedOn" class="ui-datepicker" />
                }
                @Html.ValidationMessageFor(model => model.PostedOn)
           
</div>
            <div>
                <input type="submit" value=@TempData["Action"] />
            </div>
            @Html.Hidden("BlogEntryId"
)
       
</fieldset>
 
}
 

<script type="text/javascript">
    $().ready(function
() {
        $(
‘.ui-datepicker’
).datepicker({
            dateFormat:
‘mm/dd/yy’
,
            buttonImage:
@Url.Content("~/Content/calendar.gif")
,
            buttonImageOnly:
true
,
            showOn:
"button"
        });
    });

</script
>

 

You can see that validation works by typing one letter into title and tabbing out. You will see immediate error without post back.  This is because I have a rule on my title:

        [Display(Name = "Title")]
        [
StringLength
(30, MinimumLength = 5)]
        [
Required
]
       
public string Title { get; set; }

 

image

And that is all there is to it.

Thanks.

Post to Twitter

Developing ASP.NET MVC 3 Application with EF CTP (Part 3)

Today I will concentrate on refactoring existing code and adding other links to my application, such as delete and edit links.

First things first, let’s do a bit of refactoring in advance of adding more views.  As I noticed, edit, delete and create views are the same with the exception of title for the submit button,  So, I am going to create a partial view that encompasses basic edit functionality along with submit button.  I am going to extract the title into a variable using TempData dictionary off the ViewResult object.  For example, here is how I am going to put “Delete” label into the view result:

view.TempData.Add("Action", "Delete");

 

I will use the same Action key in create and add methods as well.  Here is what code in my controller look like for the entire delete method:

        public ActionResult Delete(int id)
        {
           
BlogEntry entry = null
;
           
using (BlogContext context = new BlogContext
())
            {
                entry = context.Entries.Find(id);
                entry.CategoryID = entry.Category.CategoryId;
                entry.Categories = GetCaregories();
            }
           
var
view = View(entry);
            view.TempData.Add(
"Action", "Delete"
);
           
return view;
        }

 

Now I am going to refactor the view itself.  I am creating new view following the same routing as in part 2 of the post, but I will check Partial View checkbox.  Here is what my partial view (CreatePartial.cshtml) looks like:

@model MvcSampleApp.Models.BlogEntry
 
  @using (Html.BeginForm()) {
        @Html.ValidationSummary(true)
 
        <fieldset>
            <legend>Blog Post</legend>
            
            <div class="editor-label">
                @Html.LabelFor(model => model.Title)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
 
                          <div class="editor-label">
                @Html.LabelFor(model => model.Title)
            </div>
            <div class="editor-field">
                @Html.DropDownListFor(model=>model.CategoryID, 
new SelectList(Model.Categories, "CategoryId", "CategoryName", Model.CategoryID), 
"– Select Category –")
                @Html.ValidationMessageFor(model => model.CategoryID)
            </div>
            
            <div class="editor-label">
                @Html.LabelFor(model => model.PostText)
            </div>
            <div class="editor-field">
                @Html.TextAreaFor(model => model.PostText)
                @Html.ValidationMessageFor(model => model.PostText)
            </div>
            
            <div class="editor-label">
                @Html.LabelFor(model => model.PostedOn)
            </div>
            <div class="editor-field">
                @Html.TextBox("PostedOn", string.Format("{0:MM/dd/yyyy}", Model.PostedOn))
                @Html.ValidationMessageFor(model => model.PostedOn)
            </div>
            <div>
                  <input type="submit" value=@TempData["Action"] />
             </div>
             @Html.Hidden("BlogEntryId")
        </fieldset>
 
    }

 

Now, our old Create view looks as follows:

@model MvcSampleApp.Models.BlogEntry
 
@{
    View.Title = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
<h2>Create</h2>
@{Html.RenderPartial("CreatePartial", Model);}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
 
 

 

Super simple.  Delete and Edit views look identical with exception of the title.  Very clean end result for all my refactoring efforts.  Here is the final code for my controller:

using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web.Mvc;
using
MvcSampleApp.Models;
 

namespace
MvcSampleApp.Controllers
{
   
public class BlogsController : Controller
    {
       
//
        // GET: /Blogs/
 
       
public ActionResult
Index()
        {
           
using (BlogContext context = new BlogContext
())
            {
                context.Database.Connection.ConnectionString =
                   
ConfigurationManager.ConnectionStrings["BlogsConnectionString"
].ConnectionString;
               
var blogs = (from one in
context.Entries
                            
orderby one.PostedOn descending
                             select
one).ToList();
               
return
View(blogs);
            }
        }
 
       
//
        // GET: /Blogs/Create
 
 
       
public ActionResult
Create()
        {
           
IEnumerable<BlogCategory
> categories = GetCaregories();
           
var view = View(new BlogEntry
() { Categories = categories });
            view.TempData.Add(
"Action", "Create"
);
           
return
view;
        }
 
       
private static IEnumerable<BlogCategory
> GetCaregories()
        {
           
IEnumerable<BlogCategory
> categories;
           
using (BlogContext context = new BlogContext
())
            {
                categories = (
from one in
context.Categories
                             
orderby
one.CategoryName
                             
select
one).ToList();
            }
           
return
categories;
        }
 
       
private BlogCategory GetCategory(int
categoryID)
        {
           
using (BlogContext context = new BlogContext
())
            {
               
return
context.Categories.Find(categoryID);
            }
        }
 
       
//
        // POST: /Blogs/Create
 
        [
HttpPost
]
       
public ActionResult Create(BlogEntry
entry)
        {
 
           
try
            {
               
if
(entry.CategoryID > 0)
                {
                    entry.Category = GetCategory(entry.CategoryID);
                }
               
if
(TryValidateModel(entry))
                {
                   
using (BlogContext context = new BlogContext
())
                    {
                        context.Entries.Add(entry);
                        context.SetAsUnchanged(entry.Category);
                        context.SaveChanges();
                    }
 
                   
return RedirectToAction("Index"
);
                }
               
else
                {
                    entry.Categories = GetCaregories();
                   
return
View(entry);
                }
            }
           
catch
            {
               
return
View();
 
            }
        }
 
       
//
        // GET: /Blogs/Edit/5
 
       
public ActionResult Edit(int
id)
        {
           
BlogEntry entry = null
;
           
using (BlogContext context = new BlogContext
())
            {
                entry = context.Entries.Find(id);
                entry.CategoryID = entry.Category.CategoryId;
                entry.Categories = GetCaregories();
            }
           
var
view = View(entry);
            view.TempData.Add(
"Action", "Update"
);
           
return
view;
        }
 
       
//
        // POST: /Blogs/Edit/5
 
        [
HttpPost
]
       
public ActionResult Edit(int id, BlogEntry
entry)
        {
           
try
            {
               
if
(entry.CategoryID > 0)
                {
                    entry.Category = GetCategory(entry.CategoryID);
                }
               
if
(TryValidateModel(entry))
                {
                    entry.BlogEntryId = id;
                   
using (BlogContext context = new BlogContext
())
                    {
                        context.Entries.Attach(entry);
                        context.SetAsModified(entry);
                        context.SaveChanges();
                    }
 
                   
return RedirectToAction("Index"
);
                }
               
else
                {
                    entry.Categories = GetCaregories();
                   
return
View(entry);
                }
            }
           
catch
            {
               
return
View();
 
            }
        }
 
       
//
        // GET: /Blogs/Delete/5
 
       
public ActionResult Delete(int
id)
        {
           
BlogEntry entry = null
;
           
using (BlogContext context = new BlogContext
())
            {
                entry = context.Entries.Find(id);
                entry.CategoryID = entry.Category.CategoryId;
                entry.Categories = GetCaregories();
            }
           
var
view = View(entry);
            view.TempData.Add(
"Action", "Delete"
);
           
return
view;
        }
 
       
//
        // POST: /Blogs/Delete/5
 
        [
HttpPost
]
       
public ActionResult Delete(int id, BlogEntry
entry)
        {
          
try
            {
               
if
(entry.CategoryID > 0)
                {
                    entry.Category = GetCategory(entry.CategoryID);
                }
               
if
(TryValidateModel(entry))
                {
                    entry.BlogEntryId = id;
                   
using (BlogContext context = new BlogContext
())
                    {
                        context.Entries.Attach(entry);
                        context.Entries.Remove(entry);
                        context.SaveChanges();
                    }
 
                   
return RedirectToAction("Index"
);
                }
               
else
                {
                    entry.Categories = GetCaregories();
                   
return
View(entry);
                }
            }
           
catch
            {
               
return View();
 
            }
        }
    }
}

 

A couple more things to notice.  By default foreign keys are created with Cascade Deletes option.  This is not appropriate behavior for my use case.  I am changing this behavior in OnModelCreating method of my context.  Here is the final version of my context class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Configuration;
using
System.Data.Entity.ModelConfiguration;
 

namespace
MvcSampleApp.Models
{
   
public class BlogContext : DbContext
    {
       
public
BlogContext()
        {
           
this
.Database.Connection.ConnectionString =
                   
ConfigurationManager.ConnectionStrings["BlogsConnectionString"
]
                    .ConnectionString;
        }
       
public DbSet<BlogCategory> Categories { get; set
; }
       
public DbSet<BlogEntry> Entries { get; set
; }
 
       
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder
modelBuilder)
        {
           
base
.OnModelCreating(modelBuilder);
 
            modelBuilder.Entity<
BlogEntry
>().HasKey(a => a.BlogEntryId);
            modelBuilder.Entity<
BlogCategory
>().HasKey(a => a.CategoryId);
            modelBuilder.Entity<
BlogEntry>().HasRequired(e => e.Category).WithMany().WillCascadeOnDelete(false
);
        }
 
       
public void SetAsModified(object
entry)
        {
           
this.ObjectContext.ObjectStateManager.ChangeObjectState(entry, System.Data.EntityState
.Modified);
        }
 
       
public void SetAsUnchanged(object
entry)
        {
           
this.ObjectContext.ObjectStateManager.ChangeObjectState(entry, System.Data.EntityState.Unchanged);
        }
    }
}

 

I am using helper methods to SetAs… to ensure that I am not creating duplicate categories.  You can see how I am using these helper methods above in controller class.

You can download entire solution here.

Post to Twitter

Developing ASP.NET MVC 3 Application with EF CTP (Part 2)

First thing we want to do is create a controller for blog entries.  To do so, we will right-click on Controllers folder, select Add, then Controller.

image

On next screen we give it a name (BlogsController) and check the checkbox to create CRUD operations:

image

First method we are going to fill is Index method that will return the list of blog entries.  We will not use EF to return the list of blog posts sorted in ascending order by date.  We are not going to implement paging for now, we will come back to do this later.  Here is completed Index method(Index is just a naming convention)

        public ActionResult Index()
        {
           
using (BlogContext context = new BlogContext
())
            {
                context.Database.Connection.ConnectionString = 
                   
ConfigurationManager.ConnectionStrings["BlogsConnectionString"
].ConnectionString;
               
var blogs = (from one in
context.Entries
                            
orderby one.PostedOn descending
                             select
one).ToList();
               
return View(blogs);
            }
        }

 

As you can see the code is pretty simple.  Next step is to create index view. To do this right-click in the body of the method above and select Add View

image

Below you can see the options I selected.  We create strongly typed view based on BlogEntry class.  We use Razor (new in MVC 3.) engine and select list as view type.  Once we click Add, the IDE will scaffold the object and build the HTML view for us – Index.cshtml.  This new view will be added to Blogs folder.  Again we are using naming conventions to match controller and view

 

image

We are going to clean up the view by removing ID and text columns from the table.  We also update links to use appropriate ID from the object.  Here is final version of our view:

 

@model IEnumerable<MvcSampleApp.Models.BlogEntry>
 
@{
    View.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
    <h2>Index</h2>
 
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
 
    <table>
        <tr>
            <th></th>
            <th>
                Title
            </th>
            <th>
                PostedOn
            </th>
        </tr>
 
    @foreach (var item in Model) {
    
        <tr>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.BlogEntryId }) |
                @Html.ActionLink("Details", "Details", new { id=item.BlogEntryId }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.BlogEntryId })
            </td>
            <td>
                @item.Title
            </td>
            <td>
                @String.Format("{0:g}", item.PostedOn)
            </td>
        </tr>
    
    }
 
    </table>
 
 

 

To make this work, I had to update by Blog Entry class.  I had to add category ID in order to bind to it in the UI.  Question now is: looks like I need model objects on top of EF classes.  This seems counter-productive to me as I would have to duplicate properties.  I think the goal would be to use composition or inheritance to have model derive or expose EF class.  I will work on refactoring efforts right after the app is working.

    public class BlogEntry
    {
        [
Key
]
       
public int BlogEntryId { get; private set
; }
 
        [
Display(Name = "Title"
)]
        [
StringLength
(30, MinimumLength = 5)]
        [
Required
]
       
public string Title { get; set
; }
 
        [
Display(Name = "Post Text"
)]
        [
StringLength(int
.MaxValue)]
        [
Required
]
       
public string PostText { get; set
; }
 
        [
Display(Name = "Category"
)]
       
public virtual BlogCategory Category { get; set
; }
 
        [
Display(Name = "Post Date"
)]
        [
Range(typeof(DateTime), "1/1/1900", "12/31/2099"
)]
        [
Required
]
       
public DateTime? PostedOn { get; set
; }
 
       
public IEnumerable<BlogCategory> Categories { get; set
; }
 
        [
Required
]
        [
Display(Name = "Category"
)]
       
public int CategoryID { get; set; }
    }

 

In Razor syntax @ symbol indicates C# code, where < symbol indicates HTML tags.  As you can see on top we define model for the view, which in our case is the argument passed into the View in the Index method of the controller.  We also need to add a link to the menu for blog posts.  We add it to the _Layout.cshtml view:


                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Blogs", "Index", "Blogs")</li>
                </ul>

 

Next step is to create new post view.  We also need to add code to Create method of the new view.  Here is how Create method looks:

        public ActionResult Create()
        {
           
return View(new BlogEntry());
        }

 

Now, same step as above – right-click, add view, select Create option from View Content drop down.  Then I will remove ID field.  Here is the end result of that.

@model MvcSampleApp.Models.BlogEntry
 
@{
    View.Title = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
<h2>Create</h2>
 
    @using (Html.BeginForm()) {
        @Html.ValidationSummary(true)
 
        <fieldset>
            <legend>Blog Post</legend>
            
            <div class="editor-label">
                @Html.LabelFor(model => model.Title)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
 
                          <div class="editor-label">
                @Html.LabelFor(model => model.Title)
            </div>
            <div class="editor-field">
                @Html.DropDownListFor(model=>model.CategoryID, new SelectList(Model.Categories, "CategoryId", "CategoryName", Model.CategoryID), "– Select Category –")
                @Html.ValidationMessageFor(model => model.Title)
            </div>
            
            <div class="editor-label">
                @Html.LabelFor(model => model.PostText)
            </div>
            <div class="editor-field">
                @Html.TextAreaFor(model => model.PostText)
                @Html.ValidationMessageFor(model => model.PostText)
            </div>
            
            <div class="editor-label">
                @Html.LabelFor(model => model.PostedOn)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.PostedOn)
                @Html.ValidationMessageFor(model => model.PostedOn)
            </div>
            <div>
                                   <input type="submit" value="Create" />
                          </div>
        </fieldset>
 
    }
 
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
 
 

Next step is to create controller method to save new blog post

        [HttpPost]
       
public ActionResult Create(BlogEntry
entry)
        {
 
           
try
            {
               
if
(entry.CategoryID > 0)
                {
                    entry.Category = GetCategory(entry.CategoryID);
                }
               
if
(TryValidateModel(entry))
                {
                   
using (BlogContext context = new BlogContext
())
                    {
                        context.Database.Connection.ConnectionString =
                   
ConfigurationManager.ConnectionStrings["BlogsConnectionString"
]
                    .ConnectionString;
                        context.Entries.Add(entry);
                        context.SaveChanges();
                    }
 
                   
return RedirectToAction("Index"
);
                }
               
else
                {
                    entry.Categories = GetCaregories();
                   
return
View(entry);
                }
            }
           
catch
            {
               
return View();
 
            }
        }

 

There are still parts of the method that can be optimized.  I will look at those once the application is completed.

In the next post I will look at the edit and delete methods.

Post to Twitter

Developing ASP.NET MVC 3 Application with EF CTP (Part 1)

I this post I will walk through steps of creating an ASP.NET MVC 3 application using new code first entity framework CTP as data access and business layer.  Just a few upfront comments.  Both technologies are in preview state right now, but will likely be released in first half of next year.

To get started we have to download and install them.  To make this simpler, I will use NuGet (formerly  NuPack) which is package management software for .NET applications.  You can find it here.  Visual Studio 2010 is required to follow along with the demo, by the way.  You can download ASP.NET MVC 3.0 beta here.

Now that it has been downloaded and installed, let’s create new project.

image

Next step to pick the view engine you want to use.  You can select either ASPX (ASP.NET) or new Razor engine that will ship with MVC 3.0 and new product – Web Matrix.

image

We will go ahead and also select to create Unit Test project.

What the wizard builds for us is empty site with home view and registration service that uses membership provider.  Let’s run the app to see if everything worked correctly.  We can open web.config and verify that the system is using ASP.NET SQL Membership provider for login:

<configuration>
  <
connectionStrings
>
    <
add name=ApplicationServices

         connectionString=data source=.\SQLEXPRESS;Integrated Security=SSPI;
AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true

         providerName=System.Data.SqlClient
/>
  </
connectionStrings>
 
  <appSettings
>
    <
add key=enableSimpleMembership value=false
/>
    <
add key=ClientValidationEnabled value=false
/>
    <
add key=UnobtrusiveJavaScriptEnabled value=true
/>
  </
appSettings>
 
  <system.web
>
 
    <authentication mode=Forms
>
      <
forms loginUrl=~/Account/LogOn timeout=2880
/>
    </
authentication>
 
    <membership
>
      <
providers
>
        <
clear
/>
        <
add name=AspNetSqlMembershipProvider
 
            
type=System.Web.Security.SqlMembershipProvider
 
            
connectionStringName=ApplicationServices
             enablePasswordRetrieval=false
 
            
enablePasswordReset=true
 
            
requiresQuestionAndAnswer=false
 
            
requiresUniqueEmail=false
             maxInvalidPasswordAttempts=5
 
            
minRequiredPasswordLength=6
             minRequiredNonalphanumericCharacters=0
 
            
passwordAttemptWindow=10
             applicationName=/
/>
      </
providers
>
    </
membership
>

Now, let’s add our code.  Since I absolutely lack any imagination, I will create blogging application.  Each blog will contain title, text, posted date and category.  Let’s create entities for blogs and categories using EF code first CTP.  First, let’s install CTP 4 for entity framework.  To do, we will use NuGet functionality.  Right click on references node in solution and select add package, search for EF and Select EFCTP 4:

image

 

image

Now, let’s create our two classes – categories and blog posts.  We will add them to the Model folder. Blog category class is very simple:

    public class BlogCategory
    {
        [
Key
]
       
public int CategoryId { get; private set
; }
 
        [
Display(Name = "Category Name"
)]
       
public string CategoryName { get; internal set; }
 
    }

 

I made blog entries property virtual to enable lazy loading.  Blog entry class is only slightly mode complicated:

    public class BlogEntry
    {
        [
Key
]
       
public int BlogEntryId { get; private set
; }
 
        [
Display(Name = "Title"
)]
        [
StringLength
(30, MinimumLength = 5)]
        [
Required
]
       
public string Title { get; set
; }
 
        [
Display(Name = "Post Text"
)]
        [
StringLength
(30, MinimumLength = 5)]
        [
Required
]
       
public string PostText { get; set
; }
 
        [
Display(Name = "Category"
)]
       
public BlogCategory Category { get; set
; }
 
        [
Display(Name = "Post Date"
)]
        [
Range(typeof(DateTime), "1/1/1900", "12/31/2099"
)]
       
public DateTime? PostedOn { get; set; }
    }

 

As you can see, I am using attributes from DataAnnotations namespace to describe the properties of my classes.  Next class I need is DbContext, which essentially is my database abstraction:

    public class BlogContext : DbContext
    {
       
public DbSet<BlogCategory> Categories { get; set
; }
       
public DbSet<BlogEntry> Entries { get; set
; }
 
       
protected override void OnModelCreating(

          System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
        {
           
base
.OnModelCreating(modelBuilder);
 
            modelBuilder.Entity<
BlogEntry
>().HasKey(a => a.BlogEntryId);
            modelBuilder.Entity<
BlogCategory>().HasKey(a => a.CategoryId);
 
        }
    }

 

 

As you can see I have two properties – for blog entries and categories.  I also modify default configuration of my database by overriding OnModelCreating and specifying my primary key columns and table names.  One more class I would like to add to customize database creation behavior that is very useful for development:

    public class BlogDatabase :

       System.Data.Entity.Infrastructure.RecreateDatabaseIfModelChanges<BlogContext>
    {
       
protected override void Seed(BlogContext
context)
        {
            context.Categories.Add(
new BlogCategory()

             { CategoryName = “General” });
            context.Categories.Add(
new BlogCategory()

             { CategoryName = “Entity Framework” });
            context.Categories.Add(
new BlogCategory()

             { CategoryName = “Silverlight” });
        }
    }

 

This class inherits for recreate database behavior.  I am overriding Seed method to prepopulate my database with a handful of categories.  Now, I have to register my class in global.asax.  I am doing so by setting initializer in static method on database class.

        protected void Application_Start()
        {
           
AreaRegistration
.RegisterAllAreas();
           
Database.SetInitializer<BlogContext>(new BlogDatabase
());
 
            RegisterGlobalFilters(
GlobalFilters
.Filters);
            RegisterRoutes(
RouteTable.Routes);
        }

In the next post I will add some web pages to use these classes. 

Post to Twitter