2014-02-08 2 views
3

우리는 응용 프로그램 작동 방식을 사용자 정의하기 위해 도메인을 사용하고 있습니다. 나는 예에 그것을 설명합니다 :HttpContext의 매개 변수로 단일성을 통한 생성자 종속성 삽입

// default behavior 
public class CoreService : IService { 
    public virtual string Hello { get { return "Hello"; } } 
    public virtual string FavouriteDrink { get { return "Water"; } } 
} 

// german.site.com 
public class GermanService : CoreService { 
    public override string Hello { get { return "Gutten tag"; } } 
    public override string FavouriteDrink { get { return "Beer"; } } 
} 

// usa.site.com 
public class UsaService : CoreService { 
    public override string FavouriteDrink { get { return "Cofee"; } } 
} 

서비스는 다음과 같이 부트 스트랩됩니다

var container = new UnityContainer(); 
container.RegisterType<IService, CoreService>(); 
container.RegisterType<IService, GermanService>("german.site.com"); 
container.RegisterType<IService, UsaService>("usa.site.com"); 

내가 MVC 컨트롤러를 부트 스트랩 유니티를 사용합니다. IE :

public class HomeController : Controller { 
    private IService m_Service; 

    // contructor dependency injection magic - this resolves into "CoreService" 
    public HomeController([Dependency]IService service) { 
    if (service == null) { 
     throw new ArgumentNullException("service"); 
    } 
    m_Service = service; 
    } 
} 

도메인을 고려하여 단위 해상도를 변경하는 방법은 있습니까? 지금은

public class HomeController : Controller { 
    private IService m_Service; 

    // contructor dependency injection magic - a lot less magical 
    public HomeController() { 
    m_Service = DomainServiceLocator.Retrieve<IService>(); 
    } 
} 

지원 클래스와 함께 결국 : 런타임에 뭔가를 해석 할 때

public static class DomainServiceLocator { 
    private static UnityContainerAdapter adapter; 

    public static T Retrieve<T>() { 
    string domain = HttpContext.Current.Request.Url.Host; 
    if (adapter.IsServiceRegistered(typeof(T), domain)) { 
     return adapter.Resolve<T>(domain); 
    } 

    return adapter.Resolve<T>(); 
    } 
} 

public class QueryableContainerExtension : UnityContainerExtension { 
    private List<RegisterInstanceEventArgs> registeredInstances = new List<RegisterInstanceEventArgs>(); 
    private List<RegisterEventArgs> registeredTypes = new List<RegisterEventArgs>(); 

    protected override void Initialize() { 
    this.Context.Registering += (sender, e) => { this.registeredTypes.Add(e); }; 
    this.Context.RegisteringInstance += (sender, e) => { this.registeredInstances.Add(e); }; 
    } 


    public bool IsServiceRegistered(Type service, string name) { 
    return registeredTypes.FirstOrDefault(e => e.TypeFrom == service && e.Name == name) != null 
      || registeredInstances.FirstOrDefault(e => e.RegisteredType == service && e.Name == name) != null; 
    } 
} 

public class UnityContainerAdapter { 
    private readonly QueryableContainerExtension queryableContainerExtension; 
    private readonly IUnityContainer unityContainer; 

    public UnityContainerAdapter() 
    : this(new UnityContainer()) { 
    } 

    public UnityContainerAdapter(IUnityContainer unityContainer) { 
    this.unityContainer = unityContainer; 

    // adding extensions to unity container 
    this.queryableContainerExtension = new QueryableContainerExtension(); 
    unityContainer.AddExtension(this.queryableContainerExtension); 
    } 

    public T Resolve<T>(string name) { 
    return unityContainer.Resolve<T>(name); 
    } 

    public T Resolve<T>() { 
    return unityContainer.Resolve<T>(); 
    } 

    public bool IsServiceRegistered(Type service, string name) { 
    return this.queryableContainerExtension.IsServiceRegistered(service, name); 
    } 
} 
+1

이것을 알아 냈습니까? – user1987392

답변

1

내가이 시나리오에서 사출 공장을 사용하는 것을 좋아합니다. 기본적으로 당신은 도메인 이름을 통해 유형을 해결하고 있습니다 :

그래서 컴포지션 루트에이 같은 등록 할 수 있습니다 :

container.RegisterType<Func<string, IService>> 
       (
        new InjectionFactory(c => new Func<string, IService>(name => c.Resolve<IService>(name))) 
       ); 

이 그런 다음 HomeController 당신이 주입 할 수있는 대리자를

public class HomeController 
{ 
    private readonly Func<string,IService> _serviceFactory; 

    public HomeController(Func<string, IService> serviceFactory) 
    { 
     if(serviceFactory==null) 
      throw new ArgumentNullException("serviceFactory"); 

     this._serviceFactory= serviceFactory; 
    } 

    public void DoSomethingWithTheService() 
    { 
     var domain = this.HttpContext.Uri.Host;        
     var service = this._serviceFactory(domain); 
     var greeting = service.Hello; 
    } 
} 

```

이것은 여전히 ​​단위 테스트 가능하며 DI가 포함 된 구현을 "구성 루트"외부로 유출하지 않았습니다.

또한 CoreService를 추상화하여 직접 인스턴스화하지 않아야합니까?

+0

감사합니다. Func <> injection을 싫어합니다. 대신 공장을 만들었지 만 일반적으로 이것은 갈 길입니다. –

0

아래의 해결책은 @Spencer 아이디어를 기반으로합니다. 나는 공장을 만들었고 공장에 대한 기본 구현은 DI 컨테이너 자체 (내 경우에는 IUnityContainer)에 대한 참조를 가지므로 일단 물어 보면 도메인을 기반으로 한 해상도를 수행 할 수 있습니다. 현재의 ASP.NET (ASP.NET CORE) 세대에는 현재 HttpContext을 제공하는 마법 싱글 톤이없고 DI가 프레임 워크에 하드 코딩되어 있기 때문에 더 "현대적"입니다.

public interface IFactory<T> 
{ 
    T Retrieve(string domain); 
} 

internal sealed class Factory<T> : IFactory<T> 
{ 
    private readonly IUnityContainer _container; 

    public Factory(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public T Resolve(string domain) 
    { 
     // this is actually more complex - we have chain inheritance here 
     // for simplicity assume service is either registered for given 
     // domain or it throws an error 
     return _container.Resolve<T>(domain); 
    } 
} 

// bootstrapper 
var container = new UnityContainer(); 
container.RegisterType<IService, CoreService>(); 
container.RegisterType<IService, GermanService>("german.site.com"); 
container.RegisterType<IService, UsaService>("usa.site.com"); 
container.RegisterInstance<IFactory<IService>>(new Factory<IService>(container));  

그리고 홈 컨트롤러는 언급도

public class HomeController : Controller { 
    private IFactory<IService> m_Factory; 

    public HomeController(IFactory<IService> factory) { 
    m_Factory = factory; 
    } 

    private void FooBar() { 
    var service = m_Factory.Retrieve(this.HttpContext.Uri.Host); 
    var hello = service.Hello; 
    } 
} 

그와 같은 가치가 보인다 - 나는 게으른 해요로 - 나는

[Domain("german.site.com")] 
public class GermanService : IService { ... } 

[DomainRoot] 
public class CoreService : IService { ... } 

[Domain("usa.site.com")] 
public class UsaService : CoreService { ... } 

처럼 장식 속성의 시스템을 구축했습니다 따라서 부트 스트랩은 주어진 어셈블리의 모든 유형에서 자동으로 수행됩니다. 그러나 그 부분은 약간 길어요. 관심있는 사람이 있다면 github에 게시 할 수 있습니다.

관련 문제