asp.net mvc - tutorial - MapMvcAttributeRoutes: no se puede llamar a este método durante la fase de inicialización previa al inicio de la aplicación
web forms c# ejemplos (4)
Tengo una prueba muy simple en un proyecto de prueba en una solución que usa ASP MVC V5 y enrutamiento de atributos. El enrutamiento de atributos y el método MapMvcAttributeRoutes
son parte de ASP MVC 5.
[Test]
public void HasRoutesInTable()
{
var routes = new RouteCollection();
routes.MapMvcAttributeRoutes();
Assert.That(routes.Count, Is.GreaterThan(0));
}
Esto resulta en:
System.InvalidOperationException :
This method cannot be called during the applications pre-start initialization phase.
La mayoría de las respuestas a este mensaje de error implica configurar proveedores de membresía en el archivo web.config
. Este proyecto no tiene proveedores de membresía ni un archivo web.config
, por lo que el error parece estar ocurriendo por algún otro motivo. ¿Cómo puedo sacar el código de este estado de "pre-inicio" para que se puedan ejecutar las pruebas?
El código equivalente para los atributos en ApiController
funciona bien después de HttpConfiguration.EnsureInitialized()
.
¿Qué estás probando aquí? Parece que estás probando un método de extensión de terceros. No debe usar las pruebas de su unidad para probar el código de terceros.
Bueno, es realmente feo y no estoy seguro si valdrá la pena la complejidad de la prueba, pero así es cómo puedes hacerlo sin modificar tu código RouteConfig.Register:
[TestClass]
public class MyTestClass
{
[TestMethod]
public void MyTestMethod()
{
// Move all files needed for this test into a subdirectory named bin.
Directory.CreateDirectory("bin");
foreach (var file in Directory.EnumerateFiles("."))
{
File.Copy(file, "bin//" + file, overwrite: true);
}
// Create a new ASP.NET host for this directory (with all the binaries under the bin subdirectory); get a Remoting proxy to that app domain.
RouteProxy proxy = (RouteProxy)ApplicationHost.CreateApplicationHost(typeof(RouteProxy), "/", Environment.CurrentDirectory);
// Call into the other app domain to run route registration and get back the route count.
int count = proxy.RegisterRoutesAndGetCount();
Assert.IsTrue(count > 0);
}
private class RouteProxy : MarshalByRefObject
{
public int RegisterRoutesAndGetCount()
{
RouteCollection routes = new RouteCollection();
RouteConfig.RegisterRoutes(routes); // or just call routes.MapMvcAttributeRoutes() if that''s what you want, though I''m not sure why you''d re-test the framework code.
return routes.Count;
}
}
}
El mapeo de rutas de atributos necesita encontrar todos los controladores que está utilizando para obtener sus atributos, lo que requiere acceder al administrador de compilación, que aparentemente solo funciona en los dominios de aplicaciones creados para ASP.NET.
En ASP.NET MVC 5.1 esta funcionalidad se movió a su propia clase llamada AttributeRoutingMapper
.
(Esta es la razón por la que uno no debe confiar en el pirateo de código en clases internas)
Pero esta es la solución para 5.1 (¿y arriba?):
public static void MapMvcAttributeRoutes(this RouteCollection routeCollection, Assembly controllerAssembly)
{
var controllerTypes = (from type in controllerAssembly.GetExportedTypes()
where
type != null && type.IsPublic
&& type.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)
&& !type.IsAbstract && typeof(IController).IsAssignableFrom(type)
select type).ToList();
var attributeRoutingAssembly = typeof(RouteCollectionAttributeRoutingExtensions).Assembly;
var attributeRoutingMapperType =
attributeRoutingAssembly.GetType("System.Web.Mvc.Routing.AttributeRoutingMapper");
var mapAttributeRoutesMethod = attributeRoutingMapperType.GetMethod(
"MapAttributeRoutes",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(RouteCollection), typeof(IEnumerable<Type>) },
null);
mapAttributeRoutesMethod.Invoke(null, new object[] { routeCollection, controllerTypes });
}
Recientemente actualicé mi proyecto a ASP.NET MVC 5 y experimenté exactamente el mismo problema. Cuando uso dotPeek para investigarlo, descubrí que hay un método interno de extensión MapMvcAttributeRoutes
que tiene un IEnumerable<Type>
como parámetro que espera una lista de tipos de controladores. Creé un nuevo método de extensión que usa la reflexión y me permite probar mis rutas basadas en atributos:
public static class RouteCollectionExtensions
{
public static void MapMvcAttributeRoutesForTesting(this RouteCollection routes)
{
var controllers = (from t in typeof(HomeController).Assembly.GetExportedTypes()
where
t != null &&
t.IsPublic &&
t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
!t.IsAbstract &&
typeof(IController).IsAssignableFrom(t)
select t).ToList();
var mapMvcAttributeRoutesMethod = typeof(RouteCollectionAttributeRoutingExtensions)
.GetMethod(
"MapMvcAttributeRoutes",
BindingFlags.NonPublic | BindingFlags.Static,
null,
new Type[] { typeof(RouteCollection), typeof(IEnumerable<Type>) },
null);
mapMvcAttributeRoutesMethod.Invoke(null, new object[] { routes, controllers });
}
}
Y así es como lo uso:
public class HomeControllerRouteTests
{
[Fact]
public void RequestTo_Root_ShouldMapTo_HomeIndex()
{
// Arrange
var routes = new RouteCollection();
// Act - registers traditional routes and the new attribute-defined routes
RouteConfig.RegisterRoutes(routes);
routes.MapMvcAttributeRoutesForTesting();
// Assert - uses MvcRouteTester to test specific routes
routes.ShouldMap("~/").To<HomeController>(x => x.Index());
}
}
Un problema ahora es que dentro de RouteConfig.RegisterRoutes(route)
no puedo llamar a routes.MapMvcAttributeRoutes()
así que moví esa llamada a mi archivo Global.asax en su lugar.
Otra preocupación es que esta solución es potencialmente frágil ya que el método anterior en RouteCollectionAttributeRoutingExtensions
es interno y podría eliminarse en cualquier momento. Un enfoque proactivo sería comprobar si la variable mapMvcAttributeRoutesMethod
es nula y proporcionar un mapMvcAttributeRoutesMethod
error / excepción apropiado, si es así.
NOTA: Esto solo funciona con ASP.NET MVC 5.0. Hubo cambios significativos en el enrutamiento de atributos en ASP.NET MVC 5.1 y el método mapMvcAttributeRoutesMethod
se movió a una clase interna.