Skip to main content

DrySQL for the Microsoft MVC .NET framework

The sample Chapter 1 from Professional ASP.NET MVC 1.0 (http://aspnetmvcbook.s3.amazonaws.com/aspnetmvc-nerdinner_v1.pdf) is a great tutorial that guides you through the features of Microsoft's MVC framework. If you have used Rails before, when you read the tutorial you keep telling to yourself "wow, this is like rails but compiled and with excellent Intellisense and a nice and fast database backend". That's right, SQL Server is tightly coupled to the Linq to SQL framework that you can use as the ORM for the MS MVC (please note that the use of Linq to SQL is optional; and you could use other ORM frameworks). Just because is tightly coupled does not mean that is a bad idea; it actually integrates really well with the visual tools from Visual Studio. Here is a screen shot showing a basic overview of the database ORM mapping tool in MS Vistual Web Developer Express (which is free, like all the technologies described here, including the SQL server 2008 Express edition):

As I was reading the tutorial, I found that they fixed some of the disadvanteges in Rails mentioned in the Enterprise Rails book from O'Reilly. The thing I was still missing tho, was that Linc to SQL does not generate automatic basic Database derived validations such as the length of fields and nullability of fields which is something that Rails misses to and was solved by DrySQL. The principle behind DrySQL is that if you already defined it in your DBMS, why should you defined again in your code?

To solve this I modify the RuleViolation class that you write in the tutorial to validate the Model's fields against constraints defined in the database such as the length of fields and nullability:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Data.Linq.Mapping;

namespace NerdDinner.Models
{
public class RuleViolationException : ApplicationException
{
   public RuleViolationException(string errorMessage)
       : base(errorMessage)
   {

   }
}
public class RuleViolation
{
   public string ErrorMessage { get; private set; }
   public string PropertyName { get; private set; }
   public RuleViolation(string errorMessage)
   {
       ErrorMessage = errorMessage;
   }
   public RuleViolation(string errorMessage, string propertyName)
   {
       ErrorMessage = errorMessage;
       PropertyName = propertyName;
   }

//Parts of this method were taken from
// http://www.codeproject.com/KB/cs/LinqColumnAttributeTricks.aspx?display=Print
// * Checks that fields required fields in the database (not null flagged) are present
// * Checks that values are numbers for numeric fields
// * Checks that string values are not too big for the db field
   public static List<RuleViolation> CheckBasicDBCobnstraints(object obj)
   {
       List<RuleViolation> violations = new List<RuleViolation>();
       Type type = obj.GetType();
       object[] info;
       PropertyInfo[] properties = type.GetProperties();
       foreach (PropertyInfo prop in properties)
       {
           //Get the Linq realted info
           info = prop.GetCustomAttributes(typeof(ColumnAttribute), true);
           // Check if it is a Linq column property
           if (info.Length == 1)
           {
               ColumnAttribute ca = (ColumnAttribute)info[0];
               string dbtype = ca.DbType;
               //Check if it is text
               if (dbtype.StartsWith(\"NChar\") || dbtype.StartsWith(\"NVarChar\"))
               {
                   bool property_is_empty = false;
                   string property_str_value = (string) prop.GetValue(obj, null);
                   //Check if it is required or not
                   if (!ca.CanBeNull)
                   {
                       if (String.IsNullOrEmpty(property_str_value))
                       {
                           violations.Add(new RuleViolation(prop.Name + \" is required\", prop.Name));
                           property_is_empty = true;
                       }
                   }
                   //check the length
                   if (!property_is_empty)
                   {

                       int dblenint = 0;
                       int index1 = dbtype.IndexOf(\"(\");
                       int index2 = dbtype.IndexOf(\")\");
                       string dblen = dbtype.Substring(index1 + 1, index2 - index1 - 1);
                       int.TryParse(dblen, out dblenint);
                       if (property_str_value.Length > dblenint)
                           violations.Add(new RuleViolation(prop.Name + \" too long (Max. length is \" + dblenint.ToString() + \")\", prop.Name));
                   }
               }
               else
               {
                   object property_str_value = prop.GetValue(obj, null);
                   bool property_is_empty = (property_str_value == null);
                   //Check if it is required or not
                   if (!ca.CanBeNull)
                   {
                       if (property_is_empty)
                       {
                           violations.Add(new RuleViolation(prop.Name + \" is required\", prop.Name));
                           property_is_empty = true;
                       }
                   }
               }
           }
       }
       return violations;
   }
}
}

Then I changed the Dinner class to use the new method:

   public List<RuleViolation> GetRuleViolations()
   {
       List<RuleViolation> violations = RuleViolation.CheckBasicDBCobnstraints(this);
       violations.AddRange(GetCustomRulesViolations());
  
       return violations;
   }

   //Check for any custom constraint on the fields
   private List<RuleViolation> GetCustomRulesViolations()
   {
       List<RuleViolation> violations = new List<RuleViolation>(1);
       if (!PhoneValidator.IsValidNumber(ContactPhone, County))
           violations.Add( new RuleViolation(\"Phone# does not match country\",
           \"ContactPhone\"));
       return violations;
   }

   partial void OnValidate(ChangeAction action)
   {
       if (!IsValid)
           throw new RuleViolationException(\"Rule violations prevent saving\");
   }

Please follow the tutorial first so that you can get as feel of the power of MS MVC, then you download my complete modification here to use DrySQL in .NET.

Comments

Popular posts from this blog

Power Automate: SFTP action "Test connection failed"

When I added an SFTP create file action to my Power Automate flow ( https://flow.microsoft.com ) , I got the following error in the action step, within the designer: "Test connection failed" To troubleshoot the Power Automate connection, I had to: go the Power Automate portal then "Data"->"Connections"  the sftp connection was there, I clicked on the ellipsis, and entered the connection info It turns out, that screen provides more details about the connection error. In my case, it was complaining that "SSH host key finger-print xxx format is not supported. It must be in 'MD5' format". I had provided the sha fingerprint that WinScp shows. Instead, I needed to use the MD5 version of the fingerprint. To get that, I had to run in command line (I was in a folder that had openssh in it): ssh -o FingerprintHash=md5 mysftpsite.com To get the fingerprint in MD5 format. I took the string (without the "MD5:" part of the string) and put ...

How to create online multiplayer HTML5 games in Contruct2

  Construct2 can use websockets to send and receive messages between games. By using socket-io , we can use a Node.js script as the server and my modification to the socket-io plugin for Construct2 to allow the games to synchronize data between them in real-time. There are two parts to this design: the Node.js server and the Construct2 clients (the games playing). The main part of building an online multiplayer HTML5 game is to plan: how the clients will communicate how often and what to communicate how much of the logic will go into the server and how much to the client. In my sample game, I chose to have each client own a player and have the server just relay messages: Use string messages in the form TypeOfMessage, Parameter1, Paremeter2, Parater3, etc to communicate. Have the clients send their player position about 16 times a second. Whenever their player shoots, the client needs to send a message immediately. Almost all of the game logic will...

How to use Windows SSO with OpenXava

One of the nice things about the .NET web environment is the dead easy way to implement Single Sign On in your web apps through Active Directory authentication. In the Java world there are multiple alternatives to use Windows’ Single Sign On with Java based web apps. One of those alternatives is Waffle . Waffle allows your Java web app to authenticate against Active Directory groups (and users). The only caveat is that your web server needs to be running in Windows, which kind of makes sense. In this article, you will learn the steps required to have your OpenXava web application use Waffle to authenticate your Windows users. The first step is to download Waffle from their site and then copy the JAR files outlined in https://github.com/dblock/waffle/blob/master/Docs/tomcat/TomcatSingleSignOnValve.md to the OpenXava’s tomcat server. In your OpenXava project, create servlets.xml in the Web-inf, containing the following: <!-- the role name (the domain gorup) must be e...