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
enStartup.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