IoC、DLLリファレンス、およびアセンブリスキャン
-
03-07-2019 - |
質問
この質問はStructureMapに関連していますが、私の一般的なの質問は次のとおりです:
IoCでコンポーネントを配線する場合 コンテナコード内(反対に xml を介して設定するには) 通常、明示的なプロジェクト/ビルドが必要です すべてのアセンブリへの参照?
個別のアセンブリが必要な理由理由:
"に存在する抽象クラス コンクリートからアセンブリを分離する 実装は このような分離を実現します。」 -フレームワーク 設計ガイドラインp.91
例:
PersonBase.dll と Bob.dll
があるとしますBob は抽象クラス PersonBase を継承します。両方とも Person 名前空間にあります。 ただし、異なるアセンブリで。
私は Bob ではなく、 PersonBase にプログラミングしています。
メインコードに戻ると、人が必要です。 StructureMapはアセンブリをスキャンできます。すばらしい、StructureMapに1つを頼む!
今、私のメインコードでは、もちろん PersonBase のみを参照していますが、 Bob は参照していません。実際、私のコードに Bob についての何かを知らせたくありません。プロジェクトの参照も、ナチスもありません。それがポイントです。
だから言いたい:
//Reference: PersonBase.dll (only)
using Person;
...
//this is as much as we'll ever be specific about Bob:
Scan( x=> { x.Assembly("Bob.dll"); }
//Ok, I should now have something that's a PersonBase (Bob). But no ?
ObjectFactory.GetAllInstances<PersonBase>().Count == 0
運はありません。動作するのは、私がボブに望むことを明示することです:
//Reference: PersonBase.dll and Bob.dll
using Person;
...
Scan( x => {x.Assembly("Bob.dll"); }
//If I'm explicit, it works. But Bob's just a PersonBase, what gives?
ObjectFactory.GetAllInstances<Bob>().Count == 1 //there he is!
しかし、今はプロジェクトで Bob.dll を参照する必要がありました。
Spring + Xml構成を使用すると、この状況を回避できます。しかし、その後、Spring + Xml構成に戻ります...!
を使用して何かが足りない StructureMap、または一般として 原則、do(fluent)IoC 構成には明示的な参照が必要です すべてのアセンブリに?
おそらく関連する質問: StructureMapおよびスキャンアセンブリ
解決
ようやくこれを整理しました。次のようになります。
IoC Uml http://img396.imageshack.us/img396/1343/iocuml。 jpg
アセンブリを使用
- Core.exe
- PersonBase.dll(Core.exeがコンパイル時間を参照)
- Bob.dll( StructureMapスキャンを介してランタイムをロード)
- Betty.dll( StructureMap Scanを介してランタイムをロード)
StructureMapで取得するには、カスタムの「ITypeScanner」が必要でした。アセンブリのスキャンをサポートするには:
public class MyScanner : ITypeScanner {
public void Process(Type type, PluginGraph graph) {
if(type.BaseType == null) return;
if(type.BaseType.Equals(typeof(PersonBase))) {
graph.Configure(x =>
x.ForRequestedType<PersonBase>()
.TheDefault.Is.OfConcreteType(type));
}
}
}
メインコードは次のようになります。
ObjectFactory.Configure(x => x.Scan (
scan =>
{
scan.AssembliesFromPath(Environment.CurrentDirectory
/*, filter=>filter.You.Could.Filter.Here*/);
//scan.WithDefaultConventions(); //doesn't do it
scan.With<MyScanner>();
}
));
ObjectFactory.GetAllInstances<PersonBase>()
.ToList()
.ForEach(p =>
{ Console.WriteLine(p.FirstName); } );
他のヒント
StructureMapを使用してxml設定を行うこともできます。必要に応じてそれらを混ぜることもできます。
StructureMapにアセンブリのロード方法を伝えるために、Bobクラスに配置できるStructureMap属性もあります。 DefaultConstructorは、ときどき使用するものです。
自動スキャンオプションは、命名規則、アセンブリ規則、および名前空間規則を維持している場合にのみ機能します。 Fluentインターフェイスを使用して、構造マップを手動で構成できます。例:
ObjectFactory.Initialize(initialization =>
initialization.ForRequestedType<PersonBase>()
.TheDefault.Is.OfConcreteType<Bob>());
現在のプロジェクトで行っていること(StructureMapではなくAutoFacを使用していますが、違いはないはずです):
アプリケーションがコアアセンブリで使用する外部サービスを定義するインターフェイスがあります。たとえば、 App.Core
(PersonBaseなど)です。
次に、これらのインターフェイスの実装が Services.Real
(Bob.dllなど)にあります。
今回のケースでは、 Service.Fake
もあります。これは、他のエンタープライズサービスやデータベースなどに依存するUIテストを容易にするために使用されます。
フロントエンドの「クライアント」アプリケーション自体(この場合、ASP.NET MVCアプリ)は App.Core
を参照します。
アプリの起動時に、 Assembly.Load
を使用して適切な&quot; Services&quot;を読み込みます。構成設定に基づく実装DLL。
これらの各DLLには、実装するサービスのリストを返すIServiceRegistryの実装があります。
public enum LifestyleType { Singleton, Transient, PerRequest}
public class ServiceInfo {
public Type InterfaceType {get;set;}
public Type ImplementationType {get;set;}
// this might or might not be useful for your app,
// depending on the types of services, etc.
public LifestyleType Lifestyle {get;set;}
}
public interface IServiceRegistry {
IEnumerable<ServiceInfo> GetServices();
}
...アプリケーションは、リフレクションを介してこのServiceRegistryを見つけ、これらのServiceInfoインスタンスを列挙して、コンテナーに登録します。私たちにとって、このregister-all-servicesはWebアプリケーションに存在しますが、それを別のアセンブリに含めることは可能です(多くの場合好ましい)。
この方法により、インフラストラクチャコードからドメインロジックを分離し、「just-this-once」を防ぐことができます。インフラストラクチャコードへの直接参照に応じてアプリケーションが終了する回避策。また、各サービスの実装でコンテナへの参照を持つ必要もありません。
これを行う場合、本当に重要なことの1つは、各「トップレベル」を作成できることを確認するテストがあることを確認することです。 IOCコンテナーの潜在的な構成ごとに(この場合はASP.NET MVCコントローラー)を入力します。
それ以外の場合、1つのインターフェイスを実装してアプリケーションの巨大なセクションを分割することを忘れがちです。