Posts Tagged ‘mvc’

Concurrent Sessions in MVC2

September 21st, 2010

There’s lots of rules that control how many concurrent connections you can have in an ASP.net MVC2 application.

Firstly the browser itself limits the number of connections it will make to the same domain.  The limit used to be 2 (as per the HTTP specification).  Some browsers increase this limit, but older browsers like IE6 will still stick to it.

Secondly you have the number of threads available in IIS to content with.  Thomas Masquardt’s Blog gives a very detailed explanation of this.

Finally MVC itself has some limitations. Basically you can only have a single request using a read-write session at any given time.  This means that if you have a controller serving up images, and you don’t take any steps to avoid the problem, you will only ever serve up one image at a time.  Imran Baloch’s Blog gives a lot more information about this and shows how to create session-less actions to avoid the issue.  For me the solution is quite simple.  By placing the following code in Global.asax.cs:

protected void Application_BeginRequest()
{
    if((Request.Url.AbsoluteUri.ToUpperInvariant().Contains("REFRESHPRICES")
        && Request.Cookies["ASP.NET_SessionId"] != null))
    {
        Context.SetSessionStateBehavior(SessionStateBehavior.ReadOnly);
    }
}

I can make the session for my action read-only, and sidestep the limit.

Warning

Just marking the session sate as read-only doesn’t prevent you from trying to modify the session.  It’s up to you to maintain thread-safety if you decide to lie to the framework.

Share

MVC2 Cross Field Validation

August 10th, 2010

Introduction to Validation

Nearly all web applications require some form of validation. Validation performs two main purposes: helping the user to enter correct values on a web page, and protecting the application from invalid or malicious input.

Validation can take place client-side within the browser, or server-side when the page is posted to the server. ASP.NET MVC2 comes with a flexible framework for applying validation using data annotations on the view model. Scott Gu has written an excellent tutorial on basic MVC validation here: http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx.

Limitations of Built-in MVC Validation

The built-in validation works well for standard scenarios, but there are a few common scenarios it can’t cope with. One of the main issues is that validation can only be done on a model property in isolation. Often we will want to validate a property based on the current value of some other property: for instance we may want our confirm password field value to be the same as the password box.

In this article we will extend the MVC validation framework to provide support for cross-field validation. We’ll do this in part by creating custom validation attributes as described in Phil Haack’s article (http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx) but we’ll also need to extend the validator framework to allow us to validate across fields.

Extending the Framework to Support Cross-Field Validation

To create a custom validation attribute we usually derive from ValidationAttribute and override the IsValid method, however IsValid only gets passed the value of the property to validate and we are going to need to see the value of other fields in the model. To accommodate this we’ll create an interface ICrossFieldValidationAttribute that has an IsValid method with access to the entire view model (note these code snippets only show significant lines – download the example solution to get the whole files):

public interface ICrossFieldValidationAttribute
{
    bool IsValid(ControllerContext controllerContext, object model, ModelMetadata modelMetadata);
}

Next we build a base class for cross-field validators. This base class uses the extended IsValid defined above, rather than the narrower method used in the framework’s DataAnnotationsModelValidator class.

public abstract class CrossFieldValidator<TAttribute> : DataAnnotationsModelValidator<TAttribute>
        where TAttribute : ValidationAttribute, ICrossFieldValidationAttribute
{
    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        var attribute = Attribute as ICrossFieldValidationAttribute;

        if (!attribute.IsValid(ControllerContext, container, Metadata))
        {
            yield return new ModelValidationResult {Message = ErrorMessage};
        }
    }
}

Building the Validation

Now that we have a cross-field validation framework in place, we can build our new attribute.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class EqualToPropertyAttribute : ValidationAttribute, ICrossFieldValidationAttribute
{
    public string OtherProperty { get; set; }

    public bool IsValid(ControllerContext controllerContext, object model, ModelMetadata modelMetadata)
    {
        var propertyInfo = model.GetType().GetProperty(OtherProperty);
        var otherValue = propertyInfo.GetGetMethod().Invoke(model, null);

        if (modelMetadata.Model == null) modelMetadata.Model = string.Empty;
        if (otherValue == null) otherValue = string.Empty;

        return modelMetadata.Model.ToString() == otherValue.ToString();
    }
}

And apply it to the view model:

public class Account
{
    public string Password { get; set; }

    [EqualToProperty(OtherProperty = "Password")]
    public string ConfirmPassword { get; set; }
}

We also need to create a validator class to emit the correct client-side rules, and to invoke the IsValid method.

public class EqualToPropertyValidator : CrossFieldValidator<EqualToPropertyAttribute>
{
    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule
        {
            ValidationType = "equaltoproperty",
            ErrorMessage = Attribute.FormatErrorMessage(Metadata.PropertyName),
        };

        rule.ValidationParameters.Add("otherProperty", Attribute.OtherProperty);

        return new[] { rule };
    }
}

Finally we need to create a JavaScript function to evaluate the rule client-side:

jQuery.validator.addMethod("equaltoproperty", function (value, element, params) {
    if (this.optional(element)) {
        return true;
    }

    var otherPropertyControl = $("#" + params.otherProperty);
    if (otherPropertyControl == null) {
        return false;
    }

    var otherPropertyValue = otherPropertyControl[0].value;
    return otherPropertyValue == value;
});

function testConditionEqual(element, params) {
    var otherPropertyControl = $("#" + params.otherProperty);
    if (otherPropertyControl == null) {
        return false;
    }

    var otherPropertyValue;
    if (otherPropertyControl[0].type == "checkbox") {
        otherPropertyValue = (otherPropertyControl[0].checked) ? "True" : "False";
    } else {
        otherPropertyValue = otherPropertyControl[0].value;
    }

    return otherPropertyValue == params.comparand;
}

And the final step is to register our new attribute with MVC. We do this in Global.asax Application_Start method like this:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EqualToPropertyAttribute),
                                                      typeof(EqualToPropertyValidator));
 

Example

The example code is here CrossFieldValidation.

Share

ReSharper and Microsoft Code Contracts

January 27th, 2010

I’m using code contracts in an MVC RC / .NET 3.5 application and I don’t want ReSharper 5 complaining about potential null reference accesses that aren’t in fact possible due to the contracts.

The solution (pieced together from StackOverflow and JetBrains) is to add the following XML to a file named C:\Program Files\JetBrains\ReSharper\v5.0\Bin\ExternalAnnotations\Microsoft.Contracts\Microsoft.Contracts.xml.

<assembly name="Microsoft.Contracts">
  <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
    </attribute>
    </parameter>
  </member>
  <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
      </attribute>
    </parameter>
  </member>
  <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
      </attribute>
    </parameter>
  </member>
  <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
      </attribute>
    </parameter>
  </member>
  <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
      </attribute>
    </parameter>
  </member>
  <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
      </attribute>
    </parameter>
  </member>
  <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
    <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
    <parameter name="condition">
      <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
        <argument>0</argument>
      </attribute>
    </parameter>
  </member>
</assembly>
Share

Using the ASP.NET MVC source code to debug your app « Steve Sanderson’s blog

January 20th, 2010

Found this useful hint for debugging MVC 2 RC applications.  hopefully MS will publish the source code on their source server soon.  In the meantime…

Using the ASP.NET MVC source code to debug your app « Steve Sanderson’s blog.

Share

Using SQL Express Databases

January 18th, 2010

If you use a SQL Express database for LINQ to SQL in an MVC application Studio will create an App_Data directory and put the database there. However, when you copy your solution up to a web host you might get connection problems. This is likely to be because the LINQ designer has embedded a path to the database that isn’t valid on the server.
The fix is really simple – just use the |DataDirectory| keyword in your Web.config.

<connectionStrings>
    <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
Share

MVC 2 Cross-field Validation

January 18th, 2010

The built in annotation-based validation in MVC is fine, but sometimes you need to validate a property relative to other properties on the page.  For example, you might want to make sure that two properties are the same (password and confirm password) for example.

This post explains how to extend the validation model to cater for this.

First we need to create new validation attributes to annotate our data model with.  I use the following interface for all attributes that require more than one field to validate:

/// summary>
/// All validation data annotation attributes that require more than one
/// property to validate (or additional context) should implement this
/// interface.
// </summary>
public interface IRichValidationAttribute
{
    /// <summary>
    /// Determines whether the specified property is valid.
    /// </summary>
    /// <param name="controllerContext">Complete controller context (if required).</param>
    /// <param name="model">The model object to which the property belongs.</param>
    /// <param name="modelMetadata">Model metadata relating to the property holding
    /// the validation data annotation.</param>
    /// <returns>
    /// <c>true</c> if the specified property is valid; otherwise, <c>false</c>.
    /// </returns>
    bool IsValid(ControllerContext controllerContext, object model, ModelMetadata modelMetadata);
}

Now we can create a new attribute that implements this interface:

[AttributeUsage(AttributeTargets.Property |   AttributeTargets.Field, AllowMultiple = false)]
public class NotEqualToPropertyAttribute : ValidationAttribute, IRichValidationAttribute
{
   /// <summary>
    /// Gets or sets the other property we are validating against.
    /// </summary>
    public string OtherProperty { get; set; }

   /// <summary>
    /// Determines whether the specified value of the object is valid.
    /// </summary>
    /// <param name="value">
    /// The value of the specified validation object on which the attribute is declared.
    /// </param>
    /// <returns>
    /// true if the specified value is valid; otherwise, false.
    /// </returns>
    public override bool IsValid(object value)
    {
        // Work done in other IsValid
        return true;
    }

    /// <summary>
    /// Determines whether the specified property is valid.
    /// </summary>
    /// <param name="controller">Complete controller context (if required).</param>
    /// <param name="model">The model object to which the property belongs.</param>
     /// <param name="modelMetadata">Model metadata relating to the property holding the validation data annotation. </param>
     /// <returns>
     /// true if the specified property is valid; otherwise, false.
     /// </returns>
     public bool IsValid(ControllerContext controllerContext, object model, ModelMetadata modelMetadata)
     {
        if (model == null)
        {
             throw new ArgumentNullException("model");
        }
        // Find the value of the other property.
        var propertyInfo = model.GetType().GetProperty(OtherProperty);
        if (propertyInfo == null)
        {
           throw new InvalidOperationException(string.Format("Couldn't find {0} property on {1}.", OtherProperty, model));
        }
        var otherValue = propertyInfo.GetGetMethod().Invoke(model, null);
        return modelMetadata.Model.ToString() != otherValue.ToString();
    }
}

This can then be applied to the model:

[NotEqualToProperty(ErrorMessage = "Other occupation cannot be the same as the main occupation",
OtherProperty = "Occupation")]
public Occupation OtherOccupation { get; set; }

Now we need to create a validator for the attribute. I’ve created a base class for all validators than used the enhanced validation interface:

/// <summary>
/// A base class for validators that have attributes that require more
/// than one property to check for validity.
/// The standard <see cref="ValidationAttribute.IsValid"/> method
/// isn't sufficient because this method can't see the rest of the model.
/// </summary>
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
public abstract class CrossFieldValidator<TAttribute> : DataAnnotationsModelValidator<TAttribute> where TAttribute : ValidationAttribute
{
    protected CrossFieldValidator(ModelMetadata metadata, ControllerContext context, TAttribute attribute) : base(metadata, context, attribute)
    {
    }
}


/// <summary>
/// Returns a list of validation error messages for the model.
/// </summary>
/// <param name="container">The container for the model.</param>
/// <returns>
/// A list of validation error messages for the model, or an empty list if no errors have occurred.
/// </returns>
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var attribute = Attribute as IRichValidationAttribute;
if (attribute != null)
{
if (!attribute.IsValid(ControllerContext, container, Metadata))
{
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
else if (!Attribute.IsValid(container))
{
yield return new ModelValidationResult { Message = ErrorMessage };
}
}

Finally we need to create the client-side script:

jQuery.validator.addMethod("notequaltoproperty", function(value, element, params) {
    if (this.optional(element)) {
        return true;
    }

   var otherPropertyControl = $("#" + params.otherProperty);
    if (otherPropertyControl == null) {
       return false;
    }

    var otherPropertyValue = otherPropertyControl[0].value;
    return otherPropertyValue != value;
});

And that’s it.

Share

Testing Validation Attributes

January 14th, 2010

This code illustrates how to unit test your MVC 2 data annotation validation attributes.

In this example we are using this model object:

public class Foo
{
    [Required(ErrorMessage= "Must give a name")]
    public string Name { get; set; }

  
    [Required(ErrorMessage = "Must give a DOB")]
    [Range(typeof(DateTime), "2009/01/01", "2011/01/01",
        
ErrorMessage = "Must be in range")]
    public DateTime DateOfBirth { get; set; }
}

This examples shows two simple test cases: if a name is given validation is successful, but if it isn’t we get a validation error.  Using RhinoMocks and the MS Test framework we end up with this test class:

[TestClass]
public class FooTest
{
    private const string originalName = "Barry Bar";
    private const string newName = "Fred Foo";

    [TestMethod]
    public void NameIsValid()
    {
        var foo = new Foo {Name = originalName};
        var name = new ValueProviderResult(newName, newName, CultureInfo.CurrentCulture);


        var binder = new DefaultModelBinder();
        var controllerContext = new ControllerContext();

        var bindingContext = new ModelBindingContext
        {
            ModelMetadata =
                ModelMetadataProviders.Current.GetMetadataForType(() => foo, foo.GetType()),
            ValueProvider = new Dictionary<string, ValueProviderResult> { { "Name", name } }
        };

        var boundFoo = binder.BindModel(controllerContext, bindingContext) as Foo;
        Assert.AreEqual(newName, boundFoo.Name);
        Assert.IsTrue(bindingContext.ModelState.IsValid);
    }


    [TestMethod]
    public void NameIsRequired()
    {
        var foo = new Foo { Name = originalName };
        var name = new ValueProviderResult(null, null, CultureInfo.CurrentCulture);


        var binder = new DefaultModelBinder();
        var controllerContext = new ControllerContext();


        var bindingContext = new ModelBindingContext
        {
            ModelMetadata =
                ModelMetadataProviders.Current.GetMetadataForType(() => foo, foo.GetType()),
            ValueProvider = new Dictionary<string, ValueProviderResult> { { "Name", name } }
        };


        var boundFoo = binder.BindModel(controllerContext, bindingContext) as Foo;
        Assert.AreEqual(originalName, boundFoo.Name);
        Assert.IsFalse(bindingContext.ModelState.IsValid);
        }
    }
}

And that’s all there is to it.

Share

Validating dropdown lists in MVC 2 RC

January 12th, 2010

Here's a couple of gotchas when validating dropdown lists in MVC using jQuery validation.

Implicit [Required] Attribute

Doing any kind of validation on a dropdown list, or even just creating a validation message for it, implicit makes the list a required value. This is only a problem if the issue below affects you.

List Options Must Have a Value

Every option in the Select must have a value, e.g.:

 

<option value="1">Mr</option>

 

rather than

 

<option>Mr</option>

 

If it doesn't, the jQuery required validation will fail.

Share

Stuff I’ve learned building MVC apps part 2

June 17th, 2008

If you want to embed images, you can use the Url helper to get a qualified path. e.g.:

<img alt="alt" src="<%= Url.Content("Content/Images/image.gif") %>" >
Share

Stuff I’ve learned building MVC apps part 1

June 17th, 2008

If you’re using embedded code in an View or Master Page, you can use this declaration to avoid fully qualifying type names:

<%@ Import Namespace=”System.xxx” %>

Share