1

さて、最初に私の目標を説明し、次に私がコーディングしたものを説明し、最後に質問をします。

ゴール

複数のコントラクトを管理し、操作が行われている瞬間に、それがオンラインかオフラインかを判断できる汎用クラスを持つこと。それを行うには本当に簡単な方法があります。コントラクトを実装し、オンラインかどうかにかかわらずすべてのメソッドをチェックし、適切な呼び出しを行う、オンラインとオフラインの各ペアのクラスです。そしてそれはまさに私が避けたいことです。

参考までに、舞台裏では、WCFサービスに接続されたオンラインシナリオと、クライアントローカルデータベースに接続されたオフラインシナリオになります。

参考までに2:インターセプトやAOPを避けてこれを達成しようとしましたが、行き止まりになりました。この投稿を見ると、良い解決策と思われるものを実装していますが、コンストラクターに接続されているかどうかはわかりませんが、実際のシナリオでは、コンストラクターレベルではなく、操作レベルでこのチェックが必要です。

コード

実行してテストする準備ができています。新しいコンソールアプリケーションにコピーして貼り付けるだけです。

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;
            // Should I perform a "lock" here to make this tread safe?
        }

        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;
        }
    }
}

質問

  1. 私が提示するこのデザインはスレッドセーフですか?

  2. それはパフォーマンスの恥ですか?オンライン-オフラインの状況とそこでの反省を処理する例外は、私がすべて間違ったことをしていることに気づきました。

  3. これは一般的な要件だと思いますが、このオンライン-オフライン管理を行うapi / fwk /何かはありませんか?ここでウィールを作り直しているような気がします。

  4. これは長い質問です。クライアント(この例ではプログラムクラス)に例外について知られたくないのですが、メソッドの実行をキャンセルする方法は他にありませんが、インターセプターの例外を使用しますか?他のアプローチも歓迎します。


私は有料のサードパーティのものには興味がないので、悲しいことに、PostSharpのようなものは私にとってオプションではありません。

4

3 に答える 3

2

スレッドセーフに関して-特にあなたのコメントがロックを示唆している場合:

    public static bool IsOnline()
    {
        // A routine that check connectivity
        return false;
        // Should I perform a "lock" here to make this tread safe?
    }

「オンラインで自分をロック」することはできませんが、チェックステートメントとリターンの間でステータスが変わる可能性があるかどうか疑問に思っています(古典的なシングルトンパターンを参照)。ここで心配なのは、チェックがスレッドであるかどうかに関係なく接続がダウンすることです。安全かどうか。

その場合、スローされた例外のみを処理できます(または処理しないことを選択できます)。

于 2012-09-14T16:18:24.520 に答える
1

さて、最後に、私はこれに対する解決策を持っています。これは、このようなオンライン-オフラインのシナリオで非常に役立つと思うので、共有します。少し改善があります。このようにすると、常にオンラインが最初にヒットし、誰を最初に使用する必要があるかを設定すると便利です。しかし、これはリリース用です。これはベータ版では問題ありません。

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;
        }
    }
}
于 2012-09-14T21:45:40.230 に答える
0

別の可能なアプローチを提案したいと思います。それはあなたが計画しているものとはかなり異なりますが、うまくいくかもしれません。

基本的な考え方は、アプリを2つの部分に分割することです。最初の部分は、あなたが説明しているもの、つまりUIです。ただし、オフラインであることを前提に書かれています。すべてをローカルデータベース/ファイル/メモリストアに保存します。

次に、プログラムの別の部分があります。おそらく、同期を担当する別のスレッド(または別のプロセス)にあります。データストアを調べて変更を待機し、オンラインの場合はそれらをサーバーに書き込みます。サーバーから更新がダウンすると、データストアはメインプログラムに何かが変更されたことを通知するイベントを発生させる必要がありますが、とにかくそれを行う必要があります。オフラインの場合、シンクロナイザーは何もしませんが、後でオンラインに戻るまで、すべてのデータ変更は自動的に自動的に削除されます。

このアプローチの良いところは、特に同期を扱うコードの部分を除いて、どこでもオンライン/オフラインの切り替えについて心配する必要がないことです。オフラインであると想定するだけで、デフォルトのケースがすでに設定されています。あなたはすべての傍受/AOPのものを完全に取り除くことができます。

于 2012-09-17T04:45:35.663 に答える