2013-07-15 2 views
2

웹 API 프로젝트에서 현재 요청이 필요한 종속성이 있습니다.간단한 인젝터 - 생성자 현재 웹 API 요청의 HttpRequestMessage를 삽입하십시오.

코드는 아래와 같다 :

public interface IResourceLinker { 
    Uri Link(string routeName, object routeValues); 
} 

public class ResourceLinker : IResourceLinker { 
    private readonly HttpRequestMessage _request; 

    public ResourceLinker(HttpRequestMessage request) { 
    _request = request; 
    } 

    public Uri Link(string routeName, object routeValues) { 
    return new Uri(_request.GetUrlHelper() 
     .Link(routeName, routeValues)); 
    } 
} 

public class TestController : ApiController { 
    private IResourceLinker _resourceLinker; 

    public TestController(IResourceLinker resourceLinker) { 
    _resourceLinker = resourceLinker 
    } 

    public Test Get() { 
    var url = _resourceLinker.Link("routeName", routeValues); 

    // etc. 
    } 
} 

가 런타임시 용기에 현재 요청을 주입 할 수 있고, 간단한 인젝터 사용?

나는 다음과 같은 시도 :

public class InjectRequestHandler : DelegatingHandler 
{ 
    protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
    InjectRequest(request); 
    return base.SendAsync(request, cancellationToken); 
    } 

    public static void InjectCurrentRequestIntoContainer(
    HttpRequestMessage request) 
    { 
    var resolver = (SimpleInjectorDependencyResolver) 
     request.GetDependencyScope(); 
    resolver.Container.Register(() => request); 
    } 
} 

을하지만

컨테이너가하는 GetInstance, GetAllInstances에 대한 첫 번째 호출 후 변경 및 확인 할 수없는 다음과 같은 오류를 받았다.

현재 요청을 런타임에 컨테이너에 삽입 할 수있는 방법이 있습니까?

+0

HttpRequestMessage에서 확장 메소드로 링크 메소드를 정의하고'TestController'에서'this.Request.Link (...) '를 호출하지 않는 이유는 무엇입니까? 그렇지 않으면'HttpRequestMessage'가 런타임 값이기 때문에 생성자 삽입을 사용하는 대신'Link' 메쏘드에 메소드 인자로 넘겨 주어야합니다. – Steven

+0

스티븐, 귀하의 의견에 감사드립니다. simpleinjector의 작성자로서 런타임 등록이 가능합니까? –

+0

컨테이너에서 첫 번째 인스턴스를 확인한 후에는 새 등록을 추가 할 수 없습니다. 그러나 컨테이너 인 경우에도 HttpRequestMessage를 컨테이너에 등록하면 경쟁 조건이 발생하지만 컨테이너는 전역이지만 요청 당 하나의 메시지가 필요합니다. – Steven

답변

5

컨테이너는 등록 단계 후에 모든 등록을 차단합니다. 이것은 컨테이너의 사용을 등록과 해결의 두 단계로 나눕니다. Simple Injector가이 작업을 수행하는 유일한 컨테이너는 아닙니다. 예를 들어 Autofac은 구성 단계의 마지막 단계로 Build 방법을 사용하여 컨테이너를 빌드하는 ContainerBuilder을 사용하여 사용자가 등록 할 수있게하여이 작업을보다 명확하게 수행합니다.

Simple Injector는 주로 응용 프로그램 수명 기간 동안 등록을하면 경쟁 조건과 같은 모든 종류의 문제가있는 동작이 쉽게 발생할 수 있기 때문에이를 허용하지 않습니다. DI를 적용 할 때 등록을 중앙 집중화해야하는 반면 등록은 응용 프로그램 전체에 분산되어 있기 때문에 DI 구성을 훨씬 더 이해하기 어렵게 만듭니다. 부작용으로이 디자인은 컨테이너의 행복한 경로에 잠금이 없기 때문에 컨테이너가 다중 스레드 시나리오에서 선형 성능 특성을 가질 수 있습니다.

Simple Injector는 ResolveUnregisteredType 이벤트를 사용하여 저스트 - 인 - 시간 등록을 허용하지만 이는 귀하의 경우에 들어갈 방법이 아닙니다.

문제는 런타임에만 객체 그래프에 알려진 객체를 삽입하려는 것입니다. 컴파일 타임/설정 시간 의존성은 생성자를 통해 전달되어야하는 반면, 일반적으로 메소드 인자를 통해 런타임 의존성을 전달해야하기 때문에 이것은 최선의 방법이 아닙니다.

그러나 생성자 인수로 전달하는 것이 올바른지 확실하다면 요청의 수명 동안이 메시지를 캐시에 저장하고 캐시 된 값을 반환 할 수있는 등록을해야합니다. 예. 이 등록은 HttpContext.Items 사전에 캐시되는 HttpRequestMessage를 검색합니다

// using SimpleInjector.Advanced; // for IsVerifying() 

container.Register<HttpRequestMessage>(() => 
{ 
    var context = HttpContext.Current; 

    if (context == null && container.IsVerifying()) 
     return new HttpRequestMessage(); 

    object message = context.Items["__message"]; 
    return (HttpRequestMessage)message; 
}); 

:

처럼이 어떻게 보일지입니다. 해당 시점에 HttpContext.Current이 없으므로 검증 중에 (이 경우 container.Verify()에 전화 할 때)이 등록이 작동하도록 추가 검사가 있습니다.당신이 (당신이 일반적으로 really should BTW), 당신이 그것을 최소화 할 수있는 용기 검증에 대한 관심이하지 않으면 그러나이 등록으로

container.Register<HttpRequestMessage>(() => 
    (HttpRequestMessage)HttpContext.Current.Items["__message"]); 

을, 당신이해야 할 유일한 것은 캐시 HttpRequestMessage 예를 입니다

public static void InjectCurrentRequestIntoContainer(
    HttpRequestMessage request) 
{ 
    HttpContext.Current.Items["__message"] = request; 
} 

업데이트

:container.GetInstance 컨트롤러를 얻기 위해 호출하기 전에에는 GetCurrentHttpRequestMessage 확장 방법이 포함되어있어 현재 요청의 HttpRequestMessage을 검색 할 수 있습니다. 웹 API 통합 Wiki 페이지 describes이 확장 메소드 사용 방법.

+0

완벽한 솔루션 –

관련 문제