ASP.NET MVC 自定义路由约束和依赖注入

发布于 2024-12-18 16:01:35 字数 1613 浏览 0 评论 0原文

在我的 ASP.NET MVC 3 应用程序上,我有一个如下定义的路由约束:

public class CountryRouteConstraint : IRouteConstraint {

    private readonly ICountryRepository<Country> _countryRepo;

    public CountryRouteConstraint(ICountryRepository<Country> countryRepo) {
        _countryRepo = countryRepo;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {

        //do the database look-up here

        //return the result according the value you got from DB
        return true;
    }
}

我在我的应用程序上使用 Ninject 作为 IoC 容器,它实现了 IDependencyResolver 并且我注册了我的依赖项:

    private static void RegisterServices(IKernel kernel) {

        kernel.Bind<ICountryRepository<Country>>().
            To<CountryRepository>();
    }    

如何使用此路由以依赖注入友好的方式进行约束?

编辑

我找不到一种方法来传递对单元测试的依赖:

[Fact]
public void country_route_should_pass() {

    var mockContext = new Mock<HttpContextBase>();
    mockContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/countries/italy");

    var routes = new RouteCollection();
    TugberkUgurlu.ReservationHub.Web.Routes.RegisterRoutes(routes);

    RouteData routeData = routes.GetRouteData(mockContext.Object);

    Assert.NotNull(routeData);
    Assert.Equal("Countries", routeData.Values["controller"]);
    Assert.Equal("Index", routeData.Values["action"]);
    Assert.Equal("italy", routeData.Values["country"]);
}

On my ASP.NET MVC 3 App, I have a route constraint defined like below:

public class CountryRouteConstraint : IRouteConstraint {

    private readonly ICountryRepository<Country> _countryRepo;

    public CountryRouteConstraint(ICountryRepository<Country> countryRepo) {
        _countryRepo = countryRepo;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {

        //do the database look-up here

        //return the result according the value you got from DB
        return true;
    }
}

I am using Ninject as IoC container on my app which implements IDependencyResolver and I registered my dependency:

    private static void RegisterServices(IKernel kernel) {

        kernel.Bind<ICountryRepository<Country>>().
            To<CountryRepository>();
    }    

How can I use this route constraint with a dependency injection friendly manner?

EDIT

I cannot find a way to pass this dependency on unit test:

[Fact]
public void country_route_should_pass() {

    var mockContext = new Mock<HttpContextBase>();
    mockContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/countries/italy");

    var routes = new RouteCollection();
    TugberkUgurlu.ReservationHub.Web.Routes.RegisterRoutes(routes);

    RouteData routeData = routes.GetRouteData(mockContext.Object);

    Assert.NotNull(routeData);
    Assert.Equal("Countries", routeData.Values["controller"]);
    Assert.Equal("Index", routeData.Values["action"]);
    Assert.Equal("italy", routeData.Values["country"]);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

动听の歌 2024-12-25 16:01:35

虽然 @Darin 建议的方法有效,但注入的依赖项需要在应用程序的整个生命周期中保持活动状态。例如,如果依赖项的范围在请求范围内,那么它将适用于第一个请求,然后不适用于此后的每个请求。

您可以通过使用一个非常简单的 DI 包装器来限制路由来解决这个问题。

public class InjectedRouteConstraint<T> : IRouteConstraint where T : IRouteConstraint
{
private IDependencyResolver _dependencyResolver { get; set; }
public InjectedRouteConstraint(IDependencyResolver dependencyResolver)
{
    _dependencyResolver = dependencyResolver;
}

public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
    return _dependencyResolver.GetService<T>().Match(httpContext, route, parameterName, values, routeDirection);
}
}

然后像这样创建你的路线

var _dependencyResolver = DependencyResolver.Current; //Get this from private variable that you can override when unit testing

routes.MapRoute(
  "Countries",
  "countries/{country}",
  new { 
      controller = "Countries", 
      action = "Index" 
  },
  new { 
      country = new InjectedRouteConstraint<CountryRouteConstraint>(_dependencyResolver);
  }
);

编辑:试图使其可测试。

While the approach @Darin suggested works, the dependencies injected need to stay alive for the entire life of the application. If the scope of the dependency is in request scope, for example, then it will work for the first request then not for every request after that.

You can get around this by using a very simple DI wrapper for your route constraints.

public class InjectedRouteConstraint<T> : IRouteConstraint where T : IRouteConstraint
{
private IDependencyResolver _dependencyResolver { get; set; }
public InjectedRouteConstraint(IDependencyResolver dependencyResolver)
{
    _dependencyResolver = dependencyResolver;
}

public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
    return _dependencyResolver.GetService<T>().Match(httpContext, route, parameterName, values, routeDirection);
}
}

then create your routes like this

var _dependencyResolver = DependencyResolver.Current; //Get this from private variable that you can override when unit testing

routes.MapRoute(
  "Countries",
  "countries/{country}",
  new { 
      controller = "Countries", 
      action = "Index" 
  },
  new { 
      country = new InjectedRouteConstraint<CountryRouteConstraint>(_dependencyResolver);
  }
);

EDIT: tried to make it testable.

蓝眼睛不忧郁 2024-12-25 16:01:35
routes.MapRoute(
    "Countries",
    "countries/{country}",
    new { 
        controller = "Countries", 
        action = "Index" 
    },
    new { 
        country = new CountryRouteConstraint(
            DependencyResolver.Current.GetService<ICountryRepository<Country>>()
        ) 
    }
);
routes.MapRoute(
    "Countries",
    "countries/{country}",
    new { 
        controller = "Countries", 
        action = "Index" 
    },
    new { 
        country = new CountryRouteConstraint(
            DependencyResolver.Current.GetService<ICountryRepository<Country>>()
        ) 
    }
);
腻橙味 2024-12-25 16:01:35

您可以尝试使用属性注入和 IDependencyResolver

public class CountryRouteConstraint : IRouteConstraint {
    [Inject]
    public ICountryRepository<Country> CountryRepo {get;set;}
}

并非所有 IoC 容器都能很好地适应这一点; Ninject 有效。

我不确定这是否有效,不幸的是无法测试这个自动取款机。

另一种选择是使用服务定位器,您可以在其中提供一个静态对象来负责检索接口的实现。

You can try to use property injection and the IDependencyResolver

public class CountryRouteConstraint : IRouteConstraint {
    [Inject]
    public ICountryRepository<Country> CountryRepo {get;set;}
}

Not all IoC containers play well with this; Ninject works.

I'm not sure whether this will work, can't test this atm unfortunately.

Another option is to use a service locator instead, where you make a static object available that is responsible for retrieving an implementation of the interface.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文