Adding local dependenies in C# without absolute path - c#

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.

Related

Include NuGet Utility Package when building csproj

I need to use a NuGet package containing a utility for my project. It contains several binaries (EXEs and DLLs).
I've added it to my project successfully but I suspect the nupkg isn't formed correctly because I cannot use any of its DLLs or EXEs in my project without manually pointing to the package in my local NuGet cache. When compiling, none of its resources are added to the output (I assume this is because nothing is referenced in my code).
I'd like to create a wrapper project to call the binaries but I'd also like other project devs to be able to compile the solution without adjusting directory variables. Ideally, I could configure the csproj to pull in the bits directly from the local package cache. I think this would be possible by setting the Generate Path Property value to Yes in Visual Studio, but the variable cannot be found when I attempt to use an <Include/> statement in the csproj file.
Is what I'm asking possible? Namely, reference the NuGet package bits within my csproj to ensure the binaries are dropped in the compilation output? Can I do this with the Path Property, or is there something else I can do without directly committing the package's binaries into my project?
(I realize I need to work with the developer to fix whatever issue they have with their package, but I have no direct influence at the moment so this is the best I can do at the moment).
I figured this out, mostly due to misunderstanding how some of the different tags and attributes are meant to be used.
To achieve the desired effect, I did the following:
<ItemGroup>
<Content Include="$(Pkg{PackageId})\**">
<Link>{NameOfSolutionDirectory}\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
Where {PackageId} is the name of the NuGet package (this step requires setting 'Generate Path Property' to 'Yes' in the package properties via Solution Explorer), and {NameOfSolutionDirectory} is the name of a folder within the solution I'd like to use for containing those bits, if you're as concerned about keeping the project as organized as I am. The {} should be excluded when replacing these values.
If you want to scope to a specific directory within the package contents, do it within the Include attribute. The ** is necessary if you want to include all files within that directory, or else you can scope by extension or whatever additional pattern you'd like.

Add props file properties/variables to csproj hint path

I have added .props file and included it in .nuspec file.
In my .props file i have added new properties/elements in PropertyGroup.
After installing my nuget package, .props file is importing successfully in .csproj file. But how can I include/add props files properties/variables in HintPath (not manually).
Using HintPath & .nuspec for your nugets is an indicator of legacy approach, consider PackageReference & SDK style csproj instead, there is no hint path at all, they are all automatic.
Legacy of Hint Path
Hint path usually used as an item metadata for Reference items for nuget-installed assemblies. Legacy project format been developed when nuget was an external tool (now it is part of .Net SDK). At that time the approach was to have packages.config (you no longer need it) with the list of nugets installed to the project, and while package been installed it have to appear to the rest of .net tools as a regular sideloaded set of assemblies, this is why DLL files are been restored to "packages" folder (this no longer happens), and Reference items are been added to the project with a HintPath having relative path to this "packages" folder. This includes whole graph of references, so if PackageA uses PackageB then both are deployed to "packages" folder and both produces Reference item on all libraries from every package. This way legacy tools used to bring package management to existing ecosystem. For a new PacakgeReference approach you only have to specify top-level package, all transient references will be processed automatically on a restore & build time.
Custom Targets
Since you are mentioning you have props file in your nuspec, let me clarify how now it can be achievend in SDK style csproj in a package that your are publishing (there is no nuspec, all nuspec data is now inside csproj):
<ItemGroup>
<None Include="your_custom.props_or_targets">
<Pack>true</Pack>
<PackagePath>build</PackagePath>
</None>
</ItemGroup>
So, destination path is specified in PackagePath item metadata and it should be build to denote that this is auto-installable targets.
Now in legacy approach when installing package like this, a consumer project automatically have "Import" node to explicitly bring this targets to the project. Now in new SDK approach, if you import package like this with PackageReference, you will not find Import node - it is actually implicitly assumed, so if package under question have targets & props in build folder - all of them will be automatically imported in a build time (not in design time).
PackageReference
<ItemGroup>
<PackageReference Include="YourPackage" Version="3.6.0" />
</ItemGroup>
So if you consume project like this, this is enough to consume Properties that you bring with your custom props inside this package. Feel free to use them as usual an hopefully you no longer need HintPath at all.

Custom Nuget package shows missing dependency error

I have two class libraries in a single solution (.NET Core). One of them (cl1) is a main library and it depends on another library (cl2). I have added a .nuspec file with the required metadata only (no dependencies, no files) for the cl1 project in the project folder (same location of .csproj file) and I have set GeneratePackageOnBuild propery to true.
Whenever I am building the class library (cl1), the .nupkg is created automatically in the debug/release folder.
When I check the generated .nupkg file, I am see two strange things:
The generated .nuspec file is different than what I have added in the project folder
cl2 is mentioned as a dependency in the newely generated .nuspec file, but the DLL for cl2 is not included in the lib folder of the .nupkg. So, whenever I consume this package in another solution, I am getting the error No packages exist with this id in source(s) for the cl2.
I have surfed in internet, but was not able to find a proper solution for the above error.
And I have added a .nuspec file [...] in the project folder(same location of .csproj file)
You have to specify the path to your own NuSpec file in the .csproj using the NuspecFile tag, otherwise it will be ignored and the package will be created with the metadata from the .csproj file instead, see reference. You need to use either a relative or an absolute path to the file, for example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<NuspecFile>cl1.nuspec</NuspecFile>
</PropertyGroup>
</Project>
The generated .nuspec file is different than what I have added in the project folder
As already stated, your NuSpec file is probably not included. However, even if it is, there can be differences, because some information, e.g. source file locations are unnecessary and the target locations are in most cases given by the internal package file structure itself, so it is not there because it is redundant.
cl2 is mentioned as a dependency in the newely generated .nuspec file, but the dll for the cl2 is not included in the lib folder of the .nupkg. So, whenever I consume this nupkg in other solution, I am getting error " No packages exist with this id in source(s)" for the cl2.
Dependencies are meant for packages. So when NuGet restores the package it searches for other packages that this package depends on, here cl2, but there is none, hence the error. When packing a project, referenced projects are not included in the package. That is an open issue and there are workarounds that you can try.
The most reliable, but inconvenient solutions are to avoid the issue at all.
Only use a single project, everything will be included in the package
Pack each project on its own and use the generated package instead of the referenced project

How can I skip building all referenced projects every time when I build my source project

I have Xamarin.Forms project which has many dependencies of other projects. When I am doing any modification in business logic every time I need to execute/build my project.
Due to many other projects dependencies it is taking huge time to complete built(10-15 mins) and showing build o/p.
Done building project "Project1.csproj".
Done building project "Project2.csproj".
Done building project "Project3.csproj".
Done building project "Project4.csproj".
Done building project "Project5.csproj".....
If I remove above projects dependencies from source project's .csproj file
<ItemGroup>
<ProjectReference Include="..\..\..\Components\Business\Project1\Project1.csproj" />
<ProjectReference Include="..\..\..\Components\BusinessUI\Project2\Project2.csproj" />
<ProjectReference Include="..\..\..\Components\Core\Project3\Project3.csproj" />
<ProjectReference Include="..\..\..\Components\CoreUI\Project4\Project4.csproj" />
<ProjectReference Include="..\..\..\Components\Shared\Project4\Project5.csproj" />
</ItemGroup>
I am getting so many compile time errors
are you missing an assembly reference?
How can I build my source project without building all dependent projects every time so that I can save my time?
How have you referenced those projects? Are the referenced as project files, or their compiled .dll/.exe form?
It sounds like those were added as raw Projects/sourcecode. And in that case, of course they have to be compiled. It would be like trying to only compile half the sourcecode otherwise.
In .NET you can use any .NET .dll or .NET .exe as assembly reference. With the .NET internal stuff you always use .dll's. But .exe works too. You would have to deploy those .dll's/.exe's with the code, but something like copying it to the output directory as a build action should work. Indeed I never even heard of referencing the raw project, but it might be there somewhere.
Internally the .NET .exe is almost identically to a .NET .dll. Some bootstrap code and a entry point more, maybe some fringe stuff like COM interop less. It is one of those parts where they drew heavy inspiration from Java bytecode - and then made it better. So you can just add them as a reference.

Using the same assembly references in csproj with different folder structures

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.

Categories