Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Router is a generic class that manage multiple contracts that. It is able to find out wheter it's an online or offline situation, on the very moment when an operation is being made.

There's a really easy way of doing it: a class for each Online-Offline pair that implement the contract and check on every each method wheter if it's online or not, and makes the right call. And that's exactly what I want to avoid.

Just FYI, behind the scenes it would be an Online scenario connected to WCF services and an Offline scenario connected to a client local database.

FYI 2: I've tried to accomplish this avoiding Interception and AOP stuff, but I found a dead end. You can see this post where I implement what seems to be a good solution, but stablishes if it's connected or not on the contructor, but real-world scenario needs this check at Operation level, not constructor level.

It's ready to run & test: just copy/paste on a new console application.

using System;
using System.Reflection;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace ConsoleApplication1
{
    public class Unity
    {
        public static IUnityContainer Container;

        public static void Initialize()
        {
            Container = new UnityContainer();

            Container.AddNewExtension<Interception>();
            Container.RegisterType<ILogger, OnlineLogger>();
            Container.Configure<Interception>().SetInterceptorFor<ILogger>(new InterfaceInterceptor());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Unity.Initialize();

            var r = new Router<ILogger, OnlineLogger, OfflineLogger>();

            try
            {
                r.Logger.Write("Method executed.");
            }
            catch (CantLogException ex)
            {
                r.ManageCantLogException(ex);
            }

            Console.ReadKey();
        }
    }

    public class Router<TContract, TOnline, TOffline>
        where TOnline : TContract, new()
        where TOffline : TContract, new()
    {
        public TContract Logger;

        public Router()
        {
            Logger = Unity.Container.Resolve<TContract>();
        }

        public void ManageCantLogException(CantLogException ex)
        {
            // Is this an ugly trick? I mean, the type was already registered with online.
            Unity.Container.RegisterType<TContract, TOffline>();
            Logger = Unity.Container.Resolve<TContract>();

            var method = ((MethodBase)ex.MethodBase);
            method.Invoke(Logger, ex.ParameterCollection);
        }
    }

    public interface ILogger
    {
        [Test]
        void Write(string message);
    }

    public class OnlineLogger : ILogger
    {
        public static bool IsOnline()
        {
            // A routine that check connectivity
            return false;
        }

        public void Write(string message)
        {
            Console.WriteLine("Logger: " + message);
        }
    }

    public class OfflineLogger : ILogger
    {
        public void Write(string message)
        {
            Console.WriteLine("Logger: " + message);
        }
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    public class TestAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new TestHandler();
        }
    }

    public class TestHandler : ICallHandler
    {
        public int Order { get; set; }
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine("It's been intercepted.");

            if (!OnlineLogger.IsOnline() && input.Target is OnlineLogger)
            {
                Console.WriteLine("It's been canceled.");
                throw new CantLogException(input.MethodBase, input.Inputs);
            }
            return getNext()(input, getNext);
        }
    }

    public class CantLogException : Exception
    {
        public MethodBase MethodBase { get; set; }

        public object[] ParameterCollection { get; set; }

        public CantLogException(string message)
            : base(message)
        {
        }

        public CantLogException(MethodBase methodBase, IParameterCollection parameterCollection)
        {
            this.MethodBase = methodBase;

            var parameters = new object[parameterCollection.Count];

            int i = 0;
            foreach (var parameter in parameterCollection)
            {
                parameters[i] = parameter;
                i++;
            }

            this.ParameterCollection = parameters;
        }
    }
}

Questions

  1. Performance? Handling online-offline status through exceptions smells really bad.

  2. Multi-threading operations would expose this design as thread-unsafe?

  3. Isn't there any other way of preventing method execution?

  4. Any other constructive comments are apreciated too.


I'm not interested on paid third-party stuff, so sadly things like PostSharp aren't options for me.

share|improve this question
1  
I'm not familiar with Unity, but this seems like something you should be able to do in your ICallHandler. I'm quite sure you could do this with Castle, which has something very similar. I think the solution here is to somehow let the handler know about the types in question. – svick Sep 14 '12 at 17:37
Sounds good... an example even with Castle might be helful. I don't know how can that make a difference. Supouse the handler knows Online, Offline and Contract types, what would happen? I still need to stop method execution using an exception. – Chuck Norris Sep 14 '12 at 18:51

2 Answers

up vote 2 down vote accepted

With Castle DynamicProxy, I would create an interface proxy without a target and an interceptor that would choose which implementation to use. Something like:

class OnlineOfflineInterceptor<TInterface> : IInterceptor
{
    private readonly TInterface m_online;
    private readonly TInterface m_offline;

    public OnlineOfflineInterceptor(TInterface online, TInterface offline)
    {
        m_online = online;
        m_offline = offline;
    }

    public void Intercept(IInvocation invocation)
    {
        invocation.ReturnValue = invocation.Method.Invoke(
            Connectivity.IsConnected() ? m_online : m_offline, invocation.Arguments);
    }
}

…

var proxyGenerator = new ProxyGenerator();
ILogger logger = proxyGenerator.CreateInterfaceProxyWithoutTarget<ILogger>(
    new OnlineOfflineInterceptor<ILogger>(new OnlineLogger(), new OfflineLogger()));

I think this is more elegant than binding offline implementation to the online implementation by having a property on the online version that returns the offline version.

share|improve this answer
I like this, didn't know Castle had this feature, much cleaner and readable than Unity attributes approach. Thanks! – Chuck Norris Sep 25 '12 at 14:04

Ok, finally, I have the solution for this. I'll share it because I find this really helpful in such Online-Offline scenarios. There's a little improvement that would be nice to do: this way, Online will always be hitted first, it would be nice to set whom has to be used first. But that's for the release, this will be just fine for a beta.

using System;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace ConsoleApplication1
{
    public enum ConnectionStatus
    {
        Online,
        Offline,
        System // System checks connectivity
    }

    public static class Connectivity
    {
        private static ConnectionStatus ConnectionStatus = ConnectionStatus.Offline;

        public static void ForceConnectionStatus(ConnectionStatus connectionStatus)
        {
            ConnectionStatus = connectionStatus;
        }

        public static bool IsConnected()
        {
            switch (ConnectionStatus)
            {
                case ConnectionStatus.Online:
                    return true;
                case ConnectionStatus.Offline:
                    return false;
                case ConnectionStatus.System:
                    return CheckConnection();
            }
            return false;
        }

        private static bool CheckConnection()
        {
            return true;
        }
    }

    public class Unity
    {
        public static IUnityContainer Container;

        public static void Initialize()
        {
            Container = new UnityContainer();

            Container.AddNewExtension<Interception>();
            Container.RegisterType<ILogger, OnlineLogger>();
            Container.Configure<Interception>().SetInterceptorFor<ILogger>(new InterfaceInterceptor());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Unity.Initialize();

            var r = new Router<ILogger, OnlineLogger, OnlineLogger>();

            Connectivity.ForceConnectionStatus(ConnectionStatus.Offline);

            Console.WriteLine("Calling Online, will attend offline: ");

            r.Logger.Write("Used offline.");

            Connectivity.ForceConnectionStatus(ConnectionStatus.Online);

            Console.WriteLine("Calling Online, will attend online: ");

            r.Logger.Write("Used Online. Clap Clap Clap.");

            Console.ReadKey();
        }
    }

    public class Router<TContract, TOnline, TOffline>
        where TOnline : TContract
        where TOffline : TContract
    {
        public TContract Logger;

        public Router()
        {
            Logger = Unity.Container.Resolve<TContract>();
        }
    }

    public interface IOnline
    {
        IOffline Offline { get; set; }
    }

    public interface IOffline
    {
    }

    public interface ILogger
    {
        [Test()]
        void Write(string message);
    }

    public class OnlineLogger : ILogger, IOnline
    {
        public IOffline Offline { get; set; }

        public OnlineLogger()
        {
            this.Offline = new OfflineLogger();
        }

        public void Write(string message)
        {
            Console.WriteLine("Online Logger: " + message);
        }
    }

    public class OfflineLogger : ILogger, IOffline
    {
        public IOnline Online { get; set; }

        public void Write(string message)
        {
            Console.WriteLine("Offline Logger: " + message);
        }
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    public class TestAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new TestHandler();
        }
    }

    public class TestHandler : ICallHandler
    {
        public int Order { get; set; }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine("It's been intercepted.");

            if (!Connectivity.IsConnected() && input.Target is IOnline)
            {
                Console.WriteLine("It's been canceled.");

                var offline = ((input.Target as IOnline).Offline);

                if (offline == null)
                    throw new Exception("Online class did not initialized Offline Dispatcher.");

                var offlineResult = input.MethodBase.Invoke(offline, this.GetObjects(input.Inputs));

                return input.CreateMethodReturn(offlineResult, this.GetObjects(input.Inputs));
            }

            return getNext()(input, getNext);
        }

        private object[] GetObjects(IParameterCollection parameterCollection)
        {
            var parameters = new object[parameterCollection.Count];

            int i = 0;
            foreach (var parameter in parameterCollection)
            {
                parameters[i] = parameter;
                i++;
            }
            return parameters;
        }
    }
}
share|improve this answer
So, each online type has to know its corresponding offline type? That doesn't sound very flexible to me. For example, you can't switch to a different implementation of the offline service without changing the code of the online service. – svick Sep 17 '12 at 8:01
It's simplified for the sake of the code example. I'm using IoC, so implementation won't depend on Online service, but from IoC framework. Online type only knows there's an Offline contract. Is this what you mean? Thanks! – Chuck Norris Sep 17 '12 at 14:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.