2016-07-29 6 views
0

私の問題を解決するためにStackOverflowを見回しました。私はこれが独特の問題だとは思わないが、良い解決策を見つけることができていない。名前付きUnity登録の種類を条件付きでインスタンス化する方法

私のWPFアプリケーションでは、私のビューモデルで、いくつかのデータを返すためにいくつかのサービスを呼び出す必要があります。これらのサービスはUnitOfWorkで注入され、DbContextで注入されます。 UnitOfWorkに注入されるこのdbcontextは、いくつかの基準に基づいて異なります。

実行時にIoCコンテナの登録を正しい方法で実行し、適切なDbContextを注入することに問題があります。だから、誰かが空白を記入してください(統一登録とそれの使用法の中で)。私はトラブルに遭って助けが必要な次のコードにいくつかのインラインコメントを持っています。ありがとう。

誰かが自分の登録コードを正しい方法で置き換えて、自分のWPF ViewModelクラスで使用する方法を教えてくれれば本当に素晴らしいでしょう!ありがとう。

最後に、このコードでコーディングエラーが見つかった場合は、これがどのようにコンパイルされるのか不思議に思わないでください。ここのコードは私の実際のコードではありません。物事を簡素化するために、私はそれらを書きました。しかし、それは私の実際のアプリケーションコードに非常によく似ています。

public interface IDBContext{} 
public interface IUnitOfWork{} 
public interface ISomeEntityService{} 

public interface IRepository<T> where T : class 
{ T GetSingle(Expression<Func<T, bool>> predicate); } 

public class DBContext1 : IDBContext 
{ 
    public DBContext1(connString) : base(connString){} 
} 

public class DBContext2 : IDBContext 
{ 
    public DBContext2(connString) : base(connString){} 
} 


public class Repository<T> : IRepository<T> where T : class 
{ 
    private readonly IDBContext context; 
    private readonly IDbSet<T> dbSet; 

    public Repository(IDBContext ctx) 
    { 
     context = ctx; 
     dbSet = ((DbContext)context).Set<T>(); 
    } 

    public T GetSingle(Expression<Func<T, bool>> predicate) 
    { 
     return ((DbContext)context).Set<T>().SingleOrDefault(predicate); 
    } 
} 

public class UnitOfWork : IUnitOfWork 
{ 
    IDBContext ctx; 
    private Dictionary<string, dynamic> repositories; 

    public UnitOfWork(IDBContext context) 
    { 
     ctx = context; 
    } 

    public IRepository<T> Repository<T>() where T : class 
    { 
     if (repositories == null) 
      repositories = new Dictionary<string, dynamic>(); 

     var type = nameof(T); 
     if (repositories.ContainsKey(type)) 
      return (IRepository<T>)repositories[type]; 

     var repositoryType = typeof(Repository<>); 
     repositories.Add(type, Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), ctx)); 
     return repositories[type]; 
    } 

    public int SaveChanges() 
    { 
     return ctx.SaveChanges(); 
    } 
} 

public class MyUnityBootstrapper : UnityBootstrapper 
{ 
    protected override void ConfigureContainer() 
    { 
     Container.RegisterType<IDBContext, DBContext1>("Context1"); 
     Container.RegisterType<IDBContext, DBContext2>("Context2"); 
     Container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); 
     Container.RegisterType<IUnitOfWork, UnitOfWork>(); 
    } 
} 

public class SomeEntityService : ISomeEntityService 
{ 
    private IUnitOfWork uow; 

    public ConsumerService(IUnitOfWork _uow) 
    { uow = _uow; } 

    public SomeEntity GetSomeData(int id) 
    { 
     return uow.Repository<SomeEntity>().GetSingle(x => x.Id == id); 
    } 
} 

public class SomeViewModel : BindableBase 
{ 
    private readonly ISomeEntityService someService; 
    public SomeViewModel(ISomeEntityService _someService) 
    { 
     // when I call someService, I want to make sure it is using either 
     // DBContext1 or DBContext2 based on some condition I can set here. 
     // This is where I am totally stuck. 
     someService = _someService; 
    } 

    // get the repository instance with an id of 1000 
    someService.GetSomeData(1000); 
} 

/* 
    I could do something like this. But I am afraid, I am violating 
    two of the best practices recommendations. 
    1. I am creating a dependency to my IoC Container here. 
    2. I am using the container as a Service Locator 
*/ 
public class SomeViewModel : BindableBase 
{ 
    private readonly ISomeEntityService someService; 
    public SomeViewModel() 
    { 
     var container = SomeHowGetTheContainer(); 
     /* 
      1. Call Container.Resolve<IDBContext>(with the required context); 
      2. Use the retrieved context to inject into the UnitOfWork 
      3. Use the retrieved UnitOfWork to inject into the service 

      But that would be like throwing everything about best practices to the wind! 
     */ 
     someService = container.Resolve<ISomeEntityService>(/*do some magic here to get the right context*/) 
    } 

    // get the repository instance with an id of 1000 
    someService.GetSomeData(1000); 
} 
+0

私は抽象的なファクトリの実装でコンテナへの呼び出しを隠したいと思います。工場がコンテナに依存するのは大丈夫です。 – Haukinger

+0

@Haukinger:私のシナリオでどのように表示されるのかを教えてください。それが助けになるだろう。ありがとう。 –

答えて

0

があなたのISomeEntityServiceを解決し、このような工場の追加:

public MySomeEntityServiceFactory 
{ 
    public MySomeEntityServiceFactory(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public ISomeEntityService CreateSomeEntityService(bool condition) 
    { 
     return _container.Resolve<ISomeEntityService>(condition ? "VariantA" : "VariantB"); 
    } 

    private readonly IUnityContainer _container; 
} 

をなど2つの名前付きバインディング追加:IUnitOfWorkについて

_container.RegisterType<ISomeEntityService, SomeEntityService>("VariantA", new InjectionConstructor(new ResolvedParameter<IDBContext>("VariantA"))); 
_container.RegisterType<ISomeEntityService, SomeEntityService>("VariantB", new InjectionConstructor(new ResolvedParameter<IDBContext>("VariantB"))); 

を、あなたは解決同様の工場を追加することができます仕事の単位、およびを渡すSomeEntityServiceのコンストラクタでそれを呼び出す...

これらのファクトリは、それ自身の依存関係です。...

+0

ありがとう!私がそれを得るかどうか私に見てみましょう。私の** ViewModel **は、ISomeServiceの代わりにISomeEntityServiceFactoryというコンストラクタパラメータを受け取ります。 ViewModelコンストラクタで、適切なパラメータを渡して 'factory.CreateService'メソッドを呼び出して、正しいサービスを取得します。同様に、UnitOfWorkはパラメータとして 'IContextFactory'を受け取り、そのファクトリは適切なDbContextを作成します。私は正しいですか?私はそれを打つことを許可し、すぐにその結果をあなたに知らせるでしょう。再度、感謝します。 –

+0

これはファクトリではありませんが、ServiceLocatorパターンはアンチパターンとみなされます。対象については、[Mark Seemann](http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator/)の記事を参照してください。 – Michael

+0

それ自体の工場は、サービスロケータの一種です、誰かが主張することができます。しかし、工場への依存は、製品に依存するという明確な声明ですが、サービスロケーターへの依存は依存関係を隠すだけです。確かに、容器に応じていいですが、それは工場のための大丈夫です。製品を作るために他に何を使うべきですか? 'Activator.CreateInstance'?どちらも明らかにコンテナより劣っており、製品自体の依存関係が製品コンストラクタに明示されているため、コンテナを使用して何も隠すことはありません。 – Haukinger

関連する問題