2013-08-05 21 views
7

いくつかのデータ(特にSharePointドキュメントライブラリからInfoPath xml)を渡して処理するWebサービスがあります。私は現在、どのようなフォームデータ "戦略"をロードするためにNinjectを使用しています。文字列に基づくオブジェクトバインド

Webサービス(エントリポイント)観光情報

namespace Core.Forms 
{ 
    public class IPFormDataContext 
    { 
     private IPFormDataStrategy _ipFormDataStrategy; 

     public IPFormDataContext(IPFormDataStrategy strategy) 
     { 
      _ipFormDataStrategy = strategy; 
     } 

     public bool DoWork(XmlDocument form) 
     { 
      return _ipFormDataStrategy.DoWork(form); 
     } 
    } 

    public abstract class IPFormDataStrategy 
    { 
     public abstract bool DoWork(XmlDocument form); 
    } 
} 

namespace Core.Forms.FormStrategies 
{ 
    class FormAStrategy : IPFormDataStrategy 
    { 
     public override bool DoWork(XmlDocument form) 
     { 
      // Deserialize form using (xsd.exe generated) FormAData 
      // and perform some operation on the resulting data. 
      return resultOfWork; 
     } 
    } 
} 

FormBStrategy関連

namespace Web.Services 
{ 
    public bool AddForm(XmlDocument form, string formName) 
    { 
     IKernel kernel = new StandardKernel(new FormsModule()); 
     var ctx = kernel.Get<IPFormDataContext>(formName); 

     return ctx.DoWork(form); 
    } 
} 

Ninject関連するもの

namespace Core.Modules 
{ 
    public class FormsModule : NinjectModule 
    { 
     public override void Load() 
     { 
      Bind<IPFormDataContext>().ToSelf().Named("FormA"); 
      Bind<IPFormDataContext>().ToSelf().Named("FormB"); 
      // Snip 

      Bind<IPFormDataStrategy>().To<FormAStratgey>() 
       .WhenParentNamed("FormA"); 
      Bind<IPFormDataStrategy>().To<FormBStrategy>() 
       .WhenParentNamed("FormB"); 
      // Snip 
     } 
    } 
} 

柄ずっと同じされる:ここではいくつかのコード(質問は以下のは)です私が挙げていない他の7つの戦略もそうです。私はwebserviceにフォームxmlを渡し、入ってくるフォームタイプに基づいて正しいフォームの逆シリアル化を呼び出す方法を見つけようとしています。

上記のコードは「動作しています。それは私が読んでいるものから悪いものであるNinjectのサービスの場所のいくつかの並べ替えをしているように感じる。しかし、私はこれを達成するための適切な方法を考えることができません。 NinjectやIOC/DIフレームワークを使用して死んでいるわけではありません。

私がやっていることは間違っていますか?私は正しい方向に指摘できますか?私は好きではない

+1

これはジェネリックのための良い場所のようです: 'IPFormDataContext T:IPFormDataStrategy'のようなことをすることができますか?私は本当に忍耐に精通していない。 – lukegravitt

答えて

3

サンプルコードに表示されているクラスが正確である(つまり、メソッドとプロパティが多数ありません)。次に、可能な限り単純な解決法が機能し、クラスのいくつかのクラス/依存関係を取り除くことができます。

フレームワークに依存しないシンプルなソリューション、/コンテナは、次のようになります。

public static class FormsProcessing 
{ 
    private static ConcurrentDictionary<string, Func<FormProcessor>> _registeredProcessors = new ConcurrentDictionary<string, Func<FormProcessor>>(); 

    public delegate bool FormProcessor(XmlDocument form); 

    public static void RegisterProcessor(string formKey, Func<FormProcessor> formsProcessorFactory) 
    { 
     _registeredProcessors.AddOrUpdate(formKey, formsProcessorFactory, (k, current) => formsProcessorFactory); 
    } 

    public static FormProcessor GetProcessorFor(string formKey) 
    { 
     Func<FormProcessor> processorFactory; 
     if (_registeredProcessors.TryGetValue(formKey, out processorFactory); 
      return processorFactory(); 
     return null; 
    } 

    public static bool Process(string formKey, XmlDocument form) 
    { 
     var processor = GetProcessorFor(formKey); 
     if (null == processor) 
      throw new Exception(string.Format("No processor for '{0}' forms available", formKey)); 
     return processor(form); 
    } 
} 

使用法:

namespace Web.Services 
{ 
    public class MyServiceClass 
    { 
     public bool AddForm(XmlDocument form, string formName) 
     { 
      return FormsProcessing.Process(formName, form); 
     } 
    } 
} 

これは、シンプルかつ明示的で、かつ必要とするか、または任意の依存関係を公開していません。 IPFormDataContextおよびIPFormDataStrategyクラスのいくつかの構造上にある。明示的な依存関係は、FormProcessor署名を持つデリゲートのみです。コンテナと同様に

、あなたはどこかに登録を実行する必要があります。また

FormsProcessing.RegisterProcessor("FormA",() => new FormAStrategy().DoWork); 
FormsProcessing.RegisterProcessor("FormB",() => new FormBStrategy().DoWork); 

大会のためのアセンブリをスキャンすることによって(規則に基づく)自動登録のいくつかのフォームを追加するのは簡単だろう(たとえば、インタフェース署名)。

3

2つのことは:

  1. AddFormメソッド内のカーネルを作成します。これは、IoCパターンを反転するので、決して起こらないはずです。代わりに、AddFormが属するクラスは、必要な依存関係を要求する必要があります。
  2. マジックストリングの使用。戦略の名前をつける文字列を送信するように、AddForm()の消費者に要求するのは正しいとは思われません。

私はこれを解決する方法がよく分かりません。念頭に置くべきことの1つは、AddForm(クラスXと呼ぶ)を所有するクラスにFunc<string, IPFormDataStrategy>依存関係を追加することです。

namespace Web.Services 
{ 
    public class X 
    { 
     private readonly Func<string, IPFormDataStrategy> _strategyResolver; 

     public X(Func<string, IPFormDataStrategy> strategyResolver) 
     { 
      _strategyResolver = strategyResolver; 
     } 

     public bool AddForm(XmlDocument form, string formName) 
     { 
      return _strategyResolver(formName).DoWork(form); 
     } 
    } 
} 

次にあなたがFunc<string, IPFormDataStrategy>をバインドするToMethodを使用することができます:私はこのような何かを想像してい

public class FormsModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<FormAStrategy>().ToSelf(); 
     Bind<FormBStrategy>().ToSelf(); 

     Bind<Func<string, IPFormDataStrategy>>().ToMethod(context => 
      new Func<string, IPFormDataStrategy>(formName => { 
       switch (formName) 
       { 
        case "FormA": 
         return context.Kernel.Get<FormAStrategy>(); 
         // Note, could also simply "return new FormAStrategy()" here. 
        case "FormB": 
         return context.Kernel.Get<FormBStrategy>(); 
        default: 
         throw new InvalidOperationException(formName + " is unrecognized"); 
       } 
      }) 
     ); 
    } 
} 

あなたは、これが不必要に複雑かもしれませんし、多分それはですが...私はそれので、それが好きカーネル全体へのアクセス権を与えるのではなく、クラスXの依存関係を明示的にします(つまり、フォーム名を指定した戦略を得る)。このアプローチはまた、戦略を単一のswitch文にまとめるためのロジックを統合します。それはまだ魔法の文字列に依存しているが、私はより多くのコンテキストを知らなくても、それを回避するかどうかはわかりません...

+0

Webサービスを呼び出すアプリケーションは、SharePointリストイベントハンドラです。イベントハンドラを可能な限り基本的なものにしたい、つまり魔法の文字列を残したかったのです。このプロジェクトの要件には、将来の影響を最小限に抑えてフォームを追加する機能が含まれています。私は時々Webサービスを更新するのは問題ないが、リストイベントハンドラをアップグレードすることは苦痛であった。 – Slipfish

1

サービスロケータがgenerally an anti-patternですが、重要なことは理解することです、それは抗理由ですパターン。理由は通常、リファクタリングと型の安全性に関連しています。私はあなたが何か間違っているかどうかを判断する最善の方法は、問題をそれを絶対的な最も簡単な要件にすることです、そしてそこに到達する最も簡単な道を判断することです。私が理解したよう

、あなたの要件は次のとおりです。

  1. はなり、フォームタイプに基づいてWebサービス
  2. でXML形式のデータを受信し、あなたに適切なロジックを呼び出す

マイさらにご質問次のとおりです。

  1. フォームの種類はどのように識別しますか? XML文書のフィールドですか? (該当する場合は、それを使用してください)
  2. 頻繁にフォームタイプが変更される可能性はありますか?

それが沸き起こるときは、何らかの識別子を使用する必要があります。 @McGarnagleが指摘しているように、魔法の文字列はコードと同期しなくなる可能性があります。ストラテジークラスのタイプ名を使用することもできますが、「同期が外れる可能性があります」という問題があります。

フォームタイプが変更されない場合は、switch文を使用してStrategyインスタンスをインスタンス化してください。シンプリシティは、保守性のために従うべき最良の設計パターンです。

変更される可能性が高い場合は、サービスロケータが動作する可能性があります。サービスロケータの実装がこのコードだけに限定されている場合、それを維持するのはひどくはありません。

Ninjectでは、このbenchmarkがまだ有効かどうかはわかりませんが、Funqははるかに高速であり、構文がよくなります。 (DIコンテナをまったく使用しない場合)

関連する問題