ASP.NET MVC 6: просмотр компонентов в отдельной сборке
Я хотел бы определить компоненты представления (которые являются новыми в ASP.NET MVC 6) в отдельной сборке из проекта веб-запуска MVC 6, чтобы я мог повторно использовать их в нескольких веб-проектах. Примерное решение может выглядеть так:
- BookStore.Components(общие компоненты представления для дома)
- BookStore.Web1 (ссылки BookStore.Components)
- BookStore.Web2 (ссылки BookStore.Components)
Я создал новую библиотеку классов (Package) и создал компонент вида внутри. Я также создал представление в соответствии с соглашением вложенных папок. Мой проект BookStore.Components
выглядит следующим образом:
![введите описание изображения здесь]()
Когда я пытаюсь вызвать этот компонент вида из моего веб-проекта:
@Component.Invoke("BookOfTheMonth")
... Я получаю 500 ошибок с пустым содержимым. Кажется, что класс ViewComponent обнаружен, но вид бритвы для компонента не является.
Я также попытался расширить DefaultViewComponentDescriptorProvider
, чтобы можно было просмотреть компоненты вида из сборки BookStore.Components
:
Определено AssemblyProvider
public class AssemblyProvider : IAssemblyProvider
{
public IEnumerable<Assembly> CandidateAssemblies
{
get
{
yield return typeof(AssemblyProvider).Assembly;
yield return typeof(BookStore.Components.BookOfTheMonthViewComponent).Assembly;
}
}
}
Registered AssemblyProvider с использованием Autofac
builder.RegisterType<AssemblyProvider>()
.AsImplementedInterfaces();
builder.RegisterType<DefaultViewComponentDescriptorProvider>()
.AsImplementedInterfaces();
Я не уверен, нужна ли регистрация DefaultViewComponentDescriptorProvider
выше или нет, поэтому я попытался с ней и без нее, но я все еще получаю ошибку 500 на странице, на которой вызывается компонент вида.
Как я могу вызвать компонент вида, который живет в отдельной сборке из веб-проекта MVC6?
Ответы
Ответ 1
Обновление 2017-03-09
В Visual Studio 2017 немного изменилось, используя MS Build. К счастью, это намного проще. Здесь, как заставить это работать:
Во внешней сборке добавьте это в файл csproj:
<ItemGroup>
<EmbeddedResource Include="Views/**/*.cshtml" />
</ItemGroup>
В главном веб-проекте добавьте этот пакет NuGet:
Microsoft.Extensions.FileProviders.Embedded
Затем в Startup добавьте внешнюю сборку в список поставщиков файлов:
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Add(new EmbeddedFileProvider(
typeof(SampleClassInAssembly).Assembly
# Prior to .Net Standard 2.0
# typeof(SampleClassInAssembly).GetTypeInfo().Assembly
));
});
Теперь я оставлю исходный ответ ниже, если люди все еще пытаются заставить это работать со старыми версиями .Net Core и project.json
.
=============================================== =================
Вот шаги, чтобы сделать эту работу.
- Убедитесь, что структура вашего представления в сборке компонентов совпадает с вашим веб-проектом. Обратите внимание, что на скриншоте была ошибка, которую я опубликовал вместе с моим вопросом.
-
Зарегистрируйте CompositeFileProvider
в Startup.cs
веб-проекта:
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProvider = new CompositeFileProvider(
new EmbeddedFileProvider(
typeof(BookOfTheMonthViewComponent).GetTypeInfo().Assembly,
"BookStore.Components"
),
options.FileProvider
);
});
Оба CompositeFileProvider
и EmbeddedFileProvider
являются новыми, поэтому вам нужно получить их из фида aspnetvnext
NuGet. Я сделал это, добавив этот источник:
![введите описание изображения здесь]()
Добавьте зависимости в project.json
:
"Microsoft.AspNet.FileProviders.Composite": "1.0.0-*",
"Microsoft.AspNet.FileProviders.Embedded": "1.0.0-*",
Наконец, добавьте это в project.json
сборки Components
:
"resource": "Views/**"
Этого должно быть достаточно, чтобы заставить это работать.
Вот рабочая демонстрация:
https://github.com/johnnyoshika/mvc6-view-components/tree/master
Этот ответ был сформулирован из этой дискуссии здесь: https://github.com/aspnet/Mvc/issues/3750
Обновление 2016-01-15
В настоящее время существует одна болезненная проблема с компонентами внешнего вида. Любые изменения, внесенные в файл cshtml представления, не будут автоматически перекомпилированы. Даже принудительная Visual Studio чистая и перестроенная не делает этого. Вам нужно изменить файл .cs в сборке компонентов, чтобы вызвать перекомпиляцию вида, но похоже, что это будет исправлено в будущем. Причина этой проблемы объясняется здесь: https://github.com/aspnet/Mvc/issues/3750#issuecomment-171765303
Ответ 2
Я провел некоторое исследование в Github и обнаружил, что PhysicalFileProvider
(ссылка) IFileInfo GetFileInfo(string subpath)
метод используется механизмом Razor (ссылка) для получения реальных файлов для компиляции.
Текущая реализация этого метода
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;
}
Мы можем видеть здесь, что абсолютные пути или разрешенные методы и метод GetFullPath
объединяют путь с Root
, который является основным корнем вашего веб-приложения.
Поэтому я предполагаю, что u не может открыть ViewComponent
из другой папки, чем текущий.