MapServiceRoute - service resolution

Topics: Web Api
Dec 15, 2011 at 2:24 PM

I have a generic service that I would like to be able to control the creation of the service based on the generic type.

My service takes in a rules object that changes based on the generic type.  Is there a way to provide a concrete implementation of the service type for each mapping?  That way I could provide the different rules per service mapping?  I would love to just be able to passing in a lambda expression with the concrete implementation for each mapping if possible or someone point me in the right direction to do it myself.

Dec 15, 2011 at 5:53 PM

It turns out this was a lot easier than I thought.  The API is so nice and mature that I didn't realize how easy it would be.  I am using Ninject as my IOC container.  Here is a sample as to how I went about it:

 

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;

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.MapServiceRoute<GenericService<Customers>>("Customer");
            RouteTable.Routes.MapServiceRoute<GenericService<rv_o_Organization>>("Organization");
        }

        private object CreateInstance(Type serviceType)
        {
            object result = null;
            IKernel kernel = new StandardKernel();
            try
            {
		// Need to parse out the generic type from the service definition.
                var genericType = serviceType.GetGenericArguments().FirstOrDefault();
                Rules 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 Rules GetAccountAtAGlanceRules(Type type)
        {
            var result = new Rules();
            result.AssemblyOf(type)
                .SetConnectionStringKey("aag_ConnectionString");
            return result;
        }

        private Rules GetEgovRules(Type type)
        {
            var result = new Rules();
            result.AssemblyOf(type)
                .SetConnectionStringKey("emgov_ConnectionString");
            return result;
        }

        #endregion

    };
}

As you can see, my service is generic so I had to do a little extra work getting the underlying generic type from the service.  Basically, this allow me to pass in different instances of my Rules object.  In this example I am telling the service which connection string to use.  My GenericService<> class is defined in another assembly and that is why I need to pass in the unique Rules object into the constructor.

This gives me a lot of flexibility when defining a purely generic service but still wanting to support flexibility such as connection strings or other settings.

If you are curious about Domain and Domain2, I am using the namespace of my two different object models to represent to different requests for totally different databases.  By doing this I can just load the class from a specific namespace and assembly.

NOTE:  This is a short version of what I am really using.  What I am showing here is intended to share ideas and concepts and not what you should have in production.