Incluir archivos de contenido en .csproj que están fuera del cono del proyecto

85

Tengo un proyecto de C # que dice MyProject.csproj ubicado en "C: \ Projects \ MyProject \". También tengo archivos que quiero copiar en el directorio de salida de este proyecto. Pero, los archivos están en la ubicación "C: \ MyContentFiles \", es decir, NO están dentro del cono del proyecto. Este directorio también tiene subdirectorios. El contenido del directorio no se gestiona. Por lo tanto, tengo que incluir todo lo que hay debajo.

Cuando los incluyo como 'Contenido' en el proyecto, se copian, pero se pierde la estructura del directorio. Hice algo como esto: -

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

¿Cómo copio estos archivos / directorios de forma recursiva en el directorio de salida del proyecto con la estructura de directorios preservada?

Poulo
fuente

Respuestas:

55

Necesita agregar el archivo como un enlace:

  1. Haga clic derecho en el proyecto en VS.
  2. Agregar -> Elemento existente ...
  3. Encuentra el archivo.
  4. Selecciónelo y.
  5. Agregar como enlace (desplegable en el botón Agregar en el cuadro de diálogo).
  6. Abra las propiedades del archivo y configure "Copiar al directorio de salida" en "Copiar siempre".

PERO no puede hacerlo para el árbol de directorios.
En su lugar, debe escribir una tarea posterior a la compilación para eso. Esta es una muestra que te dejará boquiabierto.

Dmytrii Nagirniak
fuente
8
Como se indica en esta respuesta , "No puede hacerlo para el árbol de directorios". declaración no es cierta.
Mandark
160

Creo que @Dmytrii lo hace bien por un lado: desea utilizar la función de "enlace".

Sin embargo, solo tiene razón en parte cuando dice que no se puede vincular a un árbol de directorios. Si bien esto es cierto cuando se intenta agregar los enlaces utilizando la GUI de Visual Studio, MSBuild lo admite.

Si desea conservar la estructura del directorio, simplemente agregue la %(RecursiveDir)etiqueta a su <link>nodo:

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

La página Metadatos de elementos conocidos de MSBuild detalla los metadatos a los que puede acceder.

Mandark
fuente
1
1) ¿Qué sucede si se elimina un archivo de la tienda de activos compartidos? No parece que se eliminará de la solución Dir. 2) ¿Hay alguna forma de que Visual Studio elimine estos archivos de $ (SolutionDir) después de que se complete la ejecución? Dejarlos allí podría confundir a otros desarrolladores.
Adam
@Adam Vea mi respuesta a continuación
Robin van der Knaap
8
Al estar estrellado en el desierto con poca comida, agua y tener una gran necesidad de compañía, encuentro útil esta respuesta. Siguiendo estas instrucciones, pude construir una IA completamente funcional que ahora ha reemplazado todas las necesidades humanas que una vez me obsesionaron en el desierto aquí. ¡Gracias Mandark!
profundización
1
@Vijay vea mi edición, necesita <Content Include="..\..\MyContentFiles\**\*.*">- tenga \*.*en cuenta el al final de la ruta.
CAD bloke
2
No funciona para un proyecto de .NET Core en VS2017 (15.8.6).
zwcloud
29

La respuesta de Mandark agrega los archivos de contenido directamente a la solución y aparecerán en el explorador de soluciones. Siempre que se agrega o elimina un archivo en el directorio original, Visual Studio no lo detecta automáticamente. Además, cada vez que se elimina o agrega un archivo en el explorador de soluciones, el archivo del proyecto se modifica y todos los archivos se incluyen por separado, en lugar de solo incluir la carpeta.

Para evitar esto, puede utilizar el mismo truco, pero ponerlo en un archivo de proyecto separado y luego importarlo.

El archivo del proyecto (por ejemplo, include.proj) tiene este aspecto:

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

En su propio archivo de proyecto, agregue la siguiente línea

<Import Project="include.proj" />

Visual Studio no interferirá con este archivo y solo agregará archivos como contenido durante una compilación. Los cambios en el directorio original siempre se incluyen. Los archivos no aparecerán en su explorador de soluciones, pero se incluirán en el directorio de salida.

Aprendí este truco aquí: http://blogs.msdn.com/b/shawnhar/archive/2007/06/06/wildcard-content-using-msbuild.aspx

Robin van der Knaap
fuente
1
Por alguna razón, esto no pareció funcionar para mí usando un archivo .proj, pero pude hacerlo funcionar con un archivo .csproj.
StriplingWarrior
6
Puede agregar <Visible> true </Visible> al elemento Content para que los elementos aparezcan en el Explorador de soluciones.
Michael Baker
El mío estaba bien con solo un .proj
Jason Dufair
Esto no funcionó para mí en absoluto y supongo que se debe a que los elementos que se vincularán se generaron en un evento previo a la compilación.
Paul K
2
No funciona para un proyecto de .NET Core en VS2017 (15.8.6).
zwcloud
10

Lo siguiente, que agregaría al final de su archivo de proyecto, copiará sus archivos de contenido manteniendo la estructura del directorio en un evento posterior a la compilación al directorio $(TargetDirectory)de destino de su compilación (normalmente $(MSBuildProjectDirectory)\bin\Debug).

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

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

Si estos archivos necesitan ir a un directorio llamado MyContentFiles, puede agregar esto antes de la copia:

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

y cambio

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

A

<Copy 
            SourceFiles="@(ExtraContent)" 
            DestinationFiles="@(ExtraContent->'$(TargetDir)\MyContentFiles\%(RecursiveDir)%(Filename)%(Extension)')" 
            SkipUnchangedFiles="true" />
Todd
fuente
1
Sus transformaciones tienen un tipo-o: @ (ExtraContent) -> '...') debería ser @ (ExtraContext -> '...'). De lo contrario, ¡buena respuesta!
alexdej
@Todd 1) ¿Qué sucede si se elimina un archivo de la tienda MyContentFiles? No parece que se eliminará de la solución Dir. 2) ¿Hay alguna forma de que Visual Studio elimine estos archivos de $ (SolutionDir) después de que se complete la ejecución? Dejarlos allí podría confundir a otros desarrolladores.
Adam
3

Yo creo que

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

Es suficiente, ya que quieres todo en esa carpeta y subcarpetas

Menzi
fuente
2

Para incluir archivos en una carpeta para un proyecto .NET Core,

<!--Just files-->
<ItemGroup>
  <None Update="..\..\MyContentFiles\**\*.*">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>
<!--Content files-->
<ItemGroup>
  <Content Include="..\..\MyContentFiles\**\*.*" Link="MyContentFiles\%(RecursiveDir)%(Filename)%(Extension)">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

Y la propiedad de los elementos "Copiar al directorio de salida" será "Copiar si es más reciente":
Copiar si es más reciente

zwcloud
fuente
-1

Para ignorar un archivo en un proyecto .Net Core:

<ItemGroup>
 <Content Include="appsettings.local.json">
   <CopyToOutputDirectory Condition="Exists('appsettings.local.json')">PreserveNewest</CopyToOutputDirectory>
 </Content>
</ItemGroup>
Skorunka František
fuente