visual tutorial paginas net mvc entre ejemplos diferencias crear asp aplicaciones asp.net-mvc unit-testing asp.net-mvc-5 attributerouting

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.