3

WPF アプリケーションでビューとビューモデルを解決するために autofac を使用しています。IComponentContext は自動的にビューに渡されます。

例:

    public BusinessAuto(int proposedCoverageId, IComponentContext componentContext)
    {
        DataContext = componentContext.Resolve<BusinessAutoViewModel>(new TypedParameter(typeof(Int32), proposedCoverageId));
        InitializeComponent();
    }

このビューの XAML には、独自の ViewModel を持つ UserControls が作成されています。例:

<userControl:AdditionalCoveragesControl Margin="0,10"/>

Autofac は UserControl を作成していない (View は作成されている) ため、Autofac は依存関係を UserControl のコンストラクターに挿入できません。

IComponentContext への参照を、初期ビューの XAML で宣言されている UserControl に取得するにはどうすればよいですか?

どうにかして UserControl を作成するために Autofac が必要なのか、推奨されていないグローバル静的コンテナーに戻す必要があるのか​​ (ick)、または DependencyProperty を使用してコンテナーを渡す必要があると感じています (これも ick)。

4

2 に答える 2

6

これはServiceLocatorと呼ばれる制御の反転の劣った形式であり、現在の状況で要約できる欠陥があるため、(効果的に)コンテナーを注入しません。最終的にはコンテナーをすべてに注入する必要があります。

代わりに、「このコンポーネントは作成を担当し、それを実行するために何が必要か」という観点から問題に取り組む必要があります。

Lonni-Lokiが述べたように、1つのオプションは完全に形成されたコントロールを注入することですが、その点については同意しません。メインビューにこのサブコンポーネントを作成する責任がある場合は、それを作成する必要がありますが、その責任を促進するには、メインビューにservices/models / etcを挿入し、それを渡すか、そうでなければそれを作成するために使用する必要があります。Autofacのファクトリメソッドスタブはこれに理想的です。

たとえば、サブビューにIFooViewModelが必要な場合は、Func <IFooViewModel <(コンテキストに登録されたファクトリメソッド)をコンテナに挿入できます。これを使用して、新しい包含ビューを「オンデマンドでフィード」できます。

(または、必要に応じて、Func <arg1、arg2など、IFooViewModel>)

便利な経験則の1つは、クラスXを検討するときに、最初に「新しい」ものをどこにでも持っていき、代わりにそれをコンストラクターに渡すことです。次に、コードを見て、「クラスXのインスタンスが必要な場合、コンストラクターに何を渡す必要がありますか?」と自問してください。それらはあなたの依存関係です。

もっと実践的な例を見てみましょう...あなたがこのような構造を持っているとしましょう:

  • アプリケーションがメインウィンドウを作成します

  • MainWindowはSubView1を作成し、IMainWindowViewModelが必要です

  • SubView1にはISubView1Model、IFooServiceが必要です

  • SubView1はSubView2を作成します

  • SubView2にはISubView2Model、IBarServiceが必要です

したがって、ctorは次のようになります。

public MainWindow(IMainWindowViewModel viewModel, 
     Func<SubView1> subView1Factory)

public SubView1(ISubView1Model viewModel,
    IFooService fooService,
    Func<IFooService, SubView2> subView2Factory)

public SubView2(
    ISubView2ModelViewModel viewModel, 
    IBarService barService)

コンテナを設定すると、次のようになります。

(私はさまざまなIoCコンテナーを使用しているため、Autofac構文が錆びている可能性があることに注意してください)

var builder = new ContainerBuilder();

// Simple bit, register implementations for viewmodel, services
builder.RegisterType<MainWindowViewModel>.As<IMainWindowViewModel>();
builder.RegisterType<SubView1Model>.As<ISubView1Model>();
builder.RegisterInstance<FooService>.As<IFooService>();

// ok, lemme see if I can remember expression syntax...

// Simple case: 'static' resolution of subview
// We want a func that takes no args and returns us a fully-initialized
//  SubView1
builder.Register<Func<SubView1>>(context =>
{
    // Since all the bits of a subview1 are registered, simply
    // resolve it and return
    var view = context.Resolve<SubView1>();
    return () => view;
});

// Complicated case - lets say this viewmodel depends
// on foo service, which it uses to determine which 
// bar service to use
builder.Register<Func<IFooService, SubView2>>(context =>
{
    // and our view model
    var vm = context.Resolve<ISubView2ViewModel>();

    return (service) =>
    {
        var barService = new BarService(service);
        return new SubView2(vm, barService);
    };
});

IoCコンテナの輝かしい(そして私の意見では)使用法は、「私がすでに言ったことに基づいてすべてのビットを取得する方法を理解する」部分です。それ以外の場合は、手動インジェクションを使用することをお勧めします。依存関係を手動で渡します。とは言うものの、MainWindowの潜在的に複雑な構造は今では次のようになっています。

container.Resolve<MainWindow>();

コードにタイプミスやエラーが多すぎないことを願っていますが、しばらくの間Autofacを使用していません。

于 2013-01-05T09:37:54.850 に答える
2

componentContext を BusinessAuto ビューに渡すのではなく、AdditionalCoveragesControl を渡す必要があります。

public BusinessAuto(int proposedCoverageId, BusinessAutoViewModel vm, AdditionalCoveragesControl view)
{ 
  ...
  DataContext = vm;
  InternalView = view;
}    

XAML:

 <ContentPresenter Content="{Binding InternalView}" Margin="0,10"/>

次に、すべてのビューが Autofac から独立します。すべてのビューをコンテナーに登録するだけです。

于 2013-01-05T09:21:49.810 に答える