2014-04-18 2 views
0

최근에 MVC 애플리케이션이 Unity 종속성 삽입을 사용하여 종속성을 해결하도록 리팩터링했습니다. 그것은 훨씬 더 분해 가능합니다.Unity - 의존성 문제를 해결하기 위해 요청한 정보를 사용합니다.

내가 지금하고있는 일은 그것을 사용하는 여러 임차인을위한 능력을 추가하는 것입니다. 내가 사용하고있는 접근법 (나머지 코드는 세입자에 대해 많이 알 필요가 없도록)은 다른 저장소의 프록시 인 저장소 인터페이스의 세입자 필터링 버전과 같은 것을 생성합니다. 그래서 기본 메소드 중 하나를 호출 한 다음 레코드에 올바른 세입자가 있는지 확인하고 그에 따라 행동하십시오. 따라서 기본적으로 데이터가 분리되어 있지 않아도 클라이언트 코드를 변경해야 할 필요가 없더라도 각 테넌트에 대해 완전히 별개의 저장소가있는 것을 에뮬레이트 할 수 있습니다.

이 모든 문제는 DI 작업 방식에 어떻게 부합되는지입니다. 내가하려는 계획은 요청이 시작될 때 호스트 이름을 감지 한 다음이를 사용하여 임차인을 결정하는 것입니다 (각 임차인은 DB에 호스트 이름 목록을 갖게됩니다). 대부분의 객체에 대해 요청 당 수명을 사용하고 있지만, Unity는 구성 및 해결 중입니다. 요청에 대한 데이터가 필요하기 때문에 Unity가 어떻게 사용하는지 "알 수"있습니다. 가지고 있지만 컨테이너 구성 방법에서 사용 가능하지 않다고 생각합니다.) 그리고 데이터베이스에 액세스하여 어느 호스트를 알 수 있습니까 (컨테이너 구성에서 데이터베이스 호출을하는 것이 바람직하지 않음). # 2를 해결할 수있는 것은 단지 호스트 이름을 전달하고 세입자와 클래스를 작성하여 어느 세입자가 참조되고 있는지를 파악할 수 있지만 # 1에서는 도움이되지 않습니다.

지금은 "속성 주입"(덜 높은 falutin '서클에서 "공용 속성"으로도 알려짐)을 사용하고 있지만 컨트롤러를 사용하지 않도록하는 방법을 알지 못합니다. 실제로 세입자 데이터를 입력하는 곳입니다. 이제는 모든 것을 제어하는 ​​하나의 구성 루트가 없습니다.

구성 루트에서이 작업을 수행 할 수있는 방법이 있습니까? 아니면 컨트롤러에서이 작업을 수행하도록하는 것이 좋습니다.

답변

1

어떤 이유로 주입 공장을 잊어 버리는 것 같습니다. 팩토리에 대해 인터페이스/유형을 등록하면 요청시, 컨설턴트 데이터베이스를 비롯하여 해결할 때 임의로 복잡한 코드를 실행할 수 있습니다. 당신이 그것을 필요로 할 때마다, 대상도 공장 코드가 단순 매핑에서 유형 인스턴스보다는 실행 된 것을 알고하지 않도록

container.RegisterType<IRepository>( 
    new InjectionFactory( 
     c => { 
      // whatever, consult the database 
      // whatever, consult the url 
      return ...; 
     }); 

공장 조성물은 투명합니다.

+0

그런데 어떻게 그 방법으로 URL을 찾을 수 있습니까? 이 시점에서 범위에 있지 않습니까? – Casey

+0

나는이 방법으로 내 문제를 간결하게 설명 할 수 있다고 생각한다. 내가하고 싶은 일을하기 위해서, 나는 composition root 내에서 요청 데이터에 접근 할 필요가있다. 그러나 내가 아는 한 내가 접근 할 권한이 없다. 컨테이너를 구성 할 때 현재 요청 컨텍스트 중 하나. – Casey

+0

'HttpContext.Current.Request.Url' –

2

어딘가에 데이터베이스 호출을해야합니다. 어쩌면 가장 간단한 장소는 global.ascx 일 것입니다.

private static ConcurrentDictionary<string, string> _tenantCache = new ConcurrentDictionary<string, string>(); 

protected virtual void Application_BeginRequest(object sender, EventArgs e) 
{ 
    HttpApplication app = (HttpApplication)source; 
    var tenantId = _tenantCache.GetOrAdd(app.Context.Request.Url.Host, host => 
      { 
       // Make database call in this class 
       var tenant = new TenantResolver(); 
       return tenant.GetTenantId(host); 
      }) 
    app.Context.Items["TenantID"] = tenantId ; 
} 

Application_BeginRequest가 많이 호출되므로 결과를 캐싱해야합니다. 그런 다음 아이디 컨테이너를 갖도록 Unity를 구성 할 수 있습니다. 상위 컨테이너에 모든 공통/기본 매핑을 배치 한 다음 세입자별로 하위 컨테이너를 만들고 각 하위 컨테이너에 올바른 구현을 등록합니다.

그런 다음 IDependencyResolver를 구현하여 올바른 하위 컨테이너를 반환하십시오.

public class TenantDependencyResolver : IDependencyResolver 
{ 
    private static IUnityContainer _parentContainer; 
    private static IDictionary<string, IUnityContainer> _childContainers = new Dictionary<string, IUnityContainer>(); 

    public TenantDependencyResolver() 
    { 
     var fakeTenentID = "localhost"; 
     var fakeTenentContainer = _parentContainer.CreateChildContainer(); 
     // register any specific fakeTenent Interfaces to classes here 

     //Add the child container to the dictionary for use later 
     _childContainers[fakeTenentID] = fakeTenentContainer; 
    } 

    private IUnityContainer GetContainer() 
    { 
     var tenantID = HttpContext.Current.Items["TenantID"].ToString(); 
     if (_childContainers.ContainsKey(tenantID) 
     { 
      return _childContainers[tenantID]; 
     } 
     return _parentContainer; 
    } 

    public object GetService(Type serviceType) 
    { 
     var container = GetContainer(); 
     return container.Resolve(serviceType); 
    } 

    public IEnumerable<object> GetServices(Type serviceType) 
    { 
     var container = GetContainer(); 
     return container.ResolveAll(serviceType); 
    } 
} 

그런 다음 ASP.NET MVC DependecyResolver를 TenantDependencyResolver로 설정하십시오. 나는이 코드를 실행하지 않았지만 당신이해야 할 일에 대한 아이디어를 줄 것입니다. 구현이 설정되면 TenantDependecyResolver의 정적 생성자에서 구현을 수행 할 수 있습니다.

+0

아주 흥미 롭습니다. 이런 식으로 생각했을지도 모르겠다. – Casey

+0

@CharlesNRice : 임차인 당 여분의 아동용 컨테이너가있는 것이 과잉은 아니겠습니까? 각 임차인마다 서로 다른 구현이 등록되면 이는 의미가 있습니다. 그는 반면에, 훨씬 더 간단한 것을 필요로하는 것 같습니다. –

+0

@CharlesNRice : 또한 'BeginRequest'에 넣은 코드는 리팩토링을 요청합니다. 여기서 리팩터링은 별도의 클래스에 넣고'HttpContext.Current.Items'에 내부적으로 액세스하는 속성을 래핑하여 실행 만합니다 필요시 무조건적으로 각 요청보다는 오히려. –

관련 문제