net mvc asp c# asp.net-core asp.net-core-mvc asp.net-core-viewcomponent

c# - mvc - return view net core



ASP.NET MVC 6: vea los componentes en un ensamblaje separado (2)

He investigado un poco sobre Github y descubrí que el motor de Razor ( link ) utiliza el método IFileInfo GetFileInfo(string subpath) ) para obtener archivos reales para compilar.

Implementación actual de este método.

public IFileInfo GetFileInfo(string subpath) { if (string.IsNullOrEmpty(subpath)) { return new NotFoundFileInfo(subpath); } // Relative paths starting with a leading slash okay if (subpath.StartsWith("/", StringComparison.Ordinal)) { subpath = subpath.Substring(1); } // Absolute paths not permitted. if (Path.IsPathRooted(subpath)) { return new NotFoundFileInfo(subpath); } var fullPath = GetFullPath(subpath); if (fullPath == null) { return new NotFoundFileInfo(subpath); } var fileInfo = new FileInfo(fullPath); if (FileSystemInfoHelper.IsHiddenFile(fileInfo)) { return new NotFoundFileInfo(subpath); } return new PhysicalFileInfo(_filesWatcher, fileInfo); } private string GetFullPath(string path) { var fullPath = Path.GetFullPath(Path.Combine(Root, path)); if (!IsUnderneathRoot(fullPath)) { return null; } return fullPath; }

Aquí podemos ver que las rutas absolutas no están permitidas y el método GetFullPath combina la ruta con Root que es la ruta principal de la raíz de la aplicación web.

Entonces asumo que no puedes abrir ViewComponent desde otra carpeta que no sea la actual.

Me gustaría definir los componentes de vista (que son nuevos en ASP.NET MVC 6) en un ensamblaje separado del proyecto de inicio web MVC 6 para poder reutilizarlos en varios proyectos web. Una solución de ejemplo podría verse así:

  • BookStore.Components (contiene componentes de vista comunes)
  • BookStore.Web1 (hace referencia a BookStore.Components)
  • BookStore.Web2 (hace referencia a BookStore.Components)

Creé una nueva biblioteca de clases (paquete) y creé un componente de vista dentro. También creé la vista siguiendo la convención de carpetas anidadas. El proyecto My BookStore.Components ve así:

Cuando intento invocar este componente de vista desde mi proyecto web:

@Component.Invoke("BookOfTheMonth")

... Me sale un error 500 con un cuerpo de contenido vacío. Parece que se descubrió la clase ViewComponent, pero no la vista de afeitar para el componente.

También intenté extender DefaultViewComponentDescriptorProvider para que los componentes de la vista del ensamblaje BookStore.Components puedan ser descubiertos:

Definido un Proveedor de Asamblea

public class AssemblyProvider : IAssemblyProvider { public IEnumerable<Assembly> CandidateAssemblies { get { yield return typeof(AssemblyProvider).Assembly; yield return typeof(BookStore.Components.BookOfTheMonthViewComponent).Assembly; } } }

AssemblyProvider registrado utilizando Autofac

builder.RegisterType<AssemblyProvider>() .AsImplementedInterfaces(); builder.RegisterType<DefaultViewComponentDescriptorProvider>() .AsImplementedInterfaces();

No estoy seguro de si el registro de DefaultViewComponentDescriptorProvider anterior es necesario o no, por lo que lo intenté con y sin él, pero todavía obtengo un error 500 en una página donde se invoca el componente de vista.

¿Cómo puedo invocar un componente de vista que vive en un ensamblado separado del proyecto web MVC6?


Actualización 2017-03-09

Las cosas han cambiado un poco en Visual Studio 2017 usando MS Build. Por suerte es mucho más simple. Aquí es cómo hacer que esto funcione:

En el ensamblaje externo, agregue esto al archivo csproj:

<ItemGroup> <EmbeddedResource Include="Views/**/*.cshtml" /> </ItemGroup>

En el proyecto web principal, agregue este paquete NuGet: Microsoft.Extensions.FileProviders.Embedded

Luego, en Inicio, agregue el ensamblaje externo a la lista de proveedores de archivos:

services.Configure<RazorViewEngineOptions>(options => { options.FileProviders.Add(new EmbeddedFileProvider( typeof(SampleClassInAssembly).Assembly # Prior to .Net Standard 2.0 # typeof(SampleClassInAssembly).GetTypeInfo().Assembly )); });

Dejaré la respuesta original a continuación por ahora, en caso de que la gente aún intente que esto funcione con versiones anteriores de .Net Core y project.json .

================================================== ==============

Aquí están los pasos para hacer este trabajo.

  • Asegúrese de que la estructura de su vista en el ensamblaje de componentes sea la misma que su proyecto web. Tenga en cuenta que hubo un error en la captura de pantalla que publiqué junto con mi pregunta.
  • Registre CompositeFileProvider en Startup.cs del proyecto web:

    services.Configure<RazorViewEngineOptions>(options => { options.FileProvider = new CompositeFileProvider( new EmbeddedFileProvider( typeof(BookOfTheMonthViewComponent).GetTypeInfo().Assembly, "BookStore.Components" ), options.FileProvider ); });

Tanto CompositeFileProvider como EmbeddedFileProvider son nuevos, por lo que deberá obtenerlos de la aspnetvnext NuGet de aspnetvnext. Hice esto agregando esta fuente:

Agregue las dependencias en project.json :

"Microsoft.AspNet.FileProviders.Composite": "1.0.0-*", "Microsoft.AspNet.FileProviders.Embedded": "1.0.0-*",

Por último, agregue esto al project.json del ensamblaje de Components :

"resource": "Views/**"

Eso debería ser suficiente para que esto funcione.

Aquí hay una demostración de trabajo: https://github.com/johnnyoshika/mvc6-view-components/tree/master

Esta respuesta se formuló a partir de esta discusión aquí: https://github.com/aspnet/Mvc/issues/3750

Actualización 2016-01-15 Actualmente hay un problema doloroso con los componentes de la vista externa. Cualquier cambio que realice en el archivo cshtml de la vista no se recompilará automáticamente. Incluso un Visual Studio forzado limpio y reconstruido no lo hace. Debe cambiar un archivo .cs en el ensamblaje de componentes para desencadenar una recompilación de vista, pero parece que esto es algo que se corregirá en el futuro. El motivo de este problema se explica aquí: https://github.com/aspnet/Mvc/issues/3750#issuecomment-171765303