Tuesday, May 31, 2011

Web Application Automatic Deployment using MsBuild with Microsoft SDC Task Library

Introduction
Projects commonly have a web application component, be it a front end or in the form of WebServices. This article will demonstrate how the web applications can be automatically deployed and configured under IIS using the Microsoft SDC Task Library extensions for MsBuild.
The SDC Task Library is free and available at http://codeplex.com/sdctasks Over 300 tasks included in this library including tasks for: creating websites, creating application pools, creating ActiveDirectory users, running FxCop, configuring virtual servers, creating zip files, configuring COM+, creating folder shares, installing into the GAC, configuring SQL Server, configuring BizTalk 2004 and BizTalk 2006 etc.

Step 1
Create project xml file and import SDC tasks
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="Microsoft.Sdc.Common.tasks"/>

</Project>
Step 2
Create deployment Work Plan
<Target Name="Deploy_Webapp">
<CallTarget Targets="CopyFolders" />
<CallTarget Targets="SetPermissions" />
<CallTarget Targets="CleanIIS" />
<CallTarget Targets="CreateWebSite" />
</Target>
Step 3
Implement every Target in Work Plan, let's focus on CreateWebSite. First of all I will create a WebSite and then I will create a VirtualDirectory.
<Target Name="CreateWebSite" DependsOnTargets="CleanIIS">
<Web.WebSite.Create
Description="testWebSite"
Path="C:\Server"
MachineName="localhost"/>
<Web.WebSite.CreateVirtualDirectory
VirtualDirectoryName="testVD"
Path="C:\Server"
MachineName="localhost"
AppPoolID="DefaultAppPool"
AppCreate="True"
WebSiteName="testWebSite"
AuthFlags="NTLM"/>
</Target>
CreateVirtualDirectory attributes:
  • virtualDirectoryName - name of the virtual driectory to create
  • path - Path to map the virtual directory to.
  • machineName - Machine to add the virtual directory on.
  • appPoolID - Application pool to run the virtual directory under
  • appCreate - Set to "true" to create an application for this virtual directory.
  • webSiteName - Web site to attach the virtual directory onto.
  • authFlags - Authentication flags to apply to the directory.

Step 4
Create batch file to make all this easier to run
@ECHO OFF

IF EXIST %windir%\Microsoft.NET\Framework64\v2.0.50727\ (
%windir%\Microsoft.NET\Framework64\v2.0.50727\msbuild configServer.xml
) ELSE (
%windir%\Microsoft.NET\Framework\v2.0.50727\msbuild configServer.xml
)
Finally
Microsoft SDC Task Library extensions for MsBuild provides us much more options, You can read a manual that comes with it in order to learn more.

Complite Example: here

Sunday, May 8, 2011

Using DataAnnotations to validate entities

In every sample on the internet regarding MVC and Entity Framework 4.0, we can find the use of DataAnnotations.

DataAnnotations allows us to add to our entities some Meta data including simple rules of validation (as well as hiding column, defining display name etc.).
By simple validation rules I mean we can define:
- Required fields
- String Length
- Range

Adding DataAnnotations is quite simple, after adding a reference to System.ComponentModel.DataAnnotations, we can add a partial class extending the relevant entity from our Entity Framework edmx and defining a metadatatype class with the rules.
For example:

    [MetadataType(typeof(TrackMetaData))]
    public partial class Track
    {
        public class TrackMetaData
        {
            [ScaffoldColumn(false)]
            [DisplayName("Track Id")]
            public int Id { get; set; }

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

            [Range(typeof(DateTime), "1/1/1753", "31/12/9999",
               ErrorMessage = "Value for {0} must be between {1} and {2}")]
            [DisplayName("Play Date")]
            public Nullable PlayDate { get; set; }
        }
    }
}

In this example we can see the use of ScaffoldColumn, StringLegth, DisplayName & Range...for those who work on multi language system or just prefer - you can retrieve the error message from a resource file as well.

Adding this class will work for MVC, MVC's plumbing will use this class to show error messages when relevant...but what if we have another type of client and we want to make sure data is validated before saving it to database?

Add this simple helper class to your project (or even better to your framework/core/library):
   public class ValidationHelper
    {
        /// 
        /// Check if specified object is valid
        /// 
        /// The object to validate        /// 
        public static bool IsValid(object obj)
        {
            return (!Validate(obj).Any());
        }

        /// 
        /// Validate an object against Data Annotations meta data defined against the object 
        /// 
        /// The object to validate        /// A List of  
        public static List Validate(object obj)
        {
            Type instanceType = obj.GetType();
            Type metaData = null;
            MetadataTypeAttribute[] metaAttr = (MetadataTypeAttribute[])instanceType.GetCustomAttributes(typeof(MetadataTypeAttribute), true);

            if (metaAttr.Count() > 0)
            {
                metaData = metaAttr[0].MetadataClassType;
            }
            else
            {
                throw new InvalidOperationException("Cannot validate object, no metadata assoicated with the specified type");
            }

            TypeDescriptor.AddProviderTransparent(
            new AssociatedMetadataTypeTypeDescriptionProvider(instanceType, metaData), instanceType);

            List results = new List();
            ValidationContext ctx = new ValidationContext(obj, null, null);

            bool valid = Validator.TryValidateObject(obj, ctx, results, true);
            return results;
        }

        /// 
        /// Get Validation errors as string
        /// 
        /// The object to validate        /// 
        public static string GetValidationErrors(object obj)
        {
            List errors = Validate(obj);

            var errorText = new StringBuilder();
            foreach (var error in errors)
            {
                errorText.Append(error.ErrorMessage + Environment.NewLine);
            }
            return errorText.ToString();
        }
    }

Than..add a few lines of code to your UnitOfWork (see my previous post for details: EF4 Self Tracking Entities & Repository design pattern)

    public class UnitOfWork:IUnitOfWork, IDisposable
    {

        public void ApplyChanges(string entityName, object entity)
        {
            if (entity == null)
                return;

            if (entity is IObjectWithChangeTracker)
            {
                bool ok = ValidationHelper.IsValid(entity);

                if (!ok)
                {
                    string message = string.Format("Can not apply changes to the '{0}' entity due to validation errors", entity.ToString());
                    LogUtil.LogInfo(string.Format("{0} ({1})", message, ValidationHelper.GetValidationErrors(entity)));

                    throw new ValidationException(message);
                }

                _context.ApplyChanges(entityName, (IObjectWithChangeTracker)entity);
            }
            else
            {
                throw new ArgumentException("entity must implement IObjectWithChangeTracker to use applyChanges");
            }
        }
    }

That's it..before the save of every entity (you implemented a MetadataType class for), it will validate the object and refuse to applyChanges or any other policy you decide is suitable to your project.

Happy validation,
Diego