如何注册Drum.UriMaker<>使用Simple Injector?

时间:2014-10-30 19:24:10

标签: c# asp.net-web-api dependency-injection unity-container simple-injector

我使用Drum提供了一个通用类`UriMaker:

public class UriMaker<TController>
{
    // I need use this one
    public UriMaker(UriMakerContext context, HttpRequestMessage request) { }

    public UriMaker(Func<MethodInfo, RouteEntry> mapper, UrlHelper urlHelper) { }
}

像这样使用:

public class UserController : ApiController
{
    public UserController(UriMaker<UserController> urlMaker) {}
}

我曾经在Unity注册过它:

container.RegisterType(typeof(UriMaker<>),
    new InjectionConstructor(typeof(UriMakerContext), typeof(HttpRequestMessage)));

但现在正在迁移到Simple Injector。我已经有了这个:

UriMakerContext uriMaker = config.MapHttpAttributeRoutesAndUseUriMaker();
container.RegisterSingle(uriMakerContext);

那么现在如何注册UriMaker<>呢?

1 个答案:

答案 0 :(得分:2)

尽管可以配置Simple Injector以允许将UriMaker<TController>直接注入您的控制器,但我强烈建议您出于多种原因。

首先,您应该尽量减少应用程序对外部库的依赖性。这可以通过定义特定于应用程序的抽象(符合ISP)来轻松完成。

其次,直接注入UriMaker会使您极难测试,因为UriMaker被拉入您的测试代码,而它假设一个活动的HTTP请求并假设Web API路由系统是配置正确。这些都是您不希望您的测试代码依赖的内容。

最后,它使verifying the object graph变得更难,因为UriMaker依赖于HttpRequestMessage,这是一个运行时值。通常,不应将运行时值注入服务的构造函数中。您应该使用组件(包含应用程序行为的东西)构建对象图,并在构建后通过对象图发送运行时数据。

相反,我建议采用以下抽象:

public interface IUrlProvider
{
    Uri UriFor<TController>(Expression<Action<TController>> action);
}

现在您的控制器可以依赖于此IUrlProvider,而不是依赖于外部库:

public class UserController : ApiController
{
    private readonly IUrlProvider urlProvider;

    public UserController(IUrlProvider urlProvider)
    {
        this.urlProvider = urlProvider;
    }

    public string Get()
    {
        this.urlProvider.UriFor<HomeController>(c => c.SomeFancyAction());
    }
}

在幕后,您当然仍需要致电Drum,为此您需要为IUrlProvider定义代理实施:

public class DrumUrlProvider : IUrlProvider
{
    private readonly UriMakerContext context;
    private readonly Func<HttpRequestMessage> messageProvider;

    public DrumUrlProvider(UriMakerContext context,
        Func<HttpRequestMessage> messageProvider)
    {
        this.context = context;
        this.messageProvider= messageProvider;
    }

    public Uri UriFor<TController>(Expression<Action<TController>> action)
    {
        HttpRequestMessage message = this.messageProvider.Invoke();
        var maker = new UriMaker<TController>(this.context, message);
        return maker.UriFor(action);
    }
}

此实现可以通过以下方式注册为singleton:

container.EnableHttpRequestMessageTracking(config);

UriMakerContext uriMakerContext =
    config.MapHttpAttributeRoutesAndUseUriMaker();

IUrlProvider drumProvider = new DrumUrlProvider(uriMakerContext, 
    () => container.GetCurrentHttpRequestMessage());

container.RegisterSingle<IUrlProvider>(drumProvider);

此示例使用Simple Injector Web API integration package允许使用HttpRequestMessageEnableHttpRequestMessageTracking扩展方法检索当前请求GetCurrentHttpRequestMessage,如here所述。