1

私は本当にMVVM、IoC、依存性注入を始めたばかりで、解決方法がわからないというつまずきにぶつかりましたが、なぜそれが起こっているのかは理解しています。

WPFアプリケーションのMVVMフレームワークとして、DIおよびIoC機能にCastle Windsorを使用し、MVVMLightを使用しています。このチュートリアルを使用して、 CastleWindsorにコンストラクターに注入されたを作成させるMainPageViewModelことができました。IGroupRepositoryCastleWindsorにこれのモック実装を登録しました。

MainPageViewModel以下は、コンストラクター以外のクラス内の唯一の他のコードです。

public ObservableCollection<GroupViewModel> Groups
{
    get
    {
        var groupVms = new ObservableCollection<GroupViewModel>();
        IEnumerable<Group> groups = _repository.GetAllGroups();
        foreach (Group g in groups)
        {
            var vm = new GroupViewModel(g);
            groupVms.Add(vm);
        }

        return groupVms;
    }
}

目的は、リポジトリ内のグループごとにビューモデルを作成することです。ただし、これを行うと、CastleWindsorは次の例外を発生させます。

満たすべき依存関係があるため、コンポーネント'Planner.ViewModel.GroupViewModel'を作成できません。'Planner.ViewModel.GroupViewModel'は、次の依存関係を待機しています。

  • 登録されていないサービス「Planner.Models.Group」。

私はこの例外を理解しています-CastleWindsorは私のビューモデルを構築する責任がありますが、私のエンティティを処理する方法がありません。

私はグーグルをたくさんしましたが、この問題に対する答えや提案はほとんど見つかりませんでした。それは私がしていることが間違っていると思わせるものです。 このStackOverflowの質問には、ビューモデルにエンティティを含めることは問題ないことを示唆する2つの回答がありますが、それが本当かどうか疑問に思い始めています。このような他の質問は、エンティティがビューモデルの近くにあるべきではないことを示唆しています。

この問題を解決する正しい方法は何ですか?

更新:要求に応じて、これは例外のスタックトレースです。

at Castle.MicroKernel.Handlers.DefaultHandler.AssertNotWaitingForDependency()
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Resolve(Type service, IDictionary arguments)
at Castle.Windsor.WindsorContainer.Resolve(Type service)
at Planner.ViewModel.ViewModelResolver.Resolve(String viewModelName) in D:\Planner\Planner\Planner\ViewModel\ViewModelResolver.cs:line 27
at Planner.ViewModel.ViewModelLocator.get_Item(String viewModelName) in D:\Planner\Planner\Planner\ViewModel\ViewModelLocator.cs:line 21

次のコードがビューモデルのコンストラクターへの呼び出しをインターセプトし、必要に応じてそれらを挿入するため、これは正しい動作だと思いました。

public class ViewModelResolver : IViewModelResolver
{
    private IWindsorContainer _container;

    public object Resolve(string viewModelName)
    {
        if (_container == null)
        {
            _container = new WindsorContainer();
            _container.Install(new WindsorViewsInstaller());
            _container.Install(new WindsorRepositoriesInstaller());
        }

        var viewModelType =
            GetType()
            .Assembly
            .GetTypes()
            .Where(t => t.Name.Equals(viewModelName))
            .FirstOrDefault();

        return _container.Resolve(viewModelType);
    }
}

更新2:これはリッチの質問に答えると思います:

public class ViewModelLocator : DynamicObject
{
    public IViewModelResolver Resolver { get; set; }

    public ViewModelLocator()
    {
        Resolver = new ViewModelResolver();
    }

    public object this[string viewModelName]
    {
        get
        {
            return Resolver.Resolve(viewModelName);
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = this[binder.Name];
        return true;
    }
}

もう少し理解できたと思います。問題は、実際には私が投稿した元のコードにあるのではありません。問題は実際にウィンザーをセットアップすることで発生していますね。しかし、その問題をどのように解決するかはまだわかりません。

4

2 に答える 2

1

のプロパティは、コンテナにないVMの束GroupsMainPageViewModel作成しますが、スタックトレースは、GroupViewModelのインスタンスをバインド/作成しようとしているロケーターです(なぜですか?この時点では、投稿したコードではわかりません)。 。

GroupViewModelの作成方法とコンテナの動作に接続がありません。Windsorにファクトリインターフェイスのファクトリインターフェイスドキュメントを介してこれらを作成させるか、コンテナから完全に削除して自分で管理する必要があります。腸の感覚に基づいて、私は工場のインターフェースに傾倒します。

于 2012-07-18T19:57:05.330 に答える
1

リッチの答えは私を正しい方向に導きましたが、これを行おうとしている次の人に役立つことを期待して、最終的に得たコードを表示できるように、別の答えを投稿したいと思いました。

Ritchの回答だけでなく、このStack Overflowの質問と、Castle Windsorの主要な貢献者の1人であるKrzysztofKoźmicによるこのブログ投稿はすべて、正しい方法でこの問題を解決するのに役立ちました。

Ritchが言ったように、ビューモデルのコンストラクターを直接呼び出すべきではありませんでした。これがコンテナーの目的です。したがって、それを行う方法は、Windsorがビューモデルを作成するのに役立つクラスを作成することです。これは型付き工場施設として知られています。これらのクラスの良いところは、実際に実装する必要がないことです。これらは単なるインターフェイスです。これは、ビューモデルの作成に最終的に使用されるクラスのコードです。

public interface IGroupViewModelFactory
{
    GroupViewModel Create(Group group);
}

GroupViewModelこれは、私の場合はMainWindowViewModelクラスであったビューモデルのコンストラクターに注入されます。そのクラスのコードは次のとおりです。

public class MainWindowViewModel : ViewModelBase
{
    private readonly IGroupRepository _repository;
    private readonly IGroupViewModelFactory _groupViewModelFactory;

    public MainWindowViewModel(IGroupRepository repository, IGroupViewModelFactory groupViewModelFactory)
    {
        _repository = repository;
        _groupViewModelFactory = groupViewModelFactory;
    }

    public ObservableCollection<GroupViewModel> Groups
    {
        get
        {
            var groupVms = new ObservableCollection<GroupViewModel>();
            IEnumerable<Group> groups = _repository.GetAllGroups();
            foreach (Group g in groups)
            {
                var vm = _groupViewModelFactory.Create(g);
                groupVms.Add(vm);
            }

            return groupVms;
        }
    }
}

最後のステップは、ファクトリクラスをWindsorに登録することです。これは、次のコードによって実行されます。

_container.AddFacility<TypedFactoryFacility>();

_container.Register(
    Component.For<Group>(),
    Component.For<IGroupViewModelFactory>()
    .AsFactory());

Component.For<Group>(),以前にリンクした質問には、上記のコードにその行が含まれていなかったことは注目に値します。それがなければ、Windsorから例外が発生していましたが、残念ながら詳細を保持できず、複製できなくなったため、アプリケーションに何か別の問題があった可能性があります。

Castle Windsorの魔法により、リポジトリ内のエンティティからビューモデルを作成できるようになりました。

于 2012-07-22T21:28:31.883 に答える