Ninject with custom attribute and filter

I am implementing a custom credit check filter for a controller action. I use Ninject as my Ioc container. First, create a action filter and implement IActionFilter interface, implement the OnActionExecuted() and OnActionExecuting() methods. Within the credit checking functions, it needs a CreditService to check against the repository and database. So I have created the parameterized constructor using Ninject to inject the CreditService object.

 public class CreditCheckFilter : IActionFilter
    {
        private ICreditService _creditService { get; set; }

        public CreditCheckFilter(ICreditService myService)
        {
            _creditService = myService;
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {

        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userName = filterContext.HttpContext.User.Identity.Name;
            var bal = _creditService.GetTokenBalance(WebSecurity.GetUserId(userName));
            if (bal <= 0)
            {
                RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
                redirectTargetDictionary.Add("action", "GetTokens");
                redirectTargetDictionary.Add("controller", "Account");
                filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
               
            }
        }
    }

Now Let’s create a CreditCheckAttribute that implements FilterAttribute, this is the attribute we put against the controller action,

    public class CreditCheckAttribute : FilterAttribute { }

Now, we need to use Ninject BindFilter() method to bind our CreditCheckFilter, when a controller action has the attribute CreditCheckAttribute. In the ninject kernel, do this,

            kernel.BindFilter(FilterScope.Action, 0).WhenActionMethodHas();

The last step is to put CreditCheckAttribute() against the controller action,

        [CreditCheckAttribute]
        public ActionResult Playlist(TestViewModel model)
        {
            return View(model);
        }

Create a custom HtmlHelper.TextBoxFor() to display label text as “placeholder” integrated with twitter bootstrap

We have TextBoxFor() and LabelFor() in MVC, they are binded with my model. If you are using twitter bootstrap and want to display the model property label as a placeholder in textboxes, what should you do.

Here is what I have done.

My model:

public class User
{
    [Display(Name="User name")]
    public string Username {get;set;}
}

Normally in the view we put,

@model User

<ol>
    <li>
        @Html.LabelFor(p=&gt;p.Username)
        @Html.TextBoxFor(p=&gt;p.Username)
    </li>
</ol>

image

If you want to display it as placeholder in twitter bootstrap,

image

Let’s create a TextBoxPlaceHolderFor() extension method of the htmlhelpers, remember to reference the namespace,

using System.Web.Mvc.Html;

So you can re-use the HtmlHelper.TextBoxFor() in your TextBoxPlaceHolderFor() method.

Here is the code with three overloads,

public static class TextBoxForExtensions
    {
        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression, object htmlAttributes)
        {
            var dict = new RouteValueDictionary(htmlAttributes);
            return html.TextBoxPlaceHolderFor(expression, dict);
        }
        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression)
        {
            var htmlAttributes = new Dictionary();
            return html.TextBoxPlaceHolderFor(expression, htmlAttributes);
        }

        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression, IDictionary htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (!String.IsNullOrEmpty(labelText))
            {
                if (htmlAttributes == null )
                {
                    htmlAttributes = new Dictionary();
                }
                htmlAttributes.Add("placeholder", labelText);
            }
            return html.TextBoxFor(expression, htmlAttributes);
        }

    }

When you call the methods you can do,

@Html.TextBoxPlaceHolderFor(m => m.UserName)

//or 

@Html.TextBoxPlaceHolderFor(m => m.UserName, new { @class="form-field" })


You are done. Now get your form neat and clean using the TextBoxPlaceHolderFor().

ajax request only action filter, MVC

I created a handy ajax request filter, that you can apply to your ajax action method call.

    public class AjaxRequestAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if(!filterContext.HttpContext.Request.IsAjaxRequest())
                	//do something
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {

        }
    }
    [AjaxRequest]
    public ActionResult AjaxActionMethod()
    {
        		//to do
    }

Use Enum & Description to create select list item in MVC

There are always situations that we make good use of enums in projects. I like to use an Enum values to make my select list to save select-list-creation time. Here is what I always do,

Create an Enum, Eg. MonthFilter

 public enum MonthFilter
    { 
        [Description("Last Month")]
        LastMonth = 1,
        [Description("Last 3 Months")]
        Last3Months = 3,
        [Description("Last 6 Months")]
        Last6Months = 6,
        [Description("Last 12 Months")]
        Last12Months = 12,
        [Description("Year on Year")]
        YearOnYear = -1
    }

And then create the SelectListItem using this Enum and its descriptions,

    var monthFilters = from MonthFilter n in Enum.GetValues(typeof(MonthFilter))
                     select new SelectListItem { 
                            Value = ((int)n).ToString(), 
                            Text = Enumerations.GetEnumDescription(n)
                     };

Last step is create the dropdown list, by passing in the “monthFilters” Select List Items,

                @Html.DropDownList("MonthFilterDropDown", monthFilters, "Select...", null)

Handle Ajax request with expired forms authentication, in MVC ajax.form

There is a situation where I display a screen dynamically load a partial view using ajax.

And when the authentication expires, user initiates an ajax call, then the action is being called without authorization.

It actually returns a login view inside the DOM object on the page rather than returns a expected content with partial view.

The users will have bad UI experience and get confused.

Here I demonstrate the scenario to show you what exactly happens and what we can do about it.

1. Create an ajax.form which simply returns a partial view.


@using (Ajax.BeginForm("AjaxTestNoHandle", "Home", new AjaxOptions { UpdateTargetId = "testcontainer2"}, new { id="test-form"}))
{ 
    
}

<div id="testcontainer2">
</div>

2. Create the action method with “Authorize” attribute and return a partial view “TestPartial”

  [Authorize]
        public ActionResult AjaxTestNoHandle()
        {
            return PartialView("TestPartial");
        }

3. Run the application and click button “No Redirection Handling”. Because the user is not authenticated and it returns a partial view with the whole “Login” page. (I made login page with black background for displaying purpose)

image

Now let’s create  a customized authorize attribute to handle this situation.

1.  Create a CustomizedAuthorizeAttribute to handle login redirection if authentication is expired.  I only override the action HandleUnauthorizedRequest to detect if request is ajax, return a json result with a redirect url.

    public class CustomizedAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            string url = string.Format("{0}?returnUrl={1}",System.Web.Security.FormsAuthentication.LoginUrl,
                filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var redirectResult = filterContext.Result as RedirectResult;
                if (filterContext.Result is RedirectResult)
                {
                    // It was a RedirectResult => we need to calculate the url
                    var result = filterContext.Result as RedirectResult;
                    url = UrlHelper.GenerateContentUrl(result.Url, filterContext.HttpContext);
                }
                else if (filterContext.Result is RedirectToRouteResult)
                {
                    // It was a RedirectToRouteResult => we need to calculate
                    // the target url
                    var result = filterContext.Result as RedirectToRouteResult;
                    url = UrlHelper.GenerateUrl(result.RouteName, null, null, result.RouteValues, RouteTable.Routes, filterContext.RequestContext, false);
                }
                filterContext.Result = new JsonResult
                {
                    Data = new { Redirect = url },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                //non-ajax request
                base.HandleUnauthorizedRequest(filterContext);
            }
            
        }

2. Create controller action method with CustomizedAuthorize

        [CustomizedAuthorize]
        public ActionResult AjaxTest()
        {
            return PartialView("TestPartial");
        }

3. Create ajax.form post to “AjaxTest” method.


@using (Ajax.BeginForm("AjaxTest", "Home", new AjaxOptions { UpdateTargetId = "testcontainer" }, new { id="test-form"}))
{ 
    
}

<div id="testcontainer">
</div>

4. Click “Redirection Handling” button on the page without login. In FireBug – Console, you can see a json is returned

{"Redirect":"/Account/Login?returnUrl=%2fHome%2fAjaxTest%3fLength%3d4"}

5. Now let’s add a javascript function to handle the json and redirect to the returned url. Add a OnSuccess event in “AjaxOptions” call “validate”


@using (Ajax.BeginForm("AjaxTest", "Home", new AjaxOptions { UpdateTargetId = "testcontainer", OnSuccess="validate" }, new { id="test-form"}))
{ 
    
}

<div id="testcontainer">
</div>

6. Create javascript function validate.


    function validate(data) {
        if (data.Redirect)
            window.location = data.Redirect;
    }

7. Here you go, all done! When you click the “Redirection Handling” button, it will detect the forms authentication login url and return to the login page inside the ajax request!

Cools!

To download the demo project, go to codeplex https://ajaxrequestauthentic.codeplex.com/

CSS media queries not working with IE7, IE8

I have worked with media queries on a project for a while and notice that it is not actually working well in IE 7 or IE 8.

Here is a simple sample of the media query I have.

@import url("desktop.css") screen;
@import url("ipad.css") only screen and (device-width:768px);

To make sure things working alright in those browsers, you will need a separate css file for those two. And a detection of “if it is less than IE9”, use the “style-ie.css”



<link rel="stylsheet" type="text/css" media="all" href="style.css"/>
<!--[if lt IE 9]>
<link rel="stylesheet" type="text/css" media="all" href="style-ie.css"/>
<![endif]-->

Validation Summary is not cleared after Ajax.Form successfully posted, MVC 3

My scenario is after the invalid form values are posted through ajax.form, and the validation summary is displaying on the view. After I filled in all the required fields and posted the form with valid values, the post back data is correctly displaying, but the validation summary is remaining on the page.

Here are some code snippets,

The view contains two date picker (textbox) which are required fields.

    @model ClosedTicketByProductReportViewModel
    @using (Ajax.BeginForm("ClosedTicketByProductReport", "Report", new AjaxOptions { UpdateTargetId = "closedTicketByProductGrid", LoadingElementId = "loadingcontainer" }))
    { 
        @Html.ValidationSummary()
                
        <div class="control-group">
            Date From
            <div class="controls">
                @Html.TextBoxFor(p =&gt; p.DateFromForClosedProduct, new Dictionary { { "class", "small" }, { "type", "date" }, { "data-datepicker", "datepicker" } })
            </div>
        </div>
        <div class="control-group">
            Date To
            <div class="controls">
                @Html.TextBoxFor(p =&gt; p.DateToForClosedProduct, new Dictionary { { "class", "small" }, { "type", "date" }, { "data-datepicker", "datepicker" } })
            </div>
        </div>

        Generate Report
        
    }

Here is the view model I created for the view,

    public class ClosedTicketByProductReportViewModel
    {  

        [Required(ErrorMessage = "Please select Date From")]
        public DateTime? DateFromForClosedProduct { get; set; }

        [Required(ErrorMessage = "Please select Date To")]
        public DateTime? DateToForClosedProduct { get; set; }

        //report data
        public List Cases { get; set; }

    }

After I click submit without entering any dates, it displays the error messages as expected.

image

Then I enter the dates, and click submit again. The post back data is retrieved from controller action, but the validation summary remains on the page. As you can see the validation errors on the textbox itself are gone. Ajax.Form is only lack of support on the validation summaries.

image

Here is my fix, I don’t think it is great, but it is a work around.

    @using (Ajax.BeginForm("ClosedTicketByProductReport", "Report", new AjaxOptions { OnSuccess = "HideValidationSummary", UpdateTargetId = "closedTicketByProductGrid", LoadingElementId = "loadingcontainer" }))
    { }

I created a javascript function and call it on OnSuccess = "HideValidationSummary, the javascript function just hide the validation summary div.

function HideValidationSummary() {
    $(".validation-summary-errors").hide();
}

If anyone come across my blog post and has a better solution, please leave me a message. Much appreciated.

Adding a hyphen to the html attribute using html helpers, MVC3

The “data-*” is the custom data attributes in HTML5, it gives the ability to embed custom data attributes on all HTML elements and allows javascript to create a richer, more engaging user experience.  You can learn more about the usage of this “data-*” from here http://html5doctor.com/html5-custom-data-attributes/

I experience a problem of adding “data-*” to html tags by using the html helpers. I am using a datepicker from bootstrap and in the input tag, it specifies the “data-datepicker”.

<input id="SelectedDate" name = "SelectedDate" data-datepicker="datepicker" class="small" type="date" />

When I convert the html input tag to Html.TextBox helper class. It won’t pass the validation of razor engine’s syntax.

@Html.TextBoxFor(p=>p.SelectedDate, new {@class="small", type="date", data-datepicker = "datepicker"})

To work around this, you need to create a strongly typed Dictionary<string, object> and pass it to the Html.TextBox html attributes.

@Html.TextBoxFor(p => p.SelectedDate, new Dictionary<string, object> { { "class", "small" }, { "type", "date" }, { "data-datepicker", "datepicker" } })

This resolves the issue of having “data-*” custom data attributes while using mvc razor engine views.

Entityframework 4.3 code first & Migration

I wasn’t a fan of code first when it was released with EF 4. It is lack of data schema migration support. Every time you have schema updates, the database needs to be recreated using the Initializer. With EF Migration shipped out with EF 4.3, It resolves my issue.

Here I created a sample to demonstrate the migration process of EF 4.3, to prove it works with all scenarios. I create the POCOs with three different kinds of relationships and variable types of properties.

image

The database diagrams show the facts as below,

User to Addresses => one to many relation
User to Profiles => one to one relation
User to Lessons => many to many relation

User POCO

public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }

public byte Gender { get; set; }
public bool? IsMarried { get; set; }

//one to many relationship
public virtual ICollection<Address> Addresses { get; set; }

//one to one relationship
public virtual Profile Profile { get; set; }

//many to many relationship
public virtual ICollection<Lesson> Lessons { get; set; }
}

Profile POCO

    public class Profile
    {
        [Key]
        [ForeignKey("User")]
        public int UserId { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }

        public virtual User User { get; set; }
    }

Address POCO

    public class Address
    {
        public int Id { get; set; }
        public string Street { get; set; }
        public string State { get; set; }

        public virtual User User { get; set; }
    }

Lesson POCO

    public class Lesson
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public virtual ICollection Users { get; set; }
    }

And create the Context class to inherit from DbContext, and add DbSets to it.

public class CodeFirstExampleContext :DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<Lesson> Lessons { get; set; }
}

Also make sure you have your connection string inside the web.config/ app.config file.

<connectionStrings>
<add name="CodeFirstExampleContext" connectionString="Data Source=(local)\SQLEXPRESS;Initial Catalog=CodeFirstExample;integrated security=True" providerName="System.Data.SqlClient" />
</connectionStrings>

Now, I can write some code to add some records using the model that I created. And when the execution tries to talk to the database, it will create the database for you.

I put some test code inside the HomeController, Index action method.

        public ActionResult Index()
        {
            Data.User user = new Data.User();
            user.Name = "superwalnut";
            user.Email = "superwalnut@test.com";
            user.Profile = new Profile()
            {
                FirstName = "kev",
                LastName = "wam"
            };
            user.Addresses = new List</pre>
<address>(){ new Address{ Street = "test st", State = "test state" } }; CodeFirstExampleContext context = new CodeFirstExampleContext(); context.Users.Add(user); context.SaveChanges(); return View(); } 

Run the application, your database should be automatically created based on your models.

Now let’s change the User model, add two more columns in.

        public byte Gender { get; set; }
        public bool? IsMarried { get; set; }

How do we update the schema???

No worries. (I am testing using EF4.3.1)

Open Package Manager Console, and type

PM> Enable-Migrations

After you execute this command, it will create a folder inside your project. “Migrations”

image

Open the Configuration.cs file, you need to manually update the context type

image

Now you are all set to update schemas, after you have updated the User POCO, type the following command in Package Manager Console.

PM> Add-Migration

It will compare the database schema and your current models and generates a class that contains update methods. You can give a proper name for the generated class. Such as “Added User Gender IsMarried”.

The file will look like “201203270149267_Added User Gender IsMarried.cs”

Now type command in Package Manager Console to update database.

PM> Update-Database

Your database is updated after execution.

With column default values

If you already have records in the database for table User, for the newly added columns Gender (byte) and IsMarried (bool?), they will be given a default value 0. If it is nullable, null is given.

If you want to set existing records with special default values, you need to write Sql scripts in the Migration class Up method. Like below,

Sql("Update User Set Gender = 1");

To checkout the sample project code, go to codeplex

http://codefirstmigration.codeplex.com/

You will need to “Enable Nuget Package Restore” by right clicking your solution file after you download the source from codeplex TFS.

image

Render modal dialog from a PartialView, client side validation not working

While I am using JQuery to render a modal popup, and the popup content is read from a controller action with a partialview returned. The client side validation is not fired.

Here is a piece of sample code, I am using bootstrap modal to render the modal popup.

var url = "/Feed/Save/" + id;
        $.get(url, null, function (html) {
            $("#saveFeed").html(html);
            $("#saveFeedModal").modal('show');
        });

You need to add a line to parse unobtrusive validator,

            $.validator.unobtrusive.parse($("#saveFeed"));

After the modified the code, it all works fine,

var url = "/Feed/Save/" + id;
        $.get(url, null, function (html) {
            $("#saveFeed").html(html);
            $.validator.unobtrusive.parse($("#saveFeed"));
            $("#saveFeedModal").modal('show');
        });