Включая файлы содержимого в.csproj, находящиеся вне конуса проекта

У меня есть проект на C#, скажем, MyProject.csproj, расположенный в "C:\Projects\MyProject\". У меня также есть файлы, которые я хочу скопировать в выходной каталог этого проекта. Но файлы находятся по адресу "C:\MyContentFiles\", т.е. они НЕ находятся внутри конуса проекта. В этом каталоге также есть подкаталоги. Содержимое каталога не управляется. Следовательно, я должен включить все, что находится под ним.

Когда я включаю их как "Контент" в проект, они копируются, но структура каталогов теряется. Я сделал что-то вроде этого:

<Content Include="..\..\MyContentFiles\**">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Как мне рекурсивно скопировать эти файлы / каталоги в выходной каталог проекта с сохранением структуры каталогов?

7 ответов

Решение

Вам необходимо добавить файл в виде ссылки:

  1. Щелкните правой кнопкой мыши по проекту в VS.
  2. Добавить -> Существующий элемент...
  3. Найдите файл.
  4. Выберите его и.
  5. Добавить как ссылку (выпадающий в кнопке Добавить в диалоговом окне).
  6. Откройте свойства файла и установите "Копировать в выходной каталог" в "Копировать всегда".

НО Вы не можете сделать это для дерева каталогов.
Вместо этого вам нужно написать задачу пост-сборки для этого. Это образец, на который вы будете смотреть.

Я считаю, что @Dmytrii понимает это правильно с одной стороны - вы хотите использовать функцию "ссылка".

Тем не менее, он только частично прав, когда говорит, что вы не можете ссылаться на дерево каталогов. Хотя это действительно так при попытке добавить ссылки с помощью графического интерфейса Visual Studio, MSBuild поддерживает это.

Если вы хотите сохранить структуру каталогов, просто добавьте %(RecursiveDir) тег к вашему <link> узел:

<Content Include="..\..\MyContentFiles\**\*.*">
  <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

На странице MSBuild Metadata "Известный элемент" более подробно рассматриваются метаданные, к которым вы можете получить доступ.

Ответ Mandark добавляет файлы содержимого непосредственно в решение, и они отображаются в обозревателе решений. Всякий раз, когда файл добавляется или удаляется в исходном каталоге, Visual Studio не подхватывает его автоматически. Кроме того, всякий раз, когда файл удаляется или добавляется в обозревателе решений, файл проекта изменяется, и все файлы включаются отдельно, а не просто включают папку.

Чтобы предотвратить это, вы можете использовать тот же трюк, но поместить его в отдельный файл проекта и затем импортировать его.

Файл проекта (например, include.proj) выглядит следующим образом:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Content Include="..\..\MyContentFiles\**">
  <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

В вашем собственном файле проекта добавьте следующую строку

<Import Project="include.proj" />

Visual Studio не будет связываться с этим файлом, а просто добавляет файлы в качестве содержимого во время сборки. Изменения в исходном каталоге всегда включены. Файлы не будут отображаться в вашем обозревателе решений, но будут включены в выходной каталог.

Подхватил этот трюк здесь: http://blogs.msdn.com/b/shawnhar/archive/2007/06/06/wildcard-content-using-msbuild.aspx

Следующее, которое вы бы добавили в конец вашего файла проекта, скопирует ваши файлы содержимого, сохраняя структуру каталога в событии после сборки, в целевой каталог $(TargetDirectory) вашей сборки (обычно $(MSBuildProjectDirectory)\bin\Debug).

<ItemGroup>
    <ExtraContent Include="$(MSBuildProjectDirectory)\..\..\MyContentFiles\**" />
</ItemGroup>

<Target Name="AfterBuild">
    <Copy 
        SourceFiles="@(ExtraContent)" 
        DestinationFiles="@(ExtraContent->'$(TargetDir)\%(RecursiveDir)%(Filename)%(Extension)')" 
        SkipUnchangedFiles="true" />
</Target>

Если эти файлы нужно поместить в каталог с именем MyContentFiles, вы можете добавить это перед копией:

<MakeDir Directories="$(TargetDir)\MyContentFiles" Condition=" !Exists('$(TargetDir\MyContentFiles') " />

и изменить

<Copy 
            SourceFiles="@(ExtraContent)" 
            DestinationFiles="@(ExtraContent->'$(TargetDir)\%(RecursiveDir)%(Filename)%(Extension)')" 
            SkipUnchangedFiles="true" />

к

<Copy 
            SourceFiles="@(ExtraContent)" 
            DestinationFiles="@(ExtraContent->'$(TargetDir)\MyContentFiles\%(RecursiveDir)%(Filename)%(Extension)')" 
            SkipUnchangedFiles="true" />

Чтобы включить файлы в папку для проекта.NET Core,

<ItemGroup>
  <None Update="..\..\MyContentFiles\**\*.*">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

И свойство элементов "Копировать в выходной каталог" будет "Копировать, если новее":
Скопируйте, если новее

Я думаю

<Content Include="..\..\MyContentFiles\**\*.*">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Достаточно, так как вы хотите, чтобы все в этой папке и подпапках

Чтобы игнорировать файл в проекте.Net Core:

<ItemGroup>
 <Content Include="appsettings.local.json">
   <CopyToOutputDirectory Condition="Exists('appsettings.local.json')">PreserveNewest</CopyToOutputDirectory>
 </Content>
</ItemGroup>
Другие вопросы по тегам