2016-08-18 1 views
0

私は、次のProcessorクラスが定義されています:C#のできない

public class MongoWrapper : IMongoRWrapper 
{ 
     private string _fileName; 

     public void Initialise(string path) 
     { 
      _fileName = path; 
     }; 

     public void Log() 
     { 
      IsInitialised(); 
     } 

     private void IsInitialised() 
     { 
      if (string.IsNullOrEmpty(_fileName)) 
       throw new InvalidOperationException(
        Resource.MongoRepositoryHelper_must_be_initialised); 
     } 
} 

public class Repository : IRepository 
{ 
    private IMongoWrapper _mongoWrapper; 
    public Repository(IMongoWrapper mongoWrapper) 
    { 
     _mongoWrapper = mongoWrapper; 
    }  

    public void Save() 
    { 
     _mongoWrapper.Log(); 
    } 
} 

public class Processor : IProcessor 
{ 
    private IRepository _repository; 
    private IMongoWrapper _mongoWrapper; 

    public Processor(IRepository repository, IMongoWrapper mongoWrapper) 
    { 
     _repository = repository; 
     _mongoWrapper = mongoWrapper; 
    } 

    public void Process() 
    { 
     _mongoWrapper.Initialise("path"); 

     _repository.Save(); 
    } 
} 

Processorクラスは、実行時に、次の二つのクラスを注入します私は依存性注入のためにUnityを使用しています。

プロパティ、私はRepositoryクラスのプロパティにアクセスするときProcessor.Process()方法で初期化MongoWrapper_fileNameは、利用できません。

誰でも私がここで間違っていることを教えてもらえますか?

MongoWrapperで静的フィールドにしたときに機能しました。これは正しい方法ですか?私はRepositoryクラスで プロパティにアクセスするとき

+0

なぜ 'Process'が' MongoWrapper'へのパスを渡す必要がありますか?そのデザインは匂いがする。 – Steven

+0

プロセスメソッドは実際にはWindowsサービスによって呼び出され、パスはサービスがフォルダの場所から取得したファイル名です。常に変化します。 –

+0

これは 'Proces'メソッドが呼び出されるたびに変わるか、またはWindowsサービスの実行中に変更されないこの設定値ですか? – Steven

答えて

2

プロパティは、MongoWrapper_fileNameProcessor.Process()方法で初期化することは、利用できません。

あなたは使用できないと言うとき、私はあなたが文字列_fileNameに値が割り当てられていないことを意味していることを前提としています。他の何かを意味するなら、あなたはこの答えを無視するかもしれません。

割り当てられていない理由は、おそらく2つの異なる参照を注入し、デフォルトのTransientLifetimeManagerを使用しているためです。コンテナは 過渡生涯マネージャを使用するため

あなたは構成のタイプを登録

Understanding Lifetime Managers

、または RegisterTypeメソッドを使用して、デフォルトの動作です。 Resolve またはResolveAllメソッドを呼び出すたびに、または依存関係メカニズムによって インスタンスが他のクラスに挿入されると、 の新しいインスタンスが登録、マップ、または要求された型で作成されます。

これは、あなたがProcessorを解決するとき、それはMongoWrapperの1つのインスタンスを取得し、あなたがRepositoryを解決するときに別のものを得ることを意味します。毎回の注射で新しくなった。このようなことの

考える:あなたが見たよう

var processor = new Processor(new Repository(new MongoWrapper()), new MongoWrapper()); 

、それは二つの異なるMongoWrapperを作成しています。これを解決するにはいくつかの方法があります。

LifetimeManagerをもう1つ使用してください。 PerResolveLifetimeManagerはおそらくあなたが望むものです。

この寿命マネージャの動作はTransientLifetimeManager等 であるが、 デフォルトのビルドプランに信号を提供し、インスタンスはビルドアップオブジェクトグラフを横切っ を再利用するようにタイプをマーキング。再帰の場合、オブジェクトが PerResolveLifetimeManagerで登録されている場合、 シングルトン動作が適用されます。

ので、同じようにそれを登録します。

var mongoWrapper = new MongoWrapper(); 
var processor = new Processor(new Repository(mongoWrapper), mongoWrapper); 

2.使用ContainerControlledLifetimeManager、あなたのIMongoWrapperシングルトンを作成し、これを使用します。このようなことの

container.RegisterType<IMongoWrapper, MongoWraper>(new PerResolveLifetimeManager()); 

考えます常に同じ参照。 IMongoWrapperの使い方によっては、これはあなたが望むものかもしれません。このシナリオでは、具体的に_fileNamestatic(上記のとおり)に設定するようになりますが、IMongoWrapperは全体でstaticとなります。

既存のオブジェクト をシングルトンインスタンスとして登録するContainerControlledLifetimeManager。このライフタイムマネージャのUnityは、 ResolveメソッドまたはResolveAllメソッドを呼び出すたびに、または依存関係メカニズムが インスタンスを他のクラスに挿入すると、登録されたタイプまたはオブジェクトの同じインスタンスを 返します。

container.RegisterType<IMongoWrapper, MongoWraper>(new ContainerControlledLifetimeManager()); 

割り当てIMongoWrapper手動。しかし、これはIoCを使用する目的全体を敗北させるでしょう。

public class Processor : IProcessor 
{ 
    private IRepository _repository; 
    private IMongoWrapper _mongoWrapper; 

    public Processor(IRepository repository, IMongoWrapper mongoWrapper) 
    { 
     _repository = repository; 
     _mongoWrapper = mongoWrapper; 
     _repository.SetWrapper(mongoWrapper); 
    } 

    public void Process() 
    { 
     _mongoWrapper.Initialise("path"); 

     _repository.Save(); 
    } 
} 

そして、あなたのリポジトリ:私は私はあなたのデザインに関するStevenに同意することを言わなければならない、と述べているすべてで

public class Repository : IRepository 
{ 
    private IMongoWrapper _mongoWrapper; 
    public Repository() 
    { 
    } 

    public void SetWrapper(IMongoWrapper wrapper) 
    { 
     _mongoWrapper = wrapper; 
    } 

    public void Save() 
    { 
     _mongoWrapper.Log(); 
    } 
} 

。あなたは本当に両方のクラスにIMongoWrapperの同じ参照を注入する必要がありますか?そして、Repositoryで使用できるように、Processorは本当にIMongoWrapperに値を設定する必要がありますか?それはあなたに後で来るために戻ってくる可能性のある奇妙な依存関係の種類を作り出します。設計を解決する答えが良いかもしれないが、私は実際の仕事に焦点を当てる。

UPDATE:あなたは

最善の解決策だと思いますか?ライフタイムマネージャを シングルトンとして設定するか、リポジトリクラスのファイル名の値を設定するにはどうすればよいですか?

私にとっては、_fileNameは1つのリクエスト/スレッド/サイクルで使用されるコンテキスト変数です。したがって、それに応じてそれを処理することができます。あなたが実際に望むならば、MongoWrapperをシングルトンとして設定してください。それを維持する責任を負うようにしてください_filePath

しかし、_fileNameを特定のスコープ(スレッドやリクエストなど)のコンテキスト変数として保持したい場合は、an answer for a similiar questionというコードを使用できます。 idéaは、IMongoWrapperの別の参照に依存するのではなく、_fileNameのコンテナを共有できるということです。 IOperationContextの場合は、を基にしたthe answer I wroteのコードを確認してください。

最初にFilePathを保持するクラス。これは単なる文字列でもあります。

public class ContextInfo : IContextInfo 
{ 
    public string FilePath {get; set;} 
} 

public interface IContextInfo 
{ 
    string FilePath {get; set;} 
} 

次に、IOperationContext<IContextInfo>を使用するラッパー。

public class RequestContext : IRequestContext 
{ 
    private readonly IOperationContext<IContextInfo> _operationContext; 

    public RequestContext(IOperationContext<IContextInfo> operationContext) 
    { 
     _operationContext = operationContext; 
    } 

    public IContextInfo ContextInfo 
    { 
     get 
     { 
      if (_operationContext.Items.ContainsKey("ContextInfoString")) 
      { 
       return _operationContext.Items["ContextInfoString"]; 
      } 
      return null; 
     } 
     set 
     { 
      _operationContext.Items["ContextInfoString"] = value; 
     } 
    } 
} 

プロセッサにIRequestContextを注入します。

public class Processor : IProcessor 
{ 
    private IRepository _repository; 
    private IMongoWrapper _mongoWrapper; 
    private IRequestContext _requestContext 

    public Processor(IRepository repository, IMongoWrapper mongoWrapper, IRequestContext requestContext) 
    { 
     _requestContext = requestContext 
     _repository = repository; 
     _mongoWrapper = mongoWrapper; 
    } 

    public void Process() 
    { 
     // Set the context variable. 
     _requestContext.ContextInfo = new ContextInfo { FilePath = "path" }); 
     // Now it will be set for a specific lifetime. 
     _repository.Save(); 
    } 
} 

今変数が設定されている、とあなたはどこにそれを使用することができます...

public class MongoWrapper : IMongoRWrapper 
{ 
    private IRequestContext _requestContext; 
    public MongoWrapper(IRequestContext requestContext) 
    { 
     _requestContext = requestContext; 
    } 

    private void IsInitialised() 
    { 
     if (string.IsNullOrEmpty(_requestContext.ContextInfo.FilePath)) 
      throw new InvalidOperationException(
       Resource.MongoRepositoryHelper_must_be_initialised); 
    } 
} 

しかし、再び、それはすべてあなたがfileNameを使用する傾向があるかに依存し、何それは寿命だことでなければなりません。変数の所有権について考えてみましょう。それはIMongoWrapperによって所有されるべきですか?それとも、アプリケーション全体で使用されているものですか?これらの回答の質問はあなたを正しい方向に導くはずです。

+0

ユニティのライフメーションマネージャーに関する華麗な説明をありがとう。私はこのようなすばらしい説明をしたことはありません。もう一度ありがとう。はい、スティーブンは、私はいくつかのコードの臭いを見ることができると述べた。あなたは最高の解決策はどれですか?ライフタイムマネージャをシングルトンとして設定するか、リポジトリクラスのファイル名の値を設定するには?私はこれらのIOCであまり経験はありません –

+0

私は自分の答えを更新しました。主に 'fileName'の所有権に焦点を当てています。それには、問題の代替ソリューションが含まれていますが、クラスの責任を再考することをお勧めします。 – smoksnes

関連する問題