Подтвердить что ты не робот

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?

4b9b3361

Ответ 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 из другой папки, чем текущий.