IOC, DLL 참조 및 어셈블리 스캔
-
03-07-2019 - |
문제
이 질문은 구조 맵과 관련이 있지만 내 일반 질문은 다음과 같습니다.
IOC 컨테이너로 구성 요소를 배선 할 때 코드에서 (이를 통해 구성하는 것과 반대로 XML) 일반적으로 모든 어셈블리에 대한 명시적인 프로젝트/빌드 참조가 필요합니까?
왜 별도의 어셈블리인가? 왜냐하면:
"구체적인 구현에서 별도의 어셈블리에 거주하는 추상 클래스는 그러한 분리를 달성하는 좋은 방법입니다." -프레임 워크 설계 지침 p.91
예시:
내가 가지고 있다고 가정 해 봅시다 personbase.dll 그리고 bob.dll
단발 추상 클래스에서 상속합니다 인력베이스. 둘 다 안에 있습니다 사람 네임 스페이스. 그러나 다른 어셈블리에서.
나는 프로그래밍하고있다 인력베이스, 아니다 단발.
내 주요 코드로 돌아가서 사람이 필요합니다. StructureMap은 어셈블리를 스캔 할 수 있습니다. 좋아, 나는 구조를 물어 보겠다!
이제 내 주요 코드에서는 물론 인력베이스, 그렇지 않습니다 단발. 나는 실제로 내 코드가 알고 싶지 않습니다 아무것 ~에 대한 단발. 프로젝트 참조, Nuthin이 없습니다. 그것이 요점입니다.
그래서 나는 말하고 싶다 :
//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 내 프로젝트에서 바로 내가 원하지 않는 것입니다.
스프링 + XML 구성을 사용 하여이 상황을 피할 수 있습니다. 그러나 나는 Spring + XML 구성으로 돌아 왔습니다 ...!
structuremap을 사용하거나 일반적인 원칙으로 무언가를 놓치고 있습니까?
관련 질문 : 구조 맵 및 스캐닝 어셈블리
해결책
나는 마침내 이것을 분류했다. 다음과 같이 보입니다.
IOC UML http://img396.imageshack.us/img396/1343/iocuml.jpg
어셈블리와 함께
- Core.exe
- personbase.dll (참조 시간을 컴파일하십시오 by core.exe)
- bob.dll (실행 시간을로드했습니다 StructureMap 스캔을 통해)
- Betty.dll (실행 시간을로드했습니다 StructureMap 스캔을 통해)
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 구성을 수행 할 수도 있습니다. 원하는 경우 섞을 수도 있습니다.
Bob 클래스에 넣을 수있는 구조적 속성도 있습니다. DefaultConstructor는 때때로 내가 사용하게 된 것입니다.
자동 스캔 옵션은 명명, 어셈블리 및 네임 스페이스 규칙을 유지할 때만 작동합니다. 유창한 인터페이스로 StructureMap을 수동으로 구성 할 수 있습니다. 예시:
ObjectFactory.Initialize(initialization =>
initialization.ForRequestedType<PersonBase>()
.TheDefault.Is.OfConcreteType<Bob>());
현재 프로젝트에서 수행하는 일 (구조 맵이 아닌 AutoFac을 사용하지만 차이를 만들어서는 안된다고 생각합니다) :
응용 프로그램이 핵심 어셈블리에서 사용하는 외부 서비스를 정의하는 인터페이스가 있습니다. App.Core
(개인베이스처럼).
그런 다음 이러한 인터페이스의 구현이 있습니다 Services.Real
(bob.dll처럼).
우리의 경우에도 우리에게도 있습니다 Service.Fake
, 다른 엔터프라이즈 서비스 및 데이터베이스 등에 대한 의존성으로 UI 테스트를 용이하게하는 데 사용됩니다.
프론트 엔드 "클라이언트"응용 프로그램 자체 (이 경우 ASP.NET MVC 앱) 참조 App.Core
.
앱이 시작되면 사용합니다 Assembly.Load
구성 설정을 기반으로 적절한 "서비스"구현 DLL을로드합니다.
이러한 각 DLL은 ISSERVICEREGISTRY를 구현하여 IT가 구현하는 서비스 목록을 반환합니다.
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();
}
... 응용 프로그램은 반사를 통해이 서비스 요법을 찾아이 서비스 인스턴스를 통해 열거하고 컨테이너에 등록합니다. 우리에게는이 레지스터-서비스는 웹 응용 프로그램에 살고 있지만 별도의 어셈블리에 넣을 수 있습니다.
이런 식으로 우리는 인프라 코드에서 도메인 로직을 분리하고 인프라 코드에 대한 직접 참조에 따라 응용 프로그램이 끝나는 "Just-Once"워크 어라운드를 방지 할 수 있습니다. 또한 각 서비스 구현에서 컨테이너를 참조 할 필요가 없습니다.
이 작업을 수행하는 경우 정말 중요한 것 : 확실한 IOC 컨테이너의 각 잠재적 구성으로 각 "최상위"유형 (ASP.NET MVC 컨트롤러)을 작성할 수 있는지 확인하는 테스트가 있습니다.
그렇지 않으면 하나의 인터페이스를 구현하고 응용 프로그램의 거대한 섹션을 깨뜨리는 것을 잊기 쉽습니다.