Home > English > Building a generic service using WCF Web API – Part II

Building a generic service using WCF Web API – Part II

Okay, so we now have our service definition in place. Let’s take a look and see what it takes to get this service exposed in our web project. The first place that we start is the Global.asax.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.Routing;
using Microsoft.ApplicationServer.Http;
using Domain;
using Domain2;
using Ninject;
using Microsoft.ApplicationServer.Http.Activation;

namespace Gofer.Web
{
    public class Global : System.Web.HttpApplication
    {
        public const string ACCOUNT = "Domain";
        public const string EGOV = "Domain2";

        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.SetDefaultHttpConfiguration(new WebApiConfiguration() 
                {
                    CreateInstance = (serviceType, context, request) => CreateInstance(serviceType),
                    EnableTestClient = true 
                });

            RouteTable.Routes.MapServiceRouteForAssemblyOf<Customers>(ACCOUNT);
            RouteTable.Routes.MapServiceRouteForAssemblyOf<rv_o_Organization>(EGOV);
        }

        private object CreateInstance(Type serviceType)
        {
            object result = null;
            IKernel kernel = new StandardKernel();
            try
            {
                // The following is a sample entry for using the MapServiceRoute method:
                // RouteTable.Routes.MapServiceRoute<GoferService<Customers>>("Customer");
                // Hence the reason we need to pull the generic type from the GoferService.
                var genericType = serviceType.GetGenericArguments().FirstOrDefault();
                DataAccessRules rules = null;
                switch (genericType.Namespace)
                {
                    case ACCOUNT:
                        rules = GetAccountAtAGlanceRules(genericType);
                        break;
                    case EGOV:
                        rules = GetEgovRules(genericType);
                        break;
                }

                kernel.Bind(serviceType).ToSelf().WithConstructorArgument("rules", rules);

                result = kernel.Get(serviceType);
            }
            catch { }

            return result;
        }

        #region Rules

        private DataAccessRules GetAccountAtAGlanceRules(Type type)
        {
            var result = new DataAccessRules();
            result.AssemblyOf(type)
                .SetConnectionStringKey("aag_ConnectionString")
                .ShouldMap(x => x.Namespace == "Domain");
            return result;
        }
        private DataAccessRules GetEgovRules(Type type)
        {
            var result = new DataAccessRules();
            result.AssemblyOf(type)
                .SetConnectionStringKey("emgov_ConnectionString")
                .ShouldMap(x => x.Namespace == "Domain2");
            return result;
        }

        #endregion
    };
}

Just like any of the other examples you have seen with the WCF Web API, we are using the Routes property off of the RouteTable and calling the SetDefaultHttpConfiguration. We are doing something a little special in that we are using a lambda expression to call the CreateInstance(…) method as well as passing in the service type. On the next two lines of code, we see that we are calling the MapServiceRouteForAssemblyOf<T>(…) generic extension method. It is the combination of the CreateInstance() method and the MapServiceRouteForAssemblyOf<T>(…) generic extension method that makes everything work correctly.

Let’s look at the CreateInstance() method first. In this method we are setting up our dependency injection (DI) container. I am using Ninject for this example but you could use MEF or Unity or whatever you wanted as well. Next I am interrogating the Type object that was passed in. I know by convention that the only service that I am going to be dealing with is the generic service we created in the last blog post. Because of that, I know that the type is a generic type and I know that it only has one generic argument. Based on the generic type information, I examine what the namespace is for the domain object. I then perform a simple switch statement and get the correct DataAccessRules object for the corresponding namespace. This gives me the flexibility to support multiple domain objects that represent different data models and not need to recompile or do any additions with the exception of the MapServiceRouteForAssemblyOf<T>(…) call.

Finally, I call the Bind method on the IKernel object and tell it to pass in the newly created DataAccessRules object as a constructor parameter. I then ask the kernel to resolve the service type that was originally passed into the method and return that new object.

So far, we have seen how we are setting up our DI container and injecting a DataAccessRules object into the constructor. What we haven’t seen yet is how we are getting data access for all of our domain objects. Let’s take a look at the individual methods that get us our DataAccessRules object and then we will turn our attention to how we handle this for all of our domain objects.

Our DataAccessRules objects are created by either the GetAccountAtAGlanceRules(…) method or the GetEgovRules(…) method. The thing to take into consideration here is we are telling the rules object what connection string to use from the web.config file as well as specifying the namespace that we only want to load our objects from.

Now let’s shift gears and take a look at the MapServiceRouteForAssemblyOf<T>(…) generic extension method.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.ApplicationServer.Http;
using Microsoft.ApplicationServer.Http.Activation;
using Gofer.Web;

namespace System.Web.Routing
{
    public static class RouteCollectionExtension
    {
        public static void MapServiceRouteForAssemblyOf<T>(this RouteCollection routes,
            string domain,
            HttpConfiguration configuration = null,
            object constraints = null,
            bool useMethodPrefixForHttpMethod = true)
        {
            var type = typeof(T);
            var domainAssembly = System.Reflection.Assembly.GetAssembly(type);
            var types = domainAssembly.GetTypes().Where(x => x.Namespace == domain);

            foreach (var item in types)
            {
                string routePrefix = item.Name;
                Type gs = typeof(GoferService<>);
                Type constructed = gs.MakeGenericType(new Type[] { item });
                routes.MapServiceRoute(constructed, routePrefix, configuration, constraints,
                    useMethodPrefixForHttpMethod);
            }
        }
        public static void MapServiceRoute(this RouteCollection routes,
            Type serviceType,
            string routePrefix,
            HttpConfiguration configuration = null,
            object constraints = null,
            bool useMethodPrefixForHttpMethod = true)
        {
            if (configuration == null)
            {
                configuration = routes.GetDefaultHttpConfiguration();
            }

            if (routes == null)
            {
                throw new ArgumentNullException("routes");
            }

            var route = new WebApiRoute(routePrefix,
                new HttpServiceHostFactory() { Configuration = configuration }, serviceType);
            route.Constraints = new RouteValueDictionary(constraints);
            routes.Add(route);
        }
    };
}

Okay, so if we look at the first method, we see that we are accessing the assembly for which the type belongs. We then load only the objects that match the namespace that was passed into the method. Finally we loop through each type and construct the generic service that we created in our last post. With a single line entry that called the MapServiceRouteForAssemblyOf<T> we are able to map service routes for all of our objects. It is actually the second method that does the routing which is very easy to follow.

Let’s look at one last piece of the puzzle, the web.config:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <appSettings>
    <add key="aag_ConnectionString" value="Data Source=(local);Initial Catalog=AccountsAtAGlance;Integrated Security=SSPI;" />
    <add key="emgov_ConnectionString" value="Data Source=(local);Initial Catalog=emgov_data;Integrated Security=SSPI;" />
  </appSettings>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  </system.serviceModel>
</configuration>

As you can see, this is one of the reasons why I love the WCF Web API. There is nothing but the bare minimum required. I can now add as many connection strings as I want to the web.config and I only have to go to the Global.asax.cs file to make sure that it is wired in correctly.

In the next post, we will look at what the client side code looks like.

Advertisements
Categories: English Tags: , , ,
  1. No comments yet.
  1. January 4, 2012 at 3:12 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: