2016-09-21 1 views
1

컨트롤러가 일부 저장 레이어에 의존하는 웹 API 프로젝트가 있습니다. 예 :캐슬 윈저 - 패스가 하위 레이어에 대한 인수를 해결합니다.

public class ResourceController : ApiController 
{ 
    private readonly IResourceStorage _resourceStorage; 
    private readonly IOtherStorage _otherStorage; 


    public ResourceController(IResourceStorage resourceStorage, IOtherStorage otherStorage) 
    { 
     _resourceStorage = resourceStorage; 
     _otherStorage = otherStorage; 
    } 

    // Web API Actions 
} 

저장을위한 공통 코드는 다음과 같습니다 : 웹 API 요청의 일부 특정 조건에 따라

public class ResourceStorage : DBProvider, IResourceStorage 
{ 
    public ResourceStorage(string connectionString) : base(connectionString) 
    { 
    } 

    // storage methods 
} 

, 내가 다른 connectionStrings를 주입 할 수 있어야합니다 각 컨트롤러는 유사한 코드를 가지고 컨트롤러의 저장소에. 의사 코드는 그렇게 볼 수 있었다 :

public class WindsorControllerActivator : IHttpControllerActivator 
{ 
    public IHttpController Create(
     HttpRequestMessage request, 
     HttpControllerDescriptor controllerDescriptor, 
     Type controllerType) 
    { 
     string connectionString = ChooseDbConnectionStringBasedOnRequestContext(request); 

     var controller = 
     (IHttpController)_container.Resolve(connectionString, controllerType, new {databaseType = connectionString}); 

     request.RegisterForDispose(
     new Release(
      () => _container.Release(controller))); 

     return controller; 
    } 
} 

내가 덜 파괴적인 방법 : 내가 시도 무엇

에 저장에 매개 변수를 전달하는 방법을 몰라? 대안 :

  1. 패스 성 윈저 추가 인수로 parameterers 및 DynamicParameters를 사용하여 다음 층에서 취급합니다. 인수는 컨트롤러 레이어를 얻지 만 저장소로 가져올 수있는 방법을 찾을 수 없습니다. 자체적으로 CreationContext이 있으며 컨트롤러에서 저장소로 전달할 수있는 방법을 찾을 수 없습니다.
  2. N (연결 문자열 수와 같음) 컨테이너가 있고 그 중 하나를 선택하십시오. ControllerActivator. 거대하고 추악한 솔루션인데 완전히 유연하지는 않지만 작동합니다.
  3. 각자 고유 한 이름과 저장 장치가있는 N 세트의 컨트롤러를 만듭니다. DynamicParameters 처리기의 구성 요소 이름을 확인하고 연결 문자열을 선택하십시오. ControllerActivator에서 키를 올바른 컨트롤러 세트로 전달하십시오. 또한 추한 - 너무 많은 컨트롤러 및 많은 배관 코드 등록.

답변

1

당신은 공장 패턴을 사용할 수 있습니다 다음

public interface IResourceStorageFactory 
{ 
    IResourceStorage Create(int numberOfResources); 
} 

public class ResourceStorageFactory : IResourceStorageFactory 
{ 
    public IResourceStorage Create(HttpRequestMessage request) 
    { 
     var connectionString = ChooseDbConnectionStringBasedOnRequestContext(request); 

     return new ResourceStorage(connectionString); 
    } 
} 

등을 간단하게

private readonly IResourceStorage _resourceStorage; 
private readonly IOtherStorage _otherStorage; 

public ResourceController(IResourceStorageFactory resourceStorageFactory, IOtherStorage otherStorage) 
{ 
    _resourceStorage = resourceStorageFactory.Create(Request); 
    _otherStorage = otherStorage; 
} 
+0

그것은 훨씬 덜 관입입니다. 제안 해 주셔서 감사합니다.여전히 여기에 2 가지 문제가 있습니다 : 1) 모든 컨트롤러에 추가 매개 변수 StorageFactory를 제공해야합니다 (일반화 할 수 있지만 더 많은 매개 변수를 사용할 수 있음). 2) 하나의 저장소를 할당하기 전에 create를 호출해야합니다. 침입의 종류. 귀하의 답을 아직 받아 들일 수 없다고 표기하고 누군가가 더 자연스러운 해결책을 찾길 희망합니다. –

+0

예를 들어, 주입 된 특정 유형의 IR 주입 주입에 공장을 사용할 수 있습니까? –

0

내가 연결 문자열 제공자 도입 발견 된 솔루션 :

public interface IConnectionStringProvider 
{ 
    string ConnectionString { get; } 
} 

을 언제 다음 컨트롤러

kernel.Register(
    Component.For(typeof (IConnectionStringProvider)) 
      .ImplementedBy(typeof (ConnectionStringProvider)) 
      .UsingFactoryMethod(
       (k, context) => 
        new ConnectionStringProvider(context.AdditionalArguments["connectionString"].ToString())) 
      .LifestylePerWebRequest()); 

을 그리고 컨트롤러 활성제 내부에 먼저 올바른 매개 변수를 사용하여 연결 문자열 제공자를 해결 : 여전히

// The lifecycle of connection string provider if per web request. 
// We resolve it first time with the correct parameters, 
// so it is injected with the correct connection string throught the lifecycle of the request. 
_container.Resolve<IConnectionStringProvider>(new {connectionString}); 

var controller = 
    (IHttpController) _container.Resolve(controllerType); 

그것은 완벽하지를 공장 방법으로 웹 요청에 따라이 공급자를 등록 일종의 hackish 보이지만 그것은 하위 계층의 인터페이스에 의존성을 유지하고 찾을 솔루션에서 코드베이스의 적은 변화가 필요합니다

관련 문제