Thursday, August 1, 2013

CSLA.NET Hosted On WCF Server with Dependency Injection

CSLA.NET Hosted On WCF Server with Dependency Injection

Disclaimer:

I posted this so I can have a future reference if ever I encounter a similar problem in the future. I am no way an expert in CSLA.NET. In fact, I have just been using it for 3 months now. So I only tried my best to explain things how I understand them.

Also most of the things here are purely just my opinion or an explanation of someone's opinion based from my understanding (especially when talking about DI vs. ServiceLocator and explanation on some parts of CSLA). :)

Also the most important disclaimer... this is my first time to do a blog post regarding software development!! (and I'm feeling ecstatic) So please bear with me. :)

Introduction

If you know Rocky Lhotka then you must have used CSLA.NET, then you will notice that doing dependency injection isn't that simple because it creates the instances of your business objects for you via DataPortal static methods.

I've been searching for answers on how to do dependency injection in CSLA.NET and I have found this awesome article by Peter Stromquist.

Overriding
DataPortal_OnDataPortalInvoke

you can inject dependency before the DataPortal_XYZ methods are actually called. A small problem with this is you need to create a base class for every DataPortal business base classes (businesslistbase, readonlybase, etc) to implement the injection. 

However, in CSLA.NET version 4.5.20, two new awesome interfaces called IDataPortalActivator and IInterceptDataPortal were introduced. These were actually introduced to me by Jason Bock (one of our practice leads the coolest company ever, Magenic!). So I'm gonna post his explanation:
You can create your own custom implementations of these, which basically lets you get access to the complete CSLA object lifecycle: 
  • IInterceptDataPortal.Initialize
  • IDataPortalActivator.CreateInstance
  • IDataPortalActivator.InitializeInstance
  • InterceptDataPortal.Complete
You hook them up in your application like this:

ApplicationContext.DataPortalActivator = new MyObjectActivator();
Csla.Server.DataPortal.InterceptorType = typeof(MyObjectInterceptor);
Thanks to Jason, at last, I now have a clean way of accessing all CSLA object's lifecycle without the need for overriding. However, you still cannot pass the IoC container through these so you will still need that container to be accessible globally that is initialized during the startup/bootstrapper part of the application. But I don't really recommend to make the IoC container a shared/public static resource that can be accessed everywhere since the developers working on it might potentially use it as a means of Service Locator (anti-)pattern. Mark Seeman explains it in this StackOverflow question. Unless of course, you have a mechanism to somehow limit access in your container's members so that it won't be (ab)used.

How to do it

Anyway, how this works is that every time a CSLA object is created, you can insert your injection logic in IDataPortalActivator.InitializeInstance.

Btw, I didn't actually use the IInterceptDataPortal since I don't really need to have any logic during the initial and terminal parts of the lifecycle. I would use implement a custom Interceptor if I want to have some kind of logging before and after each CSLA object lifecycle.

Here's a snippet of an example CustomDataPortalActivator:

    /// 
    /// A Custom DataPortalActivator to enable dependency injection in your Business Objects every time a DataPortal invocation is done.
    /// 
    public sealed class CustomDataPortalActivator : IDataPortalActivator
    {
        private IUnityContainer _container;

        /// 
        /// This constructor will need different ContainerComposers to properly register all dependencies used in your business layer.
        /// 
        /// 
        public CustomDataPortalActivator(IEnumerable<IContainerComposer> composers)
        {
            if (composers == null)
            {
                throw new ArgumentNullException("composers");
            }
            this._container = new UnityContainer();
            foreach (var composer in composers)
            {
                // Register dependencies to be used by your business objects
                composer.Compose(this._container);
            }
        }

        public object CreateInstance(Type requestedType)
        {
            if (requestedType == null)
            {
                throw new ArgumentNullException("requestedType");
            }

            // access the private constructor of the BusinessObject and instantiate it
            // Although, I would expect that the DefaultDataPortalActivator by CSLA also does a similar thing... not sure though
            var constructor = requestedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);
            var instance = constructor.Invoke(null);

            return instance;
        }

        public void InitializeInstance(object obj)
        {
            // this is where the DI comes into play to inject all your dependencies composed in the constructor
            this._container.BuildUp(obj.GetType(), obj);
        }
    }

I am using Unity for my IoC container, btw.

You should set the ApplicationContext.DataPortalActivator in the composition root of your application. (E.g. the main method, application_startup, etc.)


The way I did it is to pass on to the CustomDataPortalActivator's constructor a list of composers to compose/register the needed dependencies for the Business Objects. I just register all my composers in the container in my composition root and pass container.ResolveAll<IContainerComposer>() into the custom DP Activator's constructor. This way, I'm not passing the container around and I'm not exposing it globally.


                // register all the composers you need to properly register dependencies that are used in your business objects
                // add a name to properly distinguish each belonging to type IContainerComposer
                // so that ResolveAll<IContainerComposer>() will actually return a value
                container.RegisterType(typeof(IContainerComposer), typeof(DataContextComposer), typeof(DataContextComposer).ToString());
                container.RegisterType(typeof(IContainerComposer), typeof(JustAnotherSampleServiceComposer), typeof(JustAnotherSampleServiceComposer).ToString());

                ApplicationContext.DataPortalActivator = new CustomDataPortalActivator(container.ResolveAll<IContainerComposer>());

Notice that I've put a third parameter in .RegisterType. It's a name to prevent ambiguous registration of type IContainerComposer.

Then here's how a concrete composer looks like:
    public class DataContextComposer : IContainerComposer
    {
        public void Compose(IUnityContainer container)
        {
            container.RegisterType(typeof(IDataContext), typeof(MyDataContext));
        }
    }

At first glance, you would probably think why didn't I just pass the container. This is because this kind of practice should be avoided as much as possible IMHO. Because we don't even know how much registered elements are in that container which could be a cause for performance issues.

Then you would think why I just did not make the IoC container available globally? Doing that will defeat the purpose of DI itself. Although you are just really injecting the dependency through the DP activator, you are opening a potential anti-pattern for developers that might work on your project. This is the Service Locator (anti-)pattern. IMO, IoC containers should only be accessible in the composition root or in the highest possible level of your object graph (in this case of CSLA, the DataPortalActivator since this is the only place where we can inject dependencies) and not everywhere. Again like what I've stated above, unless of course, you have a mechanism to somehow limit access in your container so that it won't be (ab)used.


So okay, say we've already registered all the dependencies in the composition root. Now we've successfully injected dependencies to our CSLA Business Objects.




Here's where the problem lies when using DataPortalActivator:

All we've done above would work with no problems if the DataPortal server is in the same physical location (not going through wires e.g. WCF/Remoting) as the client.

But if the DataPortal server was setup to be hosted by a WCF Server, this DataPortalActivator won't work that straightforward because ApplicationContext.DataPortalActivator doesn't get passed to the WCF server.

Then probably you're going to wonder why the comment summary for ApplicationContext says:
This is because the "server" that's mentioned here means logical server. It doesn't mean physical server. So you have to do the bootstrapping logic (similar to what was done in the composition root of your client) inside the WCF server to initialize the custom DP activator in the context of your WCF Server.

So how did I solve this problem? With somebody like me who has close-to-none experience in CSLA and n00b knowledge in WCF, I was thinking "how can I create a custom CSLA Server Host". That was all I was thinking. Then, I tried to do it. (I won't explain here how I created a custom CSLA Server Host as this should be in one of Rocky's sample applications and I think it was discussed in his books -- go buy it!)

So, the problem of creating a custom CSLA server host is that it's not a good place (its constructor) to bootstrap the IoC container and initialize the ApplicationContext.DataPortalActivator because it gets instantiated/called every time a DataPortal invocation happens. (although I'm not sure if this can be prevented when combining WCF and CSLA to have a single call all through out) Again, the best place to do bootstrapping logic is in the composition root so all of these logic can be done only once.

So I thought to myself, what if I do it in the custom server host's static constructor? So it will only be done once. It was a good idea since I don't really need any instance variables to do bootstrapping logic at all.

But then it hit me. Global.asax. I added a Global.asax file to my WCF server and added the bootstrapping logic inside Application_Start and viola! It worked!


Conclusion

Sometimes we tend to over-complicate things and the simplest answers are just there lying at the back of our heads.


So there's my huge block of text. If you reached this part of this blob, I thank you for bearing with me. If you have comments/suggestions or you don't agree with some of the things I've mentioned here, please, please, please do comment. I would very much appreciate it and I do enjoy intellectual discussions. Or if you just enjoy trolling, well, I do too. :)

Btw, thanks to Jason Bock and Peter Stromquist for their expertise which helped me to solve this. 

I have uploaded the source code in my CodePlex for a clearer understanding.

EDIT:
I had a lot of typos especially in the code snippets since I was coding while blogging and I forgot to update the snippets with the latest code.

EDIT2:
The codeplex code also shows how unit testing can be done in the UI layer. (not calling DataPortal static methods directly)

2 comments:

  1. Excellent first post! I like your clear, conversational writing style.

    ReplyDelete
    Replies
    1. Thanks Aidan! I've also read one of your blog posts regarding the MCSD exam for Web Applications. It helped me a lot!

      Delete