pr0g33k

 collapse all
  1. Building a Windows Service to Host Workflow Foundation 4 Activities

    The past few months have been very busy for me. The public school district for which I work purchased a new student information management system and they need to integrate their existing software packages with the new system. Each software package comes with different requirements for integration. Some are in-house and located on local servers while others are remote. They all perform imports and exports via text files, though, and communicate via protocols like SNMP, HTTP WebDAV, FTP, and SFTP. Some of the file processing can be event-driven since they have locally-installed applications and I can monitor their directories while the remote applications have to be polled. And some of the imports/exports are expected at specific times during the day while others are needed on time intervals. Oh yeah, these integrations can and likely will change sporadically so they need to be flexible and easy to maintain and the solution needs to simple enough that a group of junior developers can manage it if needed. Needless to say, this has kept me busy for the past several months.

    WF 4.5 to the Rescue!

    Choosing Workflow Foundation for a task like this was a no-brainer. I needed the ability to pick up files from one location, perform modifications (in some instances), change file names, transport them to other locations, and archive the files in between. These step-by-step processes are exactly what Workflow Foundation was made to do. And the WF designer in Visual Studio meant that pretty much anyone could drag and drop the WF activities onto the workflows and make simple adjustments when needed.

    Building the workflow activities for the individual tasks was pretty simple and I'll have some future posts about building the activities. What was not simple, though, was figuring out how to host the workflows. Pretty much everything I found online regarding Workflow Foundation hosting was about AppFabric. I was really excited by what AppFabric promised to deliver - a slick management interface, easier persistence and tracking configuration, and simple deployments. AppFabric is awesome - really awesome - but only if you're doing WCF-centric development. The first workflow I built relied upon a file system event to trigger the workflow (a file creation event monitored by a FileSystemWatcher-based WF activity). No matter what I tried, I couldn't get AppFabric to run the workflow. It turns out that AppFabric really only wants to run workflows when triggered by a WCF call picked up by a RecieveRequest activity. That AppFabric is presented as an IIS component should have been my first clue but I was hoping that I could just run my workflows without any regard to Internet-based traffic and still take advantage of all the other hotness in AppFabric. Unfortunately, no such luck...

    Kickin' It Old-School with a Windows Service

    I settled on developing a relatively simple Windows Service to host the workflow activities. Now, because many of the workflows needed to run uninterrupted, I didn't want to hard-code the creation of each workflow into the service. Instead, I wanted the workflows to be created and destroyed dynamically without having to restart or reinstall the service everytime a workflow was added, deleted, or changed. Fortunately, workflows can be coded in XAML and dynamically loaded from those XAML definitions. So, what I built is a Windows Service that uses a FileSystemWatcher to monitor a directory where I can add and delete XAML workflow definitions and the service manages the lifetime of each workflow. I do need to point out that the workflows you'll be adding to this service are workflow activities (XAML) and not WCF Workflow Services (XAMLX).

    Here's the code for the service:

    public partial class Service : ServiceBase
    {
        private FileSystemWatcher _FileSystemWatcher;
        private Dictionary<String, WorkflowApplication> _WorkflowApplications;
    
        public Service()
        {
            InitializeComponent();
        }
    
        protected override void OnStart(String[] args)
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
    
            String path = ConfigurationManager.AppSettings["WorkflowsPath"];
    
            if (!String.IsNullOrEmpty(path))
            {
                _WorkflowApplications = new Dictionary<String, WorkflowApplication>();
                String filter = "*.xaml";
    
                if (!String.IsNullOrEmpty(Path.GetExtension(path)))
                    path = path.Replace(Path.GetFileName(path), String.Empty);
    
                if (!path.EndsWith("\\"))
                    path = String.Format("{0}\\", path);
    
                foreach (String file in Directory.GetFiles(path, filter))
                    CreateWorkflowApplication(file);
    
                _FileSystemWatcher = new FileSystemWatcher(path, filter);
                _FileSystemWatcher.NotifyFilter = NotifyFilters.FileName;
                _FileSystemWatcher.Created += new FileSystemEventHandler(OnCreated);
                _FileSystemWatcher.Deleted += new FileSystemEventHandler(OnDeleted);
                _FileSystemWatcher.Renamed += new RenamedEventHandler(OnRenamed);
                _FileSystemWatcher.EnableRaisingEvents = true;
            }
        }
    
        protected override void OnStop()
        {
            _FileSystemWatcher.EnableRaisingEvents = false;
            _FileSystemWatcher.Dispose();
    
            foreach (var key in _WorkflowApplications.Keys)
            {
                _WorkflowApplications[key].Unload();
            }
        }
    
        private void OnCreated(Object sender, FileSystemEventArgs e)
        {
            Thread.Sleep(1000);
            CreateWorkflowApplication(e.FullPath);
        }
    
        private void OnDeleted(Object sender, FileSystemEventArgs e)
        {
            DeleteWorkflowApplication(e.FullPath);
        }
    
        private void OnRenamed(Object sender, RenamedEventArgs e)
        {
            DeleteWorkflowApplication(e.OldFullPath);
            CreateWorkflowApplication(e.FullPath);
        }
    
        private void OnUnhandledException(Object sender, UnhandledExceptionEventArgs e)
        {
            LogError(e.ExceptionObject as Exception);
        }
    
        private void CreateWorkflowApplication(String fullPath)
        {
            if (!_WorkflowApplications.ContainsKey(fullPath))
            {
                try
                {
                    ActivityXamlServicesSettings activityXamlServicesSettings = new ActivityXamlServicesSettings { CompileExpressions = true };
                    DynamicActivity dynamicActivity = ActivityXamlServices.Load(fullPath, activityXamlServicesSettings) as DynamicActivity;
                    WorkflowApplication workflowApplication = new WorkflowApplication(dynamicActivity);
                    workflowApplication.InstanceStore = new XmlInstanceStore(fullPath);
                    workflowApplication.PersistableIdle = (e) => { return PersistableIdleAction.Persist; };
                    workflowApplication.OnUnhandledException = (x) =>
                    {
                        RemoveWorkflowApplication(fullPath);
                        return UnhandledExceptionAction.Abort;
                    };
                    workflowApplication.Completed = (c) =>
                    {
                        if (c.CompletionState == ActivityInstanceState.Closed)
                            RemoveWorkflowApplication(fullPath);
                    };
    
                    _WorkflowApplications.Add(fullPath, workflowApplication);
                    workflowApplication.Run();
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    RemoveWorkflowApplication(fullPath);
                }
            }
            else
            {
                DeleteWorkflowApplication(fullPath);
                CreateWorkflowApplication(fullPath);
            }
        }
    
        private void DeleteWorkflowApplication(String fullPath)
        {
            try
            {
                if (_WorkflowApplications.ContainsKey(fullPath))
                    _WorkflowApplications[fullPath].Terminate("Workflow Activity removed.");
    
                RemoveWorkflowApplication(fullPath);
            }
            catch (Exception ex)
            {
                LogError(ex);
                RemoveWorkflowApplication(fullPath);
            }
        }
    
        private void RemoveWorkflowApplication(String fullPath)
        {
            if (_WorkflowApplications.ContainsKey(fullPath))
            {
                _WorkflowApplications[fullPath] = null;
                _WorkflowApplications.Remove(fullPath);
            }
        }
    
        private void LogError(Exception exception)
        {
            if (exception != null)
            {
                String path = ConfigurationManager.AppSettings["WorkflowsPath"];
    
                if (!String.IsNullOrEmpty(path))
                {
                    String errorPath = Path.Combine(path, "Errors");
    
                    try
                    {
                        if (!Directory.Exists(errorPath))
                            Directory.CreateDirectory(errorPath);
    
                        using (StreamWriter file = File.CreateText(Path.Combine(errorPath, String.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd_HH-mm-ss")))))
                        {
                            file.WriteLine("--------------------EXCEPTION--------------------");
                            file.WriteLine(exception.Message);
                            file.WriteLine();
                            file.WriteLine("-------------------STACK TRACE-------------------");
                            file.WriteLine(exception.StackTrace);
    
                            if (exception.InnerException != null)
                            {
                                file.WriteLine();
                                file.WriteLine("-----------------INNER EXCEPTION-----------------");
                                file.WriteLine(exception.InnerException.Message);
                                file.WriteLine();
                                file.WriteLine("-------------------STACK TRACE-------------------");
                                file.WriteLine(exception.InnerException.StackTrace);
                            }
    
                            file.Close();
                        }
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                }
            }
        }
    }

    There are a couple of things to take note of here. I'm only filtering the notifications using NotifyFilters.FileName and responding to the Created, Deleted, and Renamed events. The FileSystemWatcher fires off several notifications - often duplicated - due to the way the operating system manages file-related events. To avoid the duplications and make the code a bit simpler, I limited the number of filters and events I'm monitoring. Because of this, changing a file by overwriting it will not get picked up by the FileSystemWatcher so if you want to re-deploy a XAML file, you will need to first delete it and then copy in the updated file.

    Another thing to consider is the persistence of the workflows. The workflows I had to run are not long-running so persistence is not really required (at least not yet). Workflows are pretty closely tied to persistence, however. For example, calling Unload() on a workflow will attempt to use its persistence provider to persist its state. By default, WF uses SQL Server as a persistence store but you can disable persistence altogether, create a "dummy" InstanceStore that does nothing, or create a custom InstanceStore that saves the state in whatever format you want. I didn't have access to a SQL Server instance so I debated whether or not to enable persistence at all. I ended up creating an InstanceStore that serializes the workflow to XML so that I could at least have something to monitor the ongoing states of the workflows. Here is the XmlInstanceStore:

    public class XmlInstanceStore : InstanceStore
    {
        private readonly Guid _InstanceOwnerId;
        private readonly String _Path;
    
        public XmlInstanceStore(String path)
            : this(path, Guid.NewGuid())
        {
        }
    
        public XmlInstanceStore(String path, Guid instanceOwnerId)
        {
            _Path = path;
            _InstanceOwnerId = instanceOwnerId;
        }
    
        protected override Boolean TryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout)
        {
            return EndTryCommand(BeginTryCommand(context, command, timeout, null, null));
        }
    
        protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, Object state)
        {
            EnsurePersistenceDirectoryExists();
    
            if (command is CreateWorkflowOwnerCommand)
            {
                context.BindInstanceOwner(_InstanceOwnerId, Guid.NewGuid());
            }
            else if (command is SaveWorkflowCommand)
            {
                SaveWorkflowCommand saveCommand = (SaveWorkflowCommand)command;
                Save(saveCommand.InstanceData);
            }
            else if (command is LoadWorkflowCommand)
            {
                try
                {
                    using (FileStream inputStream = new FileStream(FilePath, FileMode.Open))
                    {
                        context.LoadedInstance(InstanceState.Initialized, LoadInstanceDataFromFile(inputStream), null, null, null);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            return new CompletedAsyncResult<Boolean>(true, callback, state);
        }
    
        protected override Boolean EndTryCommand(IAsyncResult result)
        {
            return CompletedAsyncResult<Boolean>.End(result);
        }
    
        private IDictionary<XName, InstanceValue> LoadInstanceDataFromFile(Stream inputStream)
        {
            IDictionary<XName, InstanceValue> data = new Dictionary<XName, InstanceValue>();
            NetDataContractSerializer netDataContractSerializer = new NetDataContractSerializer();
            XmlDocument xmlDocument = new XmlDocument();
    
            using (XmlReader xmlReader = XmlReader.Create(inputStream))
            {
                xmlDocument.Load(xmlReader);
            }
    
            XmlNodeList instanceElements = xmlDocument.GetElementsByTagName("InstanceValue");
    
            foreach (XmlElement instanceElement in instanceElements)
            {
                XmlElement keyElement = (XmlElement)instanceElement.SelectSingleNode("descendant::key");
                XName key = (XName)DeserializeObject(netDataContractSerializer, keyElement);
    
                XmlElement valueElement = (XmlElement)instanceElement.SelectSingleNode("descendant::value");
                Object value = DeserializeObject(netDataContractSerializer, valueElement);
                InstanceValue instanceValue = new InstanceValue(value);
    
                data.Add(key, instanceValue);
            }
    
            return data;
        }
    
        private Object DeserializeObject(NetDataContractSerializer serializer, XmlElement element)
        {
            Object deserialized = null;
    
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (XmlDictionaryWriter xmlDictionaryWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream))
                {
                    element.WriteContentTo(xmlDictionaryWriter);
                    xmlDictionaryWriter.Flush();
                    memoryStream.Position = 0;
                    deserialized = serializer.Deserialize(memoryStream);
                }
            }
    
            return deserialized;
        }
    
        private void Save(IDictionary<XName, InstanceValue> instanceData)
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.LoadXml("<InstanceValues/>");
    
            foreach (KeyValuePair<XName, InstanceValue> data in instanceData)
            {
                XmlElement instanceValue = xmlDocument.CreateElement("InstanceValue");
                XmlElement key = SerializeObject("key", data.Key, xmlDocument);
                instanceValue.AppendChild(key);
    
                XmlElement value = SerializeObject("value", data.Value.Value, xmlDocument);
                instanceValue.AppendChild(value);
    
                xmlDocument.DocumentElement.AppendChild(instanceValue);
            }
    
            xmlDocument.Save(FilePath);
        }
    
        private XmlElement SerializeObject(String elementName, Object o, XmlDocument doc)
        {
            NetDataContractSerializer netDataContractSerializer = new NetDataContractSerializer();
            XmlElement xmlElement = doc.CreateElement(elementName);
    
            using (MemoryStream memoryStream = new MemoryStream())
            {
                netDataContractSerializer.Serialize(memoryStream, o);
                memoryStream.Position = 0;
                StreamReader rdr = new StreamReader(memoryStream);
                xmlElement.InnerXml = rdr.ReadToEnd();
            }
    
            return xmlElement;
        }
    
        private String FilePath
        {
            get
            {
                String fileName = Path.GetFileNameWithoutExtension(_Path);
    
                if (String.IsNullOrEmpty(fileName))
                    fileName = _InstanceOwnerId.ToString();
    
                return Path.Combine(Path.GetDirectoryName(_Path), String.Format("{0}.xml", fileName));
            }
        }
    
        private void EnsurePersistenceDirectoryExists()
        {
            try
            {
                if (!Directory.Exists(Path.GetDirectoryName(_Path)))
                    Directory.CreateDirectory(Path.GetDirectoryName(_Path));
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

    I also created a NullInstanceStore in case I wanted to configure some workflows to persist and others to do nothing.

    public class NullInstanceStore : InstanceStore
    {
        protected override Boolean TryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout)
        {
            return EndTryCommand(BeginTryCommand(context, command, timeout, null, null));
        }
    
        protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, Object state)
        {
            return new CompletedAsyncResult<Boolean>(true, callback, state);
        }
    
        protected override Boolean EndTryCommand(IAsyncResult result)
        {
            return CompletedAsyncResult<Boolean>.End(result);
        }
    }

    (You can find the AsyncResult and CompletedAsyncResult as well as the rest of the code in the accompanying project.)

    Wrapping Up

    Like I said, the service is relatively simple. I've been running it in a production environment for the past several months without any issues. The custom activities have been really easy to build and the built-in activities like StateMachine and Parallel have been a joy with which to work. Workflow Foundation has been a lot of fun, honestly.

    One last thing... If you don't want to use InstallShield Limited Edition to create an installer for the service, Microsoft released their trusty Visual Studio Installer as a Visual Studio Extension. You can find it here.

    In the next few blog posts, I'll be talking about the CodeActivity and NativeActivity activities I've built to handle all of the file brokering in the system I built for my employer. Enjoy!

    Update!

    A few people have asked me for the source code to the entire project - including the custom activities I built to create my workflows. You can download it here (4.9 MB).

  2. Applied Architecture Patterns on the Microsoft Platform

    Applied Architecture Patterns on the Microsoft Platform

    Earlier this year, I was invited to be a technical reviewer for Packt Publishing. They recently published the first book I reviewed for them, Applied Architecture Patterns on the Microsoft Platform.

    Many of the patterns books I've read have been pretty dry and sterile. Not this one! The authors really make the content come alive with practical examples and realistic use cases. They also cover several emerging technologies such as Azure, Workflow Foundation 4.5, and Multi-master Sync.

    If you're in a position where you have to make enterprise-level architectural decisions - or if you want to be in that position - this book is a must-have. In most of the chapters, the authors present common use cases and then discuss several different Microsoft technologies and how they can be used to address the situation. Each technology is weighed by their pros and cons and then graded based on several criteria. The technology that would best work is then employed and sample code is given. It's very instructive to witness the decision-making process in this way.

    I definitely recommend this book. It's well-written, concise, and insightful.


    Packt Publishing has a plethora of wonderful technical books in their inventory. They also sell their publications as eBooks in several different formats which I definitely appreciate (I read so much more now that I have a Microsoft Surface and can store all of my books in one, convenient place).

  3. Four Ways to Version Your MVC Web API

    MSDN defines a WCF Contract as "a collection of Operations that specifies what the Endpoint communicates to the outside world" and that's appropriate considering Dictionary.com defines a contract as "an agreement between two or more parties for the doing or not doing of something specified." Whenever you create a REST endpoint and it gets consumed by a client, you're establishing a contract with that client. REST is all about resources and a Web API is a contract promising that a given resource will exist at the specified URI. Like most things in life, however, things change. When a resource that is the subject of a Web API changes, you have a few options: you can take the position that the behavior of a resource is one of its intrinsic properties, hence if that changes, you really have a different resource and therefore a different URI or you can establish a version for that resource. Since the former requires clients to update their code to take advantage of the new resources and each new resource will exponentially grow and complicate the API, we'll focus on the latter and discuss versioning in this article.

    Now the question becomes, "How do I version my Web API?" There's a lot of debate on this these days. The main debate comes down to identifying the version via the URI versus identifying the version in the header. There are pro's and con's to both. With Microsoft's MVC Web Api, though, you don't have to limit yourself to just one versioning scheme. I'm going to show you four ways to version your Web API within the same project:

    1. URI using Routing Attributes
    2. Custom HTTP Header
    3. HTTP Accept Header with a header parameter
    4. HTTP Accept Header with a Vendor MIME Type

    Project Setup

    Create a new Web Project and call it "Web" with a Solution name "WebApiVersioning." Make sure "Create directory for solution" is checked. Also note that I'm placing the Solution in a folder named "Projects."

    Select the Web API template and click "OK."

    When creating a new project, it's a good idea to update all of the NuGet packages before you get started so take a few moments to do that. As of this date, Web API 2.1 was the main update I installed. Run the project to make sure everything is working properly.

    URI-based versioning

    The templated project gives us a controller named "ValuesController" by default so we'll just work with that. To make it simple for this tutorial, I'm just going to work with the GET actions but everything we do here will apply to the other actions as well. Change the ValuesController class to this:

    namespace Web.Controllers
    {
        public class ValuesController : ApiController
        {
            // GET api/values
            public IEnumerable<string> Get()
            {
                return new string[] { "value1", "value2" };
            }
    
            // GET api/values/5
            public string Get(int id)
            {
                return "value";
            }
        }
    }

    Run the project and click on "API" in the menu (optionally, you can browse to "/help"). You should see this:

    Let's update our ValuesController and add a new version of the API. Then let's use MVC's Route attribute to add a "V1" and "V2" version indicator to the URI's.

    namespace Web.Controllers
    {
        public class Values_V1Controller : ApiController
        {
            [Route("api/v1/values")]
            public IEnumerable<string> Get()
            {
                return new string[] { "value1 (V1)", "value2 (V1)" };
            }
    
            [Route("api/v1/values/{id:int}")]
            public string Get(int id)
            {
                return String.Format("value {0} (V1)", id);
            }
        }
    
        public class Values_V2Controller : ApiController
        {
            [Route("api/v2/values")]
            public IEnumerable<string> Get()
            {
                return new string[] { "value1 (V2)", "value2 (V2)" };
            }
    
            [Route("api/v2/values/{id:int}")]
            public string Get(int id)
            {
                return String.Format("value {0} (V2)", id);
            }
        }
    }

    When you run the project and browse to the API help view, you'll notice the new URI's:

    As you can see, it's quite simple to version using the URI. This way of versioning is very helpful when your clients do not have access to any other means of reaching distinct versions of your API. For example, browsers or other applications that have only a URI address option can easily reach the different versions.

    Versioning in the URI can give your clients the wrong impression, however. According to some of the REST community, a URI defines a distinct resource. In our case, we have two distinct URI's and therefor two distinct resources. This may not be what we're intending.

    It's important to note that, in this tutorial, I'm only changing the data in the ValuesController to distinguish the 2 versions. In reality, it would be the structure of the objects returned that would dictate the need to have a different version. I didn't want to get bogged down in creating a complicated scenario. Instead, I'm just focusing on several mechanisms to enable versioning.

    Using a Custom HTTP Header

    With Web API 2 and above, you can add custom constraints to your routes. A custom route constraint enables you to prevent a route from being matched unless some custom condition is matched - in this case, the value of a custom HTTP header. The official samples list on Codeplex has a custom Route Constraint that does exactly what we need.

    First, add a new Class Library project called "Core.Web." Be sure to add it to the Projects directory, at the same directory level as the WebApiVersioning solution:

    Add a folder to the Core.Web project called "Routing" and add the Microsoft Web API 2.1 Core Libraries (or whatever is the most recent version) from NuGet.

    Next, add the following classes:

    namespace Core.Web.Routing
    {
        public class VersionConstraint : IHttpRouteConstraint
        {
            private const Int32 DEFAULT_VERSION = 1;
    
            public VersionConstraint(Int32 allowedVersion)
            {
                AllowedVersion = allowedVersion;
            }
    
            public Int32 AllowedVersion
            {
                get;
                private set;
            }
    
            public Boolean Match(HttpRequestMessage request, IHttpRoute route, String parameterName, IDictionary<String, Object> values, HttpRouteDirection routeDirection)
            {
                if (routeDirection == HttpRouteDirection.UriResolution)
                {
                    Int32 version = GetVersionHeader(request) ?? DEFAULT_VERSION;
    
                    if (version == AllowedVersion)
                        return true;
                }
    
                return false;
            }
    
            private Int32? GetVersionHeader(HttpRequestMessage request)
            {
                String versionAsString = String.Empty;
                IEnumerable<String> headerValues;
    
                if (request.Headers.TryGetValues("api-version", out headerValues) && headerValues.Count() == 1)
                    versionAsString = headerValues.First();
    
                Int32 version;
    
                if (!String.IsNullOrEmpty(versionAsString) && Int32.TryParse(versionAsString, out version))
                    return version;
    
                return null;
            }
        }
    }
    
    namespace Core.Web.Routing
    {
        public class VersionedRoute : RouteFactoryAttribute
        {
            public VersionedRoute(String template, Int32 allowedVersion)
                : base(template)
            {
                AllowedVersion = allowedVersion;
            }
    
            public Int32 AllowedVersion
            {
                get;
                private set;
            }
    
            public override IDictionary<String, Object> Constraints
            {
                get
                {
                    var constraints = new HttpRouteValueDictionary();
                    constraints.Add("version", new VersionConstraint(AllowedVersion));
                    return constraints;
                }
            }
        }
    }

    Add a Solution reference to the Web project for Core.Web and update the the Values_V1Controller and Values_V2Controller to this:

    namespace Web.Controllers
    {
        public class Values_V1Controller : ApiController
        {
            [VersionedRoute("api/values", 1)]
            [Route("api/v1/values")]
            public IEnumerable<string> Get()
            {
                return new string[] { "value1 (V1)", "value2 (V1)" };
            }
    
            [VersionedRoute("api/values/{id:int}", 1)]
            [Route("api/v1/values/{id:int}")]
            public string Get(int id)
            {
                return String.Format("value {0} (V1)", id);
            }
        }
    
        public class Values_V2Controller : ApiController
        {
            [VersionedRoute("api/values", 2)]
            [Route("api/v2/values")]
            public IEnumerable<string> Get()
            {
                return new string[] { "value1 (V2)", "value2 (V2)" };
            }
    
            [VersionedRoute("api/values/{id:int}", 2)]
            [Route("api/v2/values/{id:int}")]
            public string Get(int id)
            {
                return String.Format("value {0} (V2)", id);
            }
        }
    }

    Now, run the project and open Fiddler. Switch Fiddler to the Composer tab and type the URI for the Values controller ("http://localhost:[YOUR PORT]/api/values"). In the "Request Headers" box, add "api-version: 1":

    Next, click the "Execute" button. You should have a response in the grid on the left. Select the response for "/api/values" and switch to the "Inspectors" tab. In the bottom half of the Inspectors tab, select the JSON tab. It should look like this:

    Now switch back to the Composer tab and change the "api-version" to 2. Execute it again, select the new response, and look at it in the Inspectors tab.

    If you take a look at the API help view, you'll notice that the routes defined in the VersionedRoute attributes appear only with Values_V1:

    It will be important for you to add your own documentation to let your clients know they can optionally set the "api-version" custom header to access the other versions. Another thing to consider is that you might want to name the first version of your controller without the "_V1" and start naming the subsequent versions with "_V2". This way, the subject of your API's (e.g. "Values" in this case) appears to stay the same from version-to-version (e.g. "/api/values") for the majority of the requests only to be differentiated by the headers.

    Using a HTTP Accept Header with a header parameter

    A lot of people favor using the HTTP Accept header to request the version of the resource. The HTTP Accept header defines the Content-Types that are acceptable for the response. Or, in other words, it is a way for a client to specify the media type of the response content it is expecting. Examples are "text/plain," "image/jpeg," "application/xml," and "application/json." The HTTP Accept header optionally allows you to add parameters to define other aspects of the request. We're going to use "version=[SOME NUMBER]" as a parameter to specify the version we want.

    Update the GetVersionHeader method of the VersionConstraint class to this:

    private Int32? GetVersionHeader(HttpRequestMessage request)
    {
        String versionAsString = String.Empty;
        IEnumerable<String> headerValues;
    
        if (request.Headers.TryGetValues("api-version", out headerValues) && headerValues.Count() == 1)
            versionAsString = headerValues.First();
        else
        {
            var accept = request.Headers.Accept.Where(a => a.Parameters.Count(p => p.Name == "version") > 0);
    
            if (accept.Any())
                versionAsString = accept.First().Parameters.Single(s => s.Name == "version").Value;
        }
    
        Int32 version;
    
        if (!String.IsNullOrEmpty(versionAsString) && Int32.TryParse(versionAsString, out version))
            return version;
    
        return null;
    }
    

    Now run the project and open Fiddler again. This time, add an Accept header for "application/json" and a parameter of "version=1"

    Execute the request and examine the result in the Inspectors tab like you did previously.

    Now let's change the request to XML. Replace the "Accept: application/json;version=1" header with "Accept: application/xml;version=2" and execute the request. The result in the Inspectors tab should look like this:

    Using a HTTP Accept Header with a Vendor MIME Type

    Many in the REST camp do not like to use the generic MIME Types to request a resource (e.g. "application/xml"). Their argument is that they aren't requesting any ol' resource, they're requesting a resource with a specific schema or representation. Vendor MIME Types provide a way to specify the resource in a more granular way. Personally, I'm not a fan of custom Vendor MIME Types. It feels like a solution in search of a problem. But, for those of you that want to go this route, here's a way to implement versioning for these types of requests.

    Update the GetVersionHeader method of the VersionConstraint class to this:

    private Int32? GetVersionHeader(HttpRequestMessage request)
    {
        String versionAsString = String.Empty;
        IEnumerable<String> headerValues;
    
        if (request.Headers.TryGetValues("api-version", out headerValues) && headerValues.Count() == 1)
            versionAsString = headerValues.First();
        else
        {
            var accept = request.Headers.Accept.Where(a => a.Parameters.Count(p => p.Name == "version") > 0);
    
            if (accept.Any())
                versionAsString = accept.First().Parameters.Single(s => s.Name == "version").Value;
            else
            {
                accept = request.Headers.Accept.Where(a => Regex.IsMatch(a.MediaType, @"^application\/vnd\..*-v\d+\+.*$", RegexOptions.Singleline));
    
                if (accept.Any())
                    versionAsString = Regex.Match(accept.First().MediaType, @"^application\/vnd\..*-v(?<version>\d+)\+.*$", RegexOptions.Singleline).Groups["version"].Value;
            }
        }
    
        Int32 version;
    
        if (!String.IsNullOrEmpty(versionAsString) && Int32.TryParse(versionAsString, out version))
            return version;
    
        return null;
    }

    The Vendor MIME Type follows a specific pattern of "application/vnd.some.namespaced.resource+json". To version this, the convention is shaping up to look like this: application/vnd.some.namespaced.resource-v1+json

    The regular expressions in the GetVersionHeader method checks for this pattern and extracts the version if present.

    Run the project, open Fiddler, and compose a request like this:

    When you inspect the response, you should see the V2 version in the JSON tab. Now change the Accept to "application/vnd.webapiversioning.string-v2+xml" and execute the request. When you inspect the response you'll notice that it's still JSON and not XML! The reason is that MVC Web API doesn't parse Vendor MIME Types like it does the standard, generic MIME Types. Instead, Microsoft leaves it for you to create you own media type formatters to handle such cases.

    Add a new folder to the Core.Web project called "Extensions" and add the following class:

    namespace Core.Web.Extensions
    {
        public static class TypeExtensions
        {
            public static Type GetEnumerableType(this Type type)
            {
                if (IsIEnumerable(type))
                    return type.GetGenericArguments()[0];
                else
                {
                    foreach (var i in type.GetInterfaces())
                    {
                        if (IsIEnumerable(i))
                            return i.GetGenericArguments()[0];
                    }
                }
    
                return null;
            }
    
            private static Boolean IsIEnumerable(Type type)
            {
                return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
            }
        }
    }

    Next, add a new folder called "Http" to the Core.Web project and add the following classes:

    namespace Core.Web.Http
    {
        public class TypedJsonMediaTypeFormatter : JsonMediaTypeFormatter
        {
            private readonly Type _ResourceType;
    
            public TypedJsonMediaTypeFormatter(Type resourceType, MediaTypeHeaderValue mediaType)
            {
                _ResourceType = resourceType;
                SupportedMediaTypes.Clear();
                SupportedMediaTypes.Add(mediaType);
            }
    
            public override Boolean CanReadType(Type type)
            {
                return _ResourceType == type || _ResourceType == type.GetEnumerableType();
            }
    
            public override Boolean CanWriteType(Type type)
            {
                return _ResourceType == type || _ResourceType == type.GetEnumerableType();
            }
        }
    }
    namespace Core.Web.Http
    {
        public class TypedXmlMediaTypeFormatter : XmlMediaTypeFormatter
        {
            private readonly Type _ResourceType;
    
            public TypedXmlMediaTypeFormatter(Type resourceType, MediaTypeHeaderValue mediaType)
            {
                _ResourceType = resourceType;
                SupportedMediaTypes.Clear();
                SupportedMediaTypes.Add(mediaType);
            }
    
            public override Boolean CanReadType(Type type)
            {
                return _ResourceType == type || _ResourceType == type.GetEnumerableType();
            }
    
            public override Boolean CanWriteType(Type type)
            {
                return _ResourceType == type || _ResourceType == type.GetEnumerableType();
            }
        }
    }

    You will need to add a using for Core.Web.Extensions to each of these classes.

    These formatters, when registered with the application for a particular type, will format the response appropriately. Here is how you would perform that registration in Global.asax:

    namespace Web
    {
        public class WebApiApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                GlobalConfiguration.Configure(WebApiConfig.Register);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
    
                GlobalConfiguration.Configuration.Formatters.Add(new TypedXmlMediaTypeFormatter(typeof(String), new MediaTypeHeaderValue("application/vnd.webapiversioning.string-v1+xml")));
                GlobalConfiguration.Configuration.Formatters.Add(new TypedJsonMediaTypeFormatter(typeof(String), new MediaTypeHeaderValue("application/vnd.webapiversioning.string-v1+json")));
                GlobalConfiguration.Configuration.Formatters.Add(new TypedXmlMediaTypeFormatter(typeof(String), new MediaTypeHeaderValue("application/vnd.webapiversioning.string-v2+xml")));
                GlobalConfiguration.Configuration.Formatters.Add(new TypedJsonMediaTypeFormatter(typeof(String), new MediaTypeHeaderValue("application/vnd.webapiversioning.string-v2+json")));
            }
        }
    }

    What we're saying with these registrations is that when a request is made for the type of resource specified in the Accept header and the response is of that type, then use the appropriate media type formatter to serialize the response.

    Now run the project, open Fiddler, and compose a request with an Accept header value "application/vnd.webapiversioning.string-v2+xml". You should now see an XML response.

    Again, I'm just focusing on the versioning mechanisms in this tutorial and not a typical, real-life scenario. Your Web API's will likely deal with resources that are more complex so your registrations would likely look something like this:

    GlobalConfiguration.Configuration.Formatters.Add(new TypedJsonMediaTypeFormatter(typeof(Product), new MediaTypeHeaderValue("application/vnd.mycompany.product-v1+json")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedJsonMediaTypeFormatter(typeof(Product_V2), new MediaTypeHeaderValue("application/vnd.mycompany.product-v2+json")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedXmlMediaTypeFormatter(typeof(Product), new MediaTypeHeaderValue("application/vnd.mycompany.product-v1+xml")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedXmlMediaTypeFormatter(typeof(Product_V2), new MediaTypeHeaderValue("application/vnd.mycompany.product-v2+xml")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedJsonMediaTypeFormatter(typeof(Customer), new MediaTypeHeaderValue("application/vnd.mycompany.customer-v1+json")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedJsonMediaTypeFormatter(typeof(Customer_V2), new MediaTypeHeaderValue("application/vnd.mycompany.customer-v2+json")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedXmlMediaTypeFormatter(typeof(Customer), new MediaTypeHeaderValue("application/vnd.mycompany.customer-v1+xml")));
    GlobalConfiguration.Configuration.Formatters.Add(new TypedXmlMediaTypeFormatter(typeof(Customer_V2), new MediaTypeHeaderValue("application/vnd.mycompany.customer-v2+xml")));
    

    I hope this tutorial gives you enough options to successfully version your Web API. If you have any questions or suggestions on how I can make this better, please leave a comment.

    You can download the source for this article here.

  4. Next Steps: Adding a Service Layer and Validation

    I've been using the Repository and UnitOfWork I created in my previous post in all of my projects with quite a lot of success. One of the things I noticed, though, is that there were occasions when I repeated the same LINQ query in several controller methods with the same business logic applied to the data. I also had cases where there was validation logic in my controllers that needed to be shared. That's no bueno. I set out to centralize that code but quickly realized that a lot more work was needed.

    I won't go through all of the project setup - you can see that in my previous post - but I will point out some changes I made in my updated code. I mentioned last time that it would be best to separate the ORM-specific repository and unit of work implementations into their own assembly so I did that in my updated Recreation project (see Core.EntityFramework). I also updated Entity Framework to version 6 and added asynchronous methods to the Repository and UnitOfWork interfaces and implementations.

    One of the cases where I noticed I was repeating the same query against the data across multiple controller methods was when I added the same dropdown list to several views. An example of this might be a view to create a Reservation and another view to edit a Reservation. Both controller methods might create SelectLists for facilities and guests and pass them to the view using the ViewBag. There would also likely be two methods for Edit and two for Create where these services would be used - one for the initial loading of the view and another for the HttpPost. The queries for facilities and guests would be the same in all of the controller methods so it makes sense to centralize that code somewhere. When I searched online for a pattern that fit this use case, the Service Layer pattern seemed to be the most appropriate:

    A Service Layer defines an application's boundary and its set of available operations from the perspective of interfacing client layers. It encapsulates the application's business logic, controlling transactions and coordinating responses in the implementation of its operations.

    The Service Layer classes for the Guest and Facility domain models look like this:

    namespace Recreation.Data.Services
    {
        public interface IFacilityService : IDisposable
        {
            Dictionary<Int32, String> GetSelections();
        }
    
        public class FacilityService : IFacilityService
        {
            private readonly IRepository<Facility> _FacilityRepository;
    
            public FacilityService(IRepository<Facility> facilityRepository)
            {
                _FacilityRepository = facilityRepository;
            }
    
            public Dictionary<Int32, String> GetSelections()
            {
                return (from q in _FacilityRepository.GetQueryable()
                        select new { q.Id, q.Name }).OrderBy(o => o.Name).ToDictionary(d => d.Id, d => d.Name);
            }
    
            public void Dispose()
            {
                if (_FacilityRepository != null)
                    _FacilityRepository.Dispose();
            }
        }
    }
    namespace Recreation.Data.Services
    {
        public interface IGuestService : IDisposable
        {
            Dictionary<Int32, String> GetSelections();
        }
    
        public class GuestService : IGuestService
        {
            private readonly IRepository<Guest> _GuestRepository;
    
            public GuestService(IRepository<Guest> facilityRepository)
            {
                _GuestRepository = facilityRepository;
            }
    
            public Dictionary<Int32, String> GetSelections()
            {
                return (from q in _GuestRepository.GetQueryable()
                        select new { q.Id, q.FirstName, q.LastName }).OrderBy(o => o.LastName).ToDictionary(d => d.Id, d => String.Format("{0}, {1}", d.LastName, d.FirstName));
            }
    
            public void Dispose()
            {
                if (_GuestRepository != null)
                    _GuestRepository.Dispose();
            }
        }
    }

    You will then need to register these services with Unity so that you can inject them into the controller:

    namespace Recreation.Web.App_Start
    {
        /// <summary>
        /// Specifies the Unity configuration for the main container.
        /// </summary>
        public class UnityConfig
        {
            #region Unity Container
    
            private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
            {
                var container = new UnityContainer();
                RegisterTypes(container);
                return container;
            });
    
            /// <summary>
            /// Gets the configured Unity container.
            /// </summary>
            public static IUnityContainer GetConfiguredContainer()
            {
                return container.Value;
            }
    
            #endregion
    
            /// <summary>Registers the type mappings with the Unity container.</summary>
            /// <param name="container">The unity container to configure.</param>
            /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
            /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
            public static void RegisterTypes(IUnityContainer container)
            {
                // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
                // container.LoadConfiguration();
                // TODO: Register your types here
    
                //Data Access
                container.RegisterType<IObjectContextAdapter, RecreationContext>(new PerRequestLifetimeManager());
                container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
                container.RegisterType<IUnitOfWork, UnitOfWork>();
    
                //Services
                container.RegisterType<IFacilityService, FacilityService>();
                container.RegisterType<IGuestService, GuestService>();
            }
        }
    }

    The ReservationController would then consume those services like this (I only included the Edit methods):

    namespace Recreation.Web.Controllers
    {
        public class ReservationController : Controller
        {
            private readonly IRepository<Reservation> _ReservationRepository;
            private readonly IFacilityService _FacilityService;
            private readonly IGuestService _GuestService;
            private readonly IUnitOfWork _UnitOfWork;
    
            public ReservationController(IRepository<Reservation> reservationRepository, IFacilityService facilityService, IGuestService guestService, IUnitOfWork unitOfWork)
            {
                _ReservationRepository = reservationRepository;
                _FacilityService = facilityService;
                _GuestService = guestService;
                _UnitOfWork = unitOfWork;
            }
    
            public ActionResult Index()
            {
                return View(_ReservationRepository.GetAll());
            }
    
            public ActionResult Edit(Int32 id = 0)
            {
                var reservation = _ReservationRepository.SingleOrDefault(r => r.Id == id);
    
                if (reservation == null)
                    return HttpNotFound();
    
                ViewBag.FacilityId = new SelectList(_FacilityService.GetSelections(), "Key", "Value", reservation.FacilityId);
                ViewBag.GuestId = new SelectList(_GuestService.GetSelections(), "Key", "Value", reservation.GuestId);
                
                return View(reservation);
            }
    
            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult Edit(Reservation reservation)
            {
                _ReservationRepository.Attach(reservation, EntityStatus.Modified);
    
                if (ModelState.IsValid)
                {
                    try
                    {
                        _UnitOfWork.SaveChanges();
                    }
                    catch (ConcurrencyException)
                    {
                        ModelState.AddModelError(String.Empty, "The record you attempted to edit was modified by another user after you got the original value. Your edit operation was canceled. If you still want to edit this record, save it again.");
                    }
                    catch (Exception)
                    {
                        ModelState.AddModelError(String.Empty, "Unable to save changes. Please try again.");
                    }
                }
    
                ViewBag.FacilityId = new SelectList(_FacilityService.GetSelections(), "Key", "Value", reservation.FacilityId);
                ViewBag.GuestId = new SelectList(_GuestService.GetSelections(), "Key", "Value", reservation.GuestId);
    
                return View(reservation);
            }
    
            protected override void Dispose(Boolean disposing)
            {
                if (disposing)
                {
                    _ReservationRepository.Dispose();
                    _FacilityService.Dispose();
                    _GuestService.Dispose();
                    _UnitOfWork.Dispose();
                }
    
                base.Dispose(disposing);
            }
        }
    }

    When the controller is created, Unity instantiates the Repository<Reservation> object specified in the controller's constructor. When it sees the Service Layer references, it instantiates the Repository<> objects in their respective constructors before instantiating the Service Layer objects.

    The Edit.cshtml view looks like this:

    @model Recreation.Data.Models.Reservation
    @{
        ViewBag.Title = "Edit Reservation";
    }
    <p>@Html.ActionLink("View Reservations", "Index")</p>
    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>@ViewBag.Title</legend>
            @Html.HiddenFor(m => m.Id)
            @Html.HiddenFor(m => m.ConcurrencyToken)
            <ol>
                <li>
                    @Html.LabelFor(m => m.FacilityId)
                    @Html.DropDownList("FacilityId", "Select a Facility")
                    @Html.ValidationMessageFor(m => m.FacilityId)
                </li>
                <li>
                    @Html.LabelFor(m => m.GuestId)
                    @Html.DropDownList("GuestId", "Select a Guest")
                    @Html.ValidationMessageFor(m => m.GuestId)
                </li>
                <li>
                    @Html.LabelFor(m => m.StartDateTime)
                    @Html.TextBoxFor(m => m.StartDateTime)
                    @Html.ValidationMessageFor(m => m.StartDateTime)
                </li>
                <li>
                    @Html.LabelFor(m => m.EndDateTime)
                    @Html.TextBoxFor(m => m.EndDateTime)
                    @Html.ValidationMessageFor(m => m.EndDateTime)
                </li>
            </ol>
            <input type="submit" value="Submit" />
        </fieldset>
    }

    Validation

    Where to place validation logic - especially logic that isn't easily covered by MVC's HTML helper methods - is something that I've struggled with when it comes to MVC. Once I started down the path of creating a Service Layer, though, this became somewhat easier. I found this article on asp.net that explains placing validation logic in a Service Layer:

    So, application flow control logic belongs in a controller and data access logic belongs in a repository. In that case, where do you put your validation logic? One option is to place your validation logic in a service layer. A service layer is an additional layer in an ASP.NET MVC application that mediates communication between a controller and repository layer. The service layer contains business logic. In particular, it contains validation logic.

    Stephen Walther's approach is a good one but it didn't quite smell right to me - particularly with my Service Layer approach and my usage of the Inversion of Control (IoC) pattern using Unity. That led me to this question on Stack Overflow. The accepted answer by "Steven" was exactly what I needed. After some scrubbing and polishing, I ended up with what I think is an excellent way to do model and data validation in the Service Layer with MVC. I won't bore you with the details because "Steven" does a pretty good job of explaining everything.

    The ReservationValidator looks like this:

    namespace Recreation.Data.Validation
    {
        public class ReservationValidator : Validator<Reservation>
        {
            protected override IEnumerable<ValidationResult> Validate(Reservation entity)
            {
                if (entity.FacilityId <= 0)
                    yield return new ValidationResult("FacilityId", "You must select a Facility");
    
                if (entity.GuestId <= 0)
                    yield return new ValidationResult("GuestId", "You must select a Guest");
    
                if (entity.EndDateTime < entity.StartDateTime)
                    yield return new ValidationResult("EndDateTime", "The End Date/Time must be greater than the Start Date/Time");
    
                if ((entity.EndDateTime - entity.StartDateTime).TotalHours < 1)
                    yield return new ValidationResult("EndDateTime", "The End Date/Time must be at least one hour greater than the Start Date/Time");
            }
        }
    }

    And the ReservationService looks like this:

    namespace Recreation.Data.Services
    {
        public interface IReservationService : IDisposable
        {
            IEnumerable<Reservation> GetAll();
            Reservation GetById(Int32 id);
            void Update(Reservation reservation);
        }
    
        public class ReservationService : IReservationService
        {
            private readonly IRepository<Reservation> _ReservationRepository;
            private readonly IUnitOfWork _UnitOfWork;
            private readonly IValidationProvider _ValidationProvider;
    
            public ReservationService(IRepository<Reservation> reservationRepository, IUnitOfWork unitOfWork, IValidationProvider validationProvider)
            {
                _ReservationRepository = reservationRepository;
                _UnitOfWork = unitOfWork;
                _ValidationProvider = validationProvider;
            }
    
            public IEnumerable<Reservation> GetAll()
            {
                return _ReservationRepository.GetAll();
            }
    
            public Reservation GetById(Int32 id)
            {
                return _ReservationRepository.SingleOrDefault(r => r.Id == id);
            }
    
            public void Update(Reservation reservation)
            {
                _ValidationProvider.Validate(reservation);
                _ReservationRepository.Attach(reservation, EntityStatus.Modified);
                _UnitOfWork.SaveChanges();
            }
    
            public void Dispose()
            {
                if (_ReservationRepository != null)
                    _ReservationRepository.Dispose();
    
                if (_UnitOfWork != null)
                    _UnitOfWork.Dispose();
            }
        }
    }

    Registering the validators with Unity is pretty slick. The UnityConfig code now looks like this:

    namespace Recreation.Web.App_Start
    {
        /// <summary>
        /// Specifies the Unity configuration for the main container.
        /// </summary>
        public class UnityConfig
        {
            #region Unity Container
    
            private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
            {
                var container = new UnityContainer();
                RegisterTypes(container);
                return container;
            });
    
            /// <summary>
            /// Gets the configured Unity container.
            /// </summary>
            public static IUnityContainer GetConfiguredContainer()
            {
                return container.Value;
            }
    
            #endregion
    
            /// <summary>Registers the type mappings with the Unity container.</summary>
            /// <param name="container">The unity container to configure.</param>
            /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
            /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
            public static void RegisterTypes(IUnityContainer container)
            {
                // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
                // container.LoadConfiguration();
                // TODO: Register your types here
    
                //Data Access
                container.RegisterType<IObjectContextAdapter, RecreationContext>(new PerRequestLifetimeManager());
                container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
                container.RegisterType<IUnitOfWork, UnitOfWork>();
    
                //Services
                container.RegisterType<IFacilityService, FacilityService>();
                container.RegisterType<IGuestService, GuestService>();
                container.RegisterType<IReservationService, ReservationService>();
    
                //Validators
                container.RegisterInstance(typeof(IValidationProvider), new ValidationProvider(type => (IValidator)container.Resolve(typeof(Validator<>).MakeGenericType(type))));
                container.RegisterType(typeof(Validator<>), typeof(NullValidator<>));
                container.RegisterType<Validator<Reservation>, ReservationValidator>();
            }
        }
    }

    Using Func<Type, IValidator> as a Factory Pattern to instantiate the appropriate Validator based on the calling type is truly inspired. Kudos to "Steven" for coming up with that one!

    The ReservationController can now be updated to look like this:

    namespace Recreation.Web.Controllers
    {
        public class ReservationController : Controller
        {
            private readonly IReservationService _ReservationService;
            private readonly IFacilityService _FacilityService;
            private readonly IGuestService _GuestService;
    
            public ReservationController(IReservationService reservationService, IFacilityService facilityService, IGuestService guestService)
            {
                _ReservationService = reservationService;
                _FacilityService = facilityService;
                _GuestService = guestService;
            }
    
            public ActionResult Index()
            {
                return View(_ReservationService.GetAll());
            }
    
            public ActionResult Edit(Int32 id = 0)
            {
                var reservation = _ReservationService.GetById(id);
    
                if (reservation == null)
                    return HttpNotFound();
    
                ViewBag.FacilityId = new SelectList(_FacilityService.GetSelections(), "Key", "Value", reservation.FacilityId);
                ViewBag.GuestId = new SelectList(_GuestService.GetSelections(), "Key", "Value", reservation.GuestId);
    
                return View(reservation);
            }
    
            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult Edit(Reservation reservation)
            {
                try
                {
                    _ReservationService.Update(reservation);
                }
                catch (ValidationException vex)
                {
                    ModelState.AddErrors(vex);
                }
                catch (ConcurrencyException)
                {
                    ModelState.AddModelError(String.Empty, "The record you attempted to edit was modified by another user after you got the original value. Your edit operation was canceled. If you still want to edit this record, save it again.");
                }
                catch (Exception)
                {
                    ModelState.AddModelError(String.Empty, "Unable to save changes. Please try again.");
                }
    
                ViewBag.FacilityId = new SelectList(_FacilityService.GetSelections(), "Key", "Value", reservation.FacilityId);
                ViewBag.GuestId = new SelectList(_GuestService.GetSelections(), "Key", "Value", reservation.GuestId);
    
                return View(reservation);
            }
    
            protected override void Dispose(Boolean disposing)
            {
                if (disposing)
                {
                    _ReservationService.Dispose();
                    _FacilityService.Dispose();
                    _GuestService.Dispose();
                }
    
                base.Dispose(disposing);
            }
        }
    }

    These changes make the controller much more streamlined. I hope you can get some good use out of this. I've been using combinations of the Repository, UnitOfWork, and Service Layer patterns for the past several months and my projects have resulted in easier-to-read code and better maintainability. You can grab the code for the new Recreation project here.

  5. HTML Encode Clipboard Contents in Visual Studio 2013

    I use Visual Studio's HTML (Web Forms) Editor to author the articles for this blog because the Split screen feature, which allows me to easily see both the Design editor and Source editor at the same time, helps me ensure the markup is as minimal as possible. When Visual Studio 2013 was released, though, I noticed that one of the features I often used, Edit -> Paste Special, was missing. In previous versions, this command would paste the clipboard contents into the HTML Source Editor as HTML-encoded text. This was especially useful when I wanted to paste HTML between <pre></pre> tags so that the formatting was preserved and any angle brackets were not rendered as actual markup. Other things like quotation marks, apostrophies, and angle brackets in C# code (like the syntax for generics) needs to be HTML encoded so that they validate and appear as expected when output as HTML. So I created a windowless C# application that launches from Visual Studio's External Tools support and HTML-encodes anything placed in the clipboard. Here's how I did it:

    Create a new C# Console Application:

    When the new project opens, right-click on the Project in Solution Explorer and select Properties. Change the Output Type to "Windows Application":

    Next, add references to System.Web and System.Windows.Forms:

    Change the code in Program.cs to this:

    using System;
    using System.Web;
    using System.Windows.Forms;
    
    namespace HtmlEncodeClipboard
    {
        class Program
        {
            [STAThread]
            static void Main()
            {
                Clipboard.SetText(HttpUtility.HtmlEncode(Clipboard.GetText()));
            }
        }
    }

    Compile the project and make sure it builds. Then switch the Solution Configuration to Release and build it again so that we can get a release build.

    Now create a new Web application (ASP.NET, MVC, whatever - it doesn't matter). When the project opens, open Tools -> External Tools...

    Click the "Add" button then type a name in the Title field. For Command, browse to the bin/Release folder of your HtmlEncodeClipboard project and select the HtmlEncodeClipboard.exe executable.

    You can move the External Tool up or down the list to position it where you want in the Tools menu. Take note of where you position it, though. We'll use that information in a minute.

    To test the external tool, add a HTML page to your project (or a new View if you're in a MVC project - we just need the HTML editor). Select some markup in the editor, copy it (Ctrl + C or Edit -> Copy), go to the Tools menu and select your new command, then paste the clipboard contents (Ctrl + V or Edit -> Paste). Hopefully the result is HTML encoded text in the editor.

    Now let's add this to Visual Studio's Edit menu. Select Tools -> Customize... Switch to the Commands tab and select "Edit" from the Menu Bar dropdown list:

    Scroll down and select "Cycle Clipboard Ring" then click "Add Command..."

    In the Add Command dialog, scroll down and select "Tools" then scroll through the Commands until you find the command you added earlier. This is where it's important that you noted in which position you placed your command. I positioned mine as the third command in the Tools list so here I would choose "External Command 3."

    Next, click "Modify Selection" and rename your new menu item:

    Now you can select your command from the Edit menu, too:

    To add a keyboard shortcut, go to Tools -> Options and select "Keyboard" under the "Environment" node. In the "Show Commands Containing:" textbox, type the name of your command (in my case, the name is "ExternalCommand3" - remember the position you chose earlier). I'm only interested in using this command in the HTML Source Editor so I selected that in the "Use new shortcut in:" dropdown list. In "Press shortcut keys:" I used the "Ctrl + Shift + Alt + E" keystroke sequence because it wasn't previously assigned to anything. Make sure you click the "Assign" button after you find a keystroke sequence that works for you.

    I hope this is helpful for you. Using Visual Studio's External Tools feature makes it very easy to enhance your productivity.