11

おおよそ次のような依存関係チェーンがあります。

public class CarSalesBatchJob
{
    public CarSalesBatchJob(IFileProvider fileProvider)
    { ... }
}

public class MotorcycleSalesBatchJob
{
    public MotorcycleSalesBatchJob(IFileProvider fileProvider)
    { ... }
}    

public class FtpFileProvider : IFileProvider
{
    public FtpFileProvider(IFtpSettings settings)
    { ... }
}

public class CarSalesFtpSettings : IFtpSettings { ... }
public class MotorcycleSalesFtpSettings : IFtpSettings { ... }

これまで、私は規約ベースのバインディングを使用してきましたが、IFtpSettings. そこで、いくつかのコンテキスト バインディングを使用することにしました。最初は赤面kernel.Bind<>().To<>().WhenInjectedInto<>()が有望に見えましたが、それは最初のレベルでのみ役立ちます。つまり、 aCarSalesFtpFileProviderと aがあれば、MotorcycleSalesFtpProviderこれを行うことができます。

kernel.Bind<IFtpSettings>().To<CarSalesFtpSettings>()
    .WhenInjectedInto<CarSalesFtpFileProvider>();
kernel.Bind<IFtpSettings>().To<MotorcycleSalesFtpSettings>()
    .WhenInjectedInto<MotorcycleSalesFtpFileProvider>();

FtpFileProviderしかし、私が使用したい設定だけが実際に異なる2つの具体的な実装を作成するのはかなりばかげているようです。というメソッドがあることがわかりましたWhenAnyAnchestorNamed(string name)。しかし、このルートでは、属性と魔法の文字列をバッチ ジョブに配置する必要があり、それについてはあまり興味がありません。

また、バインディング ステートメントには昔ながらの.When(Func<IRequest, bool>)メソッドがあることにも気付きました。そのため、バインディング ステートメントとしてこれを思いつきました。

//at this point I've already ran the conventions based bindings code so I need to unbind
kernel.Unbind<IFtpSettings>();
kernel.Bind<IFtpSettings>().To<CarSalesFtpSettings>()
    .When(r => HasAncestorOfType<CarSalesBatchJob>(r));
kernel.Bind<IFtpSettings>().To<MotorcycleSalesFtpSettings>()
    .When(r => HasAncestorOfType<MotorcycleSalesBatchJob>(r));

// later on in the same class
private static bool HasAncestorOfType<T>(IRequest request)
{
    if (request == null)
        return false;

    if (request.Service == typeof(T))
        return true;

    return HasAncestorOfType<T>(request.ParentRequest);
}

したがって、コンストラクターが IFtpSettings を要求した場合、要求ツリーを再帰的に調べて、チェーン内の要求されたサービス/タイプのいずれかが提供されたタイプ (CarSalesBatchJob または MotorcycleSalesBatchJob) と一致するかどうかを確認し、一致する場合は true を返します。チェーンの一番上まで到達すると、false が返されます。

背景説明が長くなってすみません。

これが私の質問です。この方法で問題にアプローチしてはいけない理由はありますか? これは悪いフォームと見なされますか? 祖先のリクエスト タイプを見つけるより良い方法はありますか? クラス/依存関係チェーンをより「快適な」方法に再構築する必要がありますか?

4

3 に答える 3

6

This is the implementation type. のrequest.Target.Member.ReflectedType代わりに使用する必要があります。request.Service

また、WhenAnyAncestorNamed は属性を必要としません。Namedメソッドを使用して、ジョブのバインドをマークできます。

于 2012-06-06T22:02:16.230 に答える
1

私のアドバイスは、IFtpSettings 自体ではなく、登録で慣習を破る状況の宛先をターゲットにすることです。たとえば、同様の状況では、次のようにします。

container.Register<CarSalesBatchJob>(() => {
    ICommonSetting myCarSpecificDependency = container.Resolve<CarSpecificDependency>();
    new CarSalesBatchJob(myCarSpecificDependency);
});

container.Register<MotorcycleSalesBatchJob>(() => {
    ICommonSetting myMotorcycleSpecificDependency = container.Resolve<MotorcycleSpecificDependency>();
    new MotorcycleSalesBatchJob(myMotorcycleSpecificDependency);
});

これは、各バッチ ジョブがどのようにインスタンス化されるかを他のプログラマーに説明するのと同じくらい簡単です。ICommonSetting の登録をターゲットにしてそれぞれの 1 回限りを処理しようとするのではなく、それぞれの 1 回限りを個別に処理します。

別の言い方をすれば、これらのクラスに、IC コンテナーで変更する必要のある 2 つの依存関係があるとします。さまざまな場所に 4 行の登録コードがありますが、それらはすべて、MotorcycleSalesBatchJob や CarSalesBatchJob などをインスタンス化するためのものです。誰かがクラスがどのように参照されているかを知りたい場合は、探し出す必要があります。クラス (または基本クラス) への任意の参照。これらのそれぞれがどのようにインスタンス化されるかを正確に説明するコードをすべて 1 か所で記述しないのはなぜでしょうか?

これの欠点 (または少なくとも私が他の人から聞いたこと) は、これらの具体的なクラスのいずれかのコンストラクターが変更された場合、コードが壊れて登録を変更する必要があることです。私にとっては、これは良いことです。何らかの状態に基づいて IoC コンテナーが変化するこのタイプのパスを既に 1 歩踏み出しているため、期待される動作を維持していることを確認する必要があります。

可能性を考えると、さらに楽しくなります。次のようなことができます。

container.Register<IProductCatalog>(() => {
    currentState = container.Resolve<ICurrentState>().GetTheState();
    if (currentState.HasSpecialPricing())
       return container.Resolve<SpecialPricingProductCatalog>();
    return container.Resolve<RegularPricingProductCatalog>();
});

さまざまな状況で物事がどのように機能するかの複雑さはすべて、個別のクラスに分割でき、適切な状況で適切なクラスを提供するのは IoC コンテナーに任せることができます。

于 2012-06-07T15:41:04.493 に答える