DotNet Core Create Proto message common library - .net-core

The use case is pretty simple:
I have two projects (ProtoProvider & ProtoConsumer).
ProtoProvider has the proto file (to_import.proto) and the message I want to use on the ProtoConsumer.
ProtoConsumer has a referrence on ProtoProvider and attempts to use the ProvidedMessage in the imported.proto.
I cannot use the the message because I get "File not found." on the impoort and "ProvidedMessage is not defined" on the compiler for the imported.proto.
EDIT2>>>
To clarify I want to create a message like google's google.protobuf.Timestamp and distribute it to another project or projects without the other projects while having the project/projects (consumer) getting the message from the dll. The whole premise of the question is how to use the message defined in to_import.proto in another project through the dll (think of it as a common.grpc lib).
EDIT1>>>
The error messages I'm getting
Is there a solution that allows the importing of the proto files from a project reference?
I'm looking for something like google's solution for the well-known-types
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
source: https://developers.google.com/protocol-buffers/docs/csharptutorial#where-to-find-the-example-code.
(Copying the proto files or moving the files is not an elegant solution).
Below I'll provide the .csproj files
ProtoProvider:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="to_import.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.27.0" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="to_import.proto" GrpcServices="None" />
</ItemGroup>
</Project>
ProtoConsumer:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Protos\imported.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.27.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ProtoProvider\ProtoProvider.csproj" />
</ItemGroup>
</Project>
Edit>>3
Some context : The purpose of this question is to help me understand a clearer way to implement decimal once (as specified here) and reuse them in each project.

AFAIK, compiling proto files requires all files (including imports) to exist on disk.
The way this works for the well known types is that they are included in Grpc.Tools nuget package.
So its not possible to ship a class library that accomplishes what you want.
Discussion on a related grpc-dotnet issue suggests using a combination of:
Building a nuget package containing your common proto and associated class library
In the consuming code's package reference, use GeneratePathProperty to generate a variable holding the path to the nuget package content
In the consuming code's Protobuf Include, use AdditionalImportDirs to include the common proto from the nuget package.

Related

.target file is not applied to the .csproj file when nuget package is released

I'm making a nuget package which only includes references to a bunch of analyzers and a ruleset file.
I am struggling with getting it to add the <CodeAnalyzersRuleSet> tag to the .csproj file during package install.
After searching for a few hours I stumbled upon this 5 year old question which attempts to solve the same thing but I can't get it to work.
I've configured my NuGet project as follows:
Foo.csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Author, Description, ect removed for brevity -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsyncFixer" Version="1.6.0">
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.2.0">
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
build\Foo.targets file:
<Project>
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Foo.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
And then the build\Foo.ruleset itself.
I run dotnet pack (dotnet version 7.0.102) in order to create the nuget package.
During package install the only thing that happens is that the package is added like a normal <PackageReference>, and I get all the analyser warnings but it doesn't add the ruleset file, and it doesn't add the <CodeAnalyzerRuleSet> tag to the .csproj file.
I've inspected the nuget package and it doesn't include the .targets and .ruleset files unless I add the following to my .csproj file as well:
<ItemGroup>
<None Include="build\**" Pack="True" PackagePath="build\" />
</ItemGroup>
This adds the files to the .nupkg but they are still not added or applied to my project during package install.
Any help or pointers are greatly appreciated.

Is there a way to update a PackageReference to some minimum version in Directory.Build.targets?

Using .NET Core 5, is there a way to set a minimum version for a PackageReference via a Directory.Build.targets file?
We have a set of projects, most of which need to use a specific version of a package that is referenced by default by the compiler (FSharp.Core). However, one or two projects need a higher version. Therefore, we have a Directory.Build.targets that has:
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.2" />
</ItemGroup>
Most project files therefore don't need any mention of FSharp.Core, but a couple would like a higher version, so they have this in their project files:
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="5.0.2" />
</ItemGroup>
However, the Directory.Build.targets file then downgrades the version as you'd expect. I don't want it to do this. I instead want the Directory.Build.targets file to impose a minimum version of 4.7.2.
I figured I could put a Condition on the PackageReference that only applies if the existing PackageReference has a Version < 4.7.2. But I haven't been able to figure out the MSBuild way of doing that, and am rapidly coming to the conclusion that it may not even be possible.
Here's where I'm at so far:
<Project>
<ItemGroup>
<FSharpCoreReferencesRequiringUpgrading Include="#(PackageReference)" Condition="$(%(Identity)) == 'FSharp.Core'"/>
</ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.2" />
</ItemGroup>
</Project>
By using an MSBuild Message task I've been trying to figure out what incantations are required to modify the Condition on FSharpCoreReferencesRequiringUpgrading such that it:
filters out all package references other than FSharp.Core
filters out all package references that are 4.7.2 or higher
I'm stuck on the first step because doing a simple string comparison as above does not work, and attempting to invoke ToString() or Include on the PackageReference does not work:
<!--
error MSB4184: The expression "%(Identity).ToString()" cannot be evaluated.
-->
Condition="$(%(Identity).ToString()) == 'FSharp.Core'"
Does anyone know if this is possible and how to go about it?
UPDATE 1: I've created a repro here
UPDATE 2: I've added an example of a fix suggested by David Kean (#davkean) here. However, I've definitely not implemented it correctly yet as I've obviously misunderstood him
UPDATE 3: I've added an example of another fix that works with all my testing, this time based on a recommendation from #Taskkant here. The fix is here
UPDATE
Try this in Directory.Build.targets:
<Project>
<PropertyGroup Condition="'$(UseHigherFSharpCoreVersion)' != 'true'">
<FSharpCorePackageVersion>4.7.2</FSharpCorePackageVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(UseHigherFSharpCoreVersion)' == 'true'">
<FSharpCorePackageVersion>5.0.2</FSharpCorePackageVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="$(FSharpCorePackageVersion)" />
</ItemGroup>
</Project>
Then in fsproj where higher version should be used:
<PropertyGroup>
<UseHigherFSharpCoreVersion>true</UseHigherFSharpCoreVersion>
</PropertyGroup>

Using dependency of dependency in .NET Core

I'm trying to understand how dependencies work in .NET Core.
Let's say I have 2 projects. The Project1 has this definition:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Project2\Project2.csproj" />
</ItemGroup>
</Project>
And this single class which uses the Newtonsoft.Json dependency:
public class Wizard
{
public void DoMagic()
{
var settings = Newtonsoft.Json.JsonConvert.DefaultSettings;
}
}
As you can see, it also references Project2, which has the following definition:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>
When I remove the Newtonsoft.Json package reference from the Project1, I would expect it to no longer compile... But it does compile! As it seems, it's able to use the Newtonsoft.Json that is a dependency of Project2.
So I have 2 questions:
I've done my tests with an "internal" project reference for convenience, but does this work the same way with external package references (i.e. NuGet)?
Can anybody explain the rationale here? It seems risky to me, a change of a dependency in any of the dependencies of my project can break my project, if it is using that sub-dependency. In other words, why is this allowed? It seems to be always a bad idea to use implicitly a dependency of a dependency, because it could change and break your code, so why does the framework allow this?
Yes, that's how it works across everything.
You can use PrivateAssets to control this:
https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets

JUnitTestLogger.dll is not copied on build when it is a transitive dependency

I'm making a core package for all the test projects in several solutions. A dependency graph is like:
MyTests.csproj -> MyTestFramework (nuget package) -> JUnitTestLogger (nuget package)
The problem is JUnitTestLogger.dll has to be copied to the output folder on the build of MyTests.csproj, or it just doesn't work. OK, I add:
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
to MyTests.csproj and build again. As a result, there is every transitive dependency dll in the output folder... except for JUnitTestLogger.dll. I've checked its source code JUnitTestLogger.csproj and found nothing special.
What's wrong with this package, why is it not copied? I'm asking here, not on Github because of low activity at the project's repo.
MyTests.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MyTestFramework" Version="1.2.3" />
</ItemGroup>
</Project>
MyTestFramework.csproj (part):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>true</IsPackable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JUnitTestLogger" Version="1.1.0" />
</ItemGroup>
</Project>
Auto-generated MyTestFramework.nuspec (part):
<dependencies>
<group targetFramework=".NETCoreApp2.2">
<dependency id="JUnitTestLogger" version="1.1.0" exclude="Build,Analyzers" />
</group>
</dependencies>
I'm a maintainer on JunitXml.TestLogger (A newer package than the one you mentioned, but both were forked from the same source in Github/Spekt and share some code). The main thing I am aware of that is unusual with the test loggers is that they are referenced in your project, but not used by the code.
I haven't seen this specific issue, but there have been several in the past like this one where the library isn't copied on build. When I first used these libraries I had to put extra steps in my CI builds to copy the library in. From what I recall, my solution was the same as the one in the issue, which was to switch from msbuild to dotnet build. So maybe that or one of the other closed issues there will give a clue.

.NET Core 3 Invalid reference from Common Library

I need 2 assemblies for my .NET Core 3 WPF App - System.Windows.Forms and System.Drawing.Common.
When I try to add them as references in Visual Studio 2019 from the common shared folder (C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\3.0.0) i get an exception message box saying "The reference is invalid or unsupported".
I would expect this kind of thing to happen with framework mismatches, but this is from the dotnet core v3 shared folder, so everything should line up.
Is there any particular trick to adding these references that I am unaware of?
csproj file:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="logo.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
In the .NET Core, the project system does not handle well binary assembly references. It may not always work and is an unsupported and unrecommended way to reference assemblies. The proper way is to use package references or framework references. The latter are expressed at the top level of the project via the Sdk attribute i.e. <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">.
First of all, you should not add assembly references but add package references either manually or using the package manager. The reference to System.Drawing.Common assembly for .NET Core 3.0 target should look as follows:
<PackageReference Include="System.Drawing.Common" Version="4.6.0" />

Resources