Using the same assembly references in csproj with different folder structures - c#

I have a C# project, MyProject.csproj that sits in a solution with the following folder structure and references Dependency.dll:
Libs
Dependency.dll
Projects
MyProject
MyProject.csproj
Thus the reference to Dependency.dll in MyProject.csproj has a HintPath of something like this:
..\..\Libs\Dependency.dll
Now I'd like to use MyProject in a different solution in a different project structure, without modifications, as source. This is because MyProject sits in its own source control repository and I'm using it in different solutions as Mercurial subrepositories/Git submodules. (The problem might be solved on the source control level...) Such a diffreent solution would look like this:
Libs
Dependency.dll
MyProject
MyProject.csproj
Note that the MyProject folder is now on the same level as the Libs folder. Thus the original HintPath is now invalid (since it should be ..\Libs\Dependency.dll) and I get build errors.
Is there a way to fix this but keep the same csproj across the different solutions?
I found the following possible solutions which are great but require the modification of the csproj. This is mostly possible in my case but sometimes there are external components where I can't request such modifications, so I'd look for some solution-level override if possible.
Conditional HintPath based on file existence check: .csproj multiple hint paths for an assembly This might work as for the majority of cases the solution structure is well-known here.
Specifying multiple assembly search locations: https://stackoverflow.com/a/15816779/220230
Thank you.

For now, I solved the issue using the technique outlined in this blog post.
<ItemGroup>
<LibReferenceSearchPathFiles Include="..\..\Libs\**\*.dll">
<InProject>false</InProject>
</LibReferenceSearchPathFiles>
</ItemGroup>
<Target Name="BeforeResolveReferences">
<RemoveDuplicates Inputs="#(LibReferenceSearchPathFiles->'%(RootDir)%(Directory)')">
<Output TaskParameter="Filtered" ItemName="LibReferenceSearchPath" />
</RemoveDuplicates>
<CreateProperty Value="#(LibReferenceSearchPath);$(AssemblySearchPaths)">
<Output TaskParameter="Value" PropertyName="AssemblySearchPaths" />
</CreateProperty>
</Target>
This enables dlls from subfolders of Libs to be loaded. If all the dlls would be in the root of the Libs folder, then the first wildcard can be removed from the Include value.

Related

Adding local dependenies in C# without absolute path

My current specialization is TypeScript/NodeJS, but I know what each popular language has the package management including referencing to local dependency.
What the equivalent of package.json for the ProjectName.csproj of a C# project?
{
dependencies: {
"local-one": "../foo/bar"
}
}
For this question, it is important to not use the absolute paths because these paths will become unactual once other team members will clone the repository on his local computes while relative paths could be actual in mono repositories.
In a .csproj file, you have two common ways of specifying dependencies. <ProjectReference ...> elements reference other C# projects (relative paths are fine). <PackageReference ...> elements reference nuget packages (i.e. external libraries by name only - they will be stored in the global assembly cache (GAC) and no path needs to be specified.
If you are working with .NET Framework projects, external libraries may be specified by a combination of <Reference ...> elements in the .csproj file (again relative paths would be expected) and a packages.config file specifying the nuget packages.
Relative paths will be used automatically by most IDEs you might be using. Generally you shouldn't need to edit the project file directly too often.
I'd recommend following some simple C# tutorials and examining the .csproj files you end up creating to try and understand them.
Best of luck working with C#.
EDIT
As requested, an example of a <ProjectReference ...>:
From a .NET Framework/old-style project:
<ProjectReference Include="..\..\path\to\MySecondProject.csproj">
<Project>{1E14D605-3FD2-4AA7-8578-78944A8BB348}</Project>
<Name>MySecondProject</Name>
</ProjectReference>
The Guid is the identifier for the other project (you can find it in the other .csproj file).
From an SDK-style project:
<ProjectReference Include="..\..\path\to\MySecondProject.csproj">
Again, I will recommend that you use an IDE to modify your project, especially if you are working on .NET Framework projects.
Please see https://learn.microsoft.com/en-us/visualstudio/get-started/tutorial-projects-solutions?view=vs-2022#add-a-project-reference.

exclude certain projects from using Directory.Build.props

i have a new visual studio solution that has around 350 projects. it takes visual studio a lot of time to compile the .sln file so i implemented Directory.Build.props to avoid copying of references that are not needed to copy to the local directory so the build can be made quicker. below is the code that im using inside the Directory.Build.props file under the root folder.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
</Project>
since i placed Directory.Build.props under root folder it is being applied for all projects.
Question::
how can i exclude few projects from applying Directory.Build.props so that the references can be copied to the local.
in short i want the Directory.Build.props to be applied to only 300 projects under the solution file remaining 50 projects need to be excluded from this
how/where can i write a condition in the above code that will exclude certain projects being affected by this code
For others dealing with the same problem, there is another trick that can be used to exclude certain project from using the Directory.Build.props file found at root level.
If you add a dummy Directory.Build.props file in the project you want to exclude, then the Directory.Build.props from the root will not be used. This is because MSBuild walks the directory structure upwards from the location of your project, until it locates the first Directory.Build.props. That will be used. This behavior is documented on the Customize your build page under Search scope at the Microsoft docs.
Sample of the dummy Directory.Build.props:
<Project>
<!-- Only here so that the default Directory.Build.props will not be used. -->
</Project>
I found this to be a convenient way to solve this issue. Especially when dealing with only a few projects that need to be excluded.
I had to work around this in a bit of a hacky way.
In my example, there was a custom analyzer project I wrote that I did not want included in another set of projects. I ended up writing something like this in my Directory.Build.props:
<Project>
...
<Choose>
<When Condition="$(MSBuildProjectName)!='Analyzer' AND ...">
<ItemGroup>
<ProjectReference Include="..\Analyzer\Analyzer.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Analyzer</OutputItemType>
</ProjectReference>
</ItemGroup>
</When>
<Otherwise>
...
</Otherwise>
</Choose>
...
</Project>
Where I filled in ... with the projects I wanted it to skip.
I understand this may not be the exact answer you were looking for, but I did a ton of research and was also unable to find any way to do it the way you described. The stuff I have posted was the only way I was able to achieve the ability to exclude certain things from being applied to specific projects by filtering via name. I know that this is hacky and sucks, but it's the only thing that was able to work for me.
Also note that <Otherwise></Otherwise> may be turned into <Otherwise /> possibly, and may even be optional altogether. I left it there so that you could place stuff inside of it if needed.

.NET Core - Nuget Pack of UniTest Project shows warning "The assembly is not inside the 'lib' folder"

I'm trying to pack a UnitTest project as a Nuget package and I always get the following warning(s) if I build my project:
The assembly
'content\SpecFlow.MSDependencyInjection.SpecFlowPlugin.dll' is not
inside the 'lib' folder and hence it won't be added as a reference
when the package is installed into a project. Move it into the 'lib'
folder if it needs to be referenced.
My csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.1.0</Version>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="SpecFlow" Version="3.0.188" />
<PackageReference Include="SpecFlow.MSDependencyInjection.SpecFlowPlugin" Version="1.0.2" />
<PackageReference Include="SpecFlow.MsTest" Version="3.0.188" />
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.0.188" />
</ItemGroup>
</Project>
The error still appears if I copy the files into the lib folder of the Nuget package. I'm at a loss what I need to change for this warning to disappear. To be frank I'm not even sure why it appears in the first place because I have a different project that works fine without this error.
Update 1:
After the detailed answer from #zivkan I changed my project structure so it is not a UnitTest project anymore.
Sadly the errors still appear if my project is a class library...
Screenshot with all Nuget-Packages that I need for my project to work
If I only add my own Nuget-Package that consists of two dependencies (Microsoft.Extensions.DependencyInjection and SpecFlow) it still produces this error but the two dependencies in this Nuget-Package don't. To me this seems to be a problem with the Nuget-Packages...
I'm not 100% sure, but my guess is that since with SDK style csproj files, when you build, only your assembly's dll is normally written to the output directory. When you run a non-test netcoreapp, the dotnet cli looks at what project references and nuget references you have, and configures the assembly loader to load from their "original" locations, rather than having all the assemblies copied to your app's bin folder. Perhaps the unit test framework doesn't support loading assemblies in this way and creates Content items out of each dll, which tells the build step to copy the content (in this case dlls) into the output directory (bin\$(Configuration)\$(TargetFramework)). Therefore, when you run unit tests, the unit test framework has all the required assemblies in the single directory, whereas that's normally not true for non-test projects.
Next you need to understand that when NuGet packs a project, it looks for MSBuild items of the type Content, and puts copies of them in the nupkg's content and contentFiles directories. Due to how NuGet works, only dlls in the lib\ or ref\ directories within the nupkg are given to the compiler, therefore any dlls you have a content directory will not be passed to the compiler, so your project that references this nupkg cannot use classes in those dlls. This is not how people usually intend to use NuGet packages and therefore NuGet generates a warning.
So, I believe the reason you're getting this warning is because you're packing a project type that is not intended to be packable. The project type does some uncommon things in order to work, which triggers NuGet warnings because typically when this uncommon thing is done, it's a mistake.
I believe this to be a case of a XY problem. I assume you're packing a unit test project because you want to share some utility code useful for tests, maybe some mocks or object initialisation code. In this case, I recommend you create a new classlib project, put your shared code in there, leaving all your test cases in your netcoreapp test project, even if it's nothing more than a single method call into the classlib. This way you can pack and share the classlib without warnings. Packing a unit test seems unusual and it would be interesting to discuss why you want to do this, what problem do you intend to solve and if packing a test is really the best way to achieve it. Unfortunately Stack Overflow isn't a good place to have discussions and is often actively discouraged.
perhaps you have missed a file, please follow this link for full details : https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package#Package_Conventions

Copy all of the required dlls to folder

I have a MonoAndroid10 project and it has a lot of dependencies(NuGet packages too). I would like to be able to copy all of the DLL dependencies to the output folder.
Normally in a .Net Standard 2.0 project the following
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
would make that possible. But in the MonoAndroid project, nothing happens.
If what I posted above doesn't work for a MonoAndroid project, how could I copy everything that I need in some folder, preferably in a post-build action?
I'm trying to do this because after copying all of the required DLLs in one folder I can merge them together with ILRepack.
I've come across this today, solved by adding
<CopyNuGetImplementations>true</CopyNuGetImplementations>
to android project .csproj file (under the desired PropertyGroup section).
Note: there is no need to set CopyLocalLockFileAssemblies to ture neither in android project nor in any of the dependencies.

How to determine behavior of binaries in NuGet package

There is this big solution I'm working on, where I turned a lot of the projects into NuGet packages. The packages were created via a .nuproj file in a separate solution in VS.
Everything works fine, except for the following:
At bootstrap I load some catalogs for MEF to be able to import them, which worked perfectly when I worked with the original projects, but now the needed DLLs (which come from the a package) don't make it to the bin\Debug\Modules folder.
Is there a way to make NuGet copy its content to the Modules folder? (and not to the root path)
I tried using the different kinds of sub-folders inside the package with no success.
I found that the best solution for this matter is the following:
Take the files that need to be loaded and put them on the content folder. This can be done simply:
<ItemGroup>
<Content Include=" {here go the needed files} " />
</ItemGroup>
The content folder just holds the files, but it does not copy them to the output folder on the client project. In order to copy them to the desired output, a .targets file can be used, just like the following:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyToOutput" AfterTargets="Build">
<ItemGroup>
<FilesToCopy Include="$(MSBuildThisFileDirectory)..\content\**\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(FilesToCopy)"
DestinationFiles="#(FilesToCopy->'$(OutDir)/%(RecursiveDir)%(FileName)%(Extension)')"/>
</Target>
</Project>
Keep in mind that the targets file name and the ID of the NuGet have to be equal for the targets file to be added to the project.
You should be able to use a target of content/Modules. Anything in the content directory is copied in to the bin directory on build.
If you were trying to use the special "convention based" folders, like lib/net45, those are directories that cause Visual Studio to automatically create an assembly reference when the package is installed. You shouldn't use those for regular content files.
See the documentation for more details.

Categories