MVC 3 Unobtrusive Validation with LINQ to SQL

Recently I've been working on a small project that uses LINQ to SQL as it's data layer (it's a very simple model) and contains two presentation projects - a web form application for the front end and an MVC 3 administration panel.

When playing with the unobtrusive validation engine, you'll find many examples on the 'net telling you to decorate your model with various attributes from the System.ComponentModel.DataAnnotations namespace. Now, I ran in to a bit of an issue with the LINQ to SQL objects - they're generated dynamically. I could add the attributes, but they would be overwritten any time I made a change to my DBML file. So, after a bit of searching I came across the MetaDataTypeAttribute in the same namespace. This attribute allows you to create a completely separate, standard class and define it as a Meta Data container for another class. Neat, hu?

Well, I quickly ran in to my second issue - I could create my separate class, but I still had to tag the LINQ classes with the attribute... which meant that data would also be overwritten.

LINQ classes are partial classes - it didn't dawn on me for a few hours, but I could effectively create another partial class in the DBML's code behind file (right click on the DBML file -> View Code) and decorate THAT class with the attribute - which would never be overwritten by the code generator. Below is what my LINQ code behind looks like. This example also includes a CustomValidationAttribute example, which took me a little bit to figure out:

   1:  using System;
   2:  using System.Text;
   3:  using System.ComponentModel.DataAnnotations;
   5:  namespace MyNameSpace {
   6:      [MetadataType(typeof(News_Validation))]
   7:      partial class News {
   8:      }
   9:      [MetadataType(typeof(ExclusionDate_Validation))]
  10:      partial class ExclusionDate {
  11:      }
  12:      public sealed class News_Validation {
  13:          [Required(ErrorMessage = "Post content is required.")]
  14:          public string Content { get; set; }
  15:      }
  16:      public sealed class ExclusionDate_Validation {
  17:          [Required(ErrorMessage = "'Date To Replace' is required."), CustomValidation(typeof(Validator), "ValidateDate")]
  18:          public DateTime DateToReplace { get; set; }
  20:          [CustomValidation(typeof(Validator), "ValidateDate")]
  21:          public DateTime ReplacementDate { get; set; }
  22:      }
  24:      public static class Validator {
  25:          public static ValidationResult ValidateDate(string inputDate) {
  26:              DateTime thisDate;
  27:              if ((inputDate != null) && inputDate.Trim() != string.Empty) {
  28:                  if (DateTime.TryParse(inputDate, out thisDate)) {
  29:                      return ValidationResult.Success;
  30:                  } else {
  31:                      return new ValidationResult("Not a valid date.");
  32:                  }
  33:              } else {
  34:                  return ValidationResult.Success;
  35:              }
  36:          }
  37:      }
  38:  }

Popular posts from this blog

How I Learned to Lose Weight and Love Exercise (again)

AutoMapper: UseValue vs ResolveUsing vs MapFrom

GDPR: Application Password Security in 2018