After clean project regenerating c# files from .proto in another project - c#

I have two projects:
Project hosts the code of the gRPC server and also the .proto files.
Project acts as the client but has also a lot of other different functionality
I want to be able to clean and build my client project, that depends on auto generated .cs files. To generate the .cs files from .proto files Google.Protobuf and Grpc.Net.ClientFactory is used.
So far I've done:
I added a service reference to my 2. project.
My 2.project.csproj looks like (for an Example.proto):
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<PackageReference Include="Grpc.Core" Version="2.32.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Grpc.AspNetCore" Version="2.32.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.32.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
<PackageReference Include="Grpc.AspNetCore" Version="2.32.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.32.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.13.0" />
<PackageReference Include="Grpc.Tools" Version="2.32.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<Protobuf Include="..\Project_A\Protos\Example.proto" GrpcServices="Client">
<Link>Protos\Example.proto</Link>
</Protobuf>
</ItemGroup>
When I am now changing the .protofile the client .cs files get generated in the path:
...\2.project\obj\$(ConfigurationName)\$(FrameworkName)\
This all fine when simply building the second project. But whenever the second project gets a clean rebuild the .cs files are not generated again leaving me with missing dependencies.
I would guess somewhere is a option to trigger the generation of .proto files before the build starts?
Or Should I exclude the generated files from the cleanup?
Or is there some other way to achieve this that I'm not seeing?
UPDATE:
I now moved the proto files in a seperate project and import them now using this statement:
<ItemGroup>
<Protobuf Include="..\Proto_Project\Protos\Example.proto" GrpcServices="Client" OutputDir="Protos" CompileOutputs="false">
<Link>Protos\Example.proto</Link>
</Protobuf>
</ItemGroup>
This works in that way, that everytime I change the proto, it gets reconverted in .cs.
But here is the source of my current (and also previous, as I know think) problem. Every time I rebuild the solution the protofiles get reconvertet to .cs. The problem is that this happens after the compilation of the project. This leads to missing dependencies, since the files are not present at compile time. When simply building the solution the files are not retranslated and everything works.
How can I either stop the protobuf compiler from running on rebuild, or let him run earlier?

I would advice you to decouple the two projects by moving all the .proto files to a separate code repository. Next use that repository in your other projects and generate the proto code for that project specifically. This way you can also use the backwards compatibility properties of Protobuf.
With git you can do that with git submodules. You can track the version of the proto file repo in your server and client project.

Related

The attribute "Include" in element <PackageReference> is unrecognized

I created a Directory.Build.props file by right clicking the Solution on Solution Explorer, creating an XML file, and named it so. I then input this XML and tried building but was met with errors:
<Project>
<PropertyGroup>
<Version>1.2.3</Version>
<Authors>John Doe</Authors>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</PropertyGroup>
</Project>
The errors say:
The attribute "Include" in element <PackageReference> is unrecognized. and
Project "C:\redacted\Directory.Build.props" was not imported by "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Microsoft.Common.props" at (33,3), due to the file being invalid. ProjectName C:\Users\me\source\redacted\ProjectName\Directory.Build.props 33
I am so confused here. The other articles said to locate the MSBuild and I know that its on my machine. The build was working just fine prior to me adding the XML too. Any guidance would be very appreciated! I am currently on Visual Studio 2022 by the way.
The PackageReference element needs to be in an ItemGroup - you've got it in a PropertyGroup. It should look like this:
<Project>
<PropertyGroup>
<Version>1.2.3</Version>
<Authors>John Doe</Authors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
I had the same kind of problem one time and I found out it was caused by ‘\xef\xbb\xbf#’ which is a ‘Unicode BOM(Byte Order Mark)’ and consists of invisible characters added by certain text editors like Notepad++, for instance. The BOM often functions as a magic number used to pass along information to the program reading the file, such as the Unicode character encoding or endianess but it's presence can interfere with software that does not expect it.
The way I found it by fluke was I had my csproj file on github and when I clicked 'edit', the BOM stuck out like a red dot. I simply backspaced on it and then everything went well.
I came accross the same kind of problem, for me it was due to invalid XML structure :
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.2.32">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
</PackageReference>
</ItemGroup>
As you can see, the first PackageReference tag was not closed, leading to the exact same error The attribute "Include" in element <PackageReference> is unrecognized. on the second PackageReference tag.
Valid XML structure :
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.2.32"/>
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3"/>
</ItemGroup>
I hope that can help somebody else !

Using ANTLR4 in .NET Core without the use of external Java library?

I've followed through these instructions but I get a ton of compilation errors after the files have been compiled.
What I have done:
Created a new Console project in .NET Core 3.1.
Installed NuGet package Antlr4.
Added a new text file named example.g4 to the project, and saved it in encoding UTF-8 without signature.
Populated the grammar with some demo features.
Build Solution.
Ton of errors after successful compilation of lexer/visitor/parser/etc.
Some of those errors include the following:
The name '_interp' does not exist in the current context AntlrDemo C:\AntlrDemo\obj\Debug\netcoreapp3.1\exampleLexer.cs 45 Active
'ParserATNSimulator' does not contain a constructor that takes 2 arguments AntlrDemo C:\AntlrDemo\obj\Debug\netcoreapp3.1\exampleParser.cs 95 Active
'exampleParser.TokenNames': no suitable method found to override AntlrDemo C:\AntlrDemo\obj\Debug\netcoreapp3.1\exampleParser.cs 69 Active
What's going on?
The issue is not from the grammar - it successfully compiles in .NET Framework.
If you don't mind working with the official Antlr4 code generator and runtime, but don't want to actually download and install Java and the Antlr Tool .jar by hand, try this instead:
Install the latest NET 5, or use old NET Core.
dotnet new -i Antlr4BuildTasks.Templates
mkdir Foo
cd Foo
dotnet new antlr
dotnet build
dotnet run
This does use the Antlr4 Java tool, but it's completely hidden. You don't download the runtime, nor Java. It's all contained in the Antlr4BuildTasks tool that you just reference in your .csproj. If you want to work with an older Antlr4 version, like 4.8 or 4.7, Antlr4BuildTasks will download the tool and runtime from Maven Central and NuGet.org; you just set the versions in the .csproj file then "dotnet build".
I have another tool that generates a driver for grammar and support code for C# (both official and Harwell's version), Java, and JavaScript targets. It is now being used for CI in github.com/antlr/grammars-v4.
If you try swapping between Antlr4 (the official Antlr4) and Antlr4cs (Harwell's tool/runtime), you will find the tools and runtime are quite different. There is no shim to allow code written for one runtime to be used in the other, but I am working on one.
As far as the <PrivateAssets> code in the .csproj file, getting rid of the lines as you suggest is fine. The reason it is included is to not propagate the dependent assemblies of the build tool directly into your code. But, while the tool is only useful in building the app, not running it, <PrivateAssets> doesn't prevent the assembly for the tool itself is still being included.
--Ken
After you install Antlr4 NuGet package, the following code is added to your .csproj file:
<ItemGroup>
<PackageReference Include="Antlr4" Version="4.6.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
The fix was to change the above to the following:
<ItemGroup>
<PackageReference Include="Antlr4" Version="4.6.6">
<!--<PrivateAssets>all</PrivateAssets>-->
</PackageReference>
</ItemGroup>
It also seems to work by uncommenting the PrivateAssets element. But I have no idea what the actual problem is here, and if I'm doing something wrong. Can someone shine some light about it?
EDIT: Another alternative solution is to instead install the two NuGet Packages Antlr4.CodeGenerator and Antlr4.Runtime.
Hopefully, this won't cause more confusion. I just moved my C# parser library from NET Framework 4.8 to NET 5 and was able to build it without errors with this .csproj file.
I changed the target framework from net5.0 to net5.0-windows7.0 in the example below to avoid compiler warning CA1416 which protested that I was using debug print methods (based on Console.Writeline calls with param[] arguments) in my code. I wanted to keep my debugging messages so I switched from the net5.0 target.
But the plain 'net5.0' compiled okay for me (except for the warnings I just described). My simple test cases ran fine with net5.0.
Here is an excerpt from my class library file, showing that I could leave the PrivateAssets and IncludeAssets lines alone.
<PropertyGroup>
<!-- use net5.0-windows7.0 to avoid CA1416 warnings about Console Writeline calls only available in win7 and later-->
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Antlr4" Version="4.6.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Antlr4.Runtime" Version="4.6.6" />
</ItemGroup>
Here is an excerpt from my unit test project, showing the inclusion of Antlr4(4.6.6) and the Antlr4.Runtime(4.6.6).
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Antlr4" Version="4.6.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Antlr4.Runtime" Version="4.6.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

Compile .bond files of projects using my NuGet package

I have a NuGet package with a .bond file. Users of my package can derive their Bond structs from the structs in my package's .bond file.
I want the user's Bond files to be compiled when they include my NuGet package. Today they must include my NuGet and the Bond.CSharp NuGet. But, my NuGet already has a reference to Bond.CSharp.
How can I author my package so that the consumers do not need to have their own <PackageReference Include="Bond.CSharp" ... />?
Bond codegen is run from the Bond.CSharp's build targets.
By default, the build targets of packages you consume do not flow to your consumers. The default value of a PackageReference's PrivateAssets is "contentfiles;analyzers;build".
You can override this behavior in your csproj's PackageReference:
<ItemGroup>
<PackageReference Include="Bond.CSharp" Version="[9.0.3]">
<!-- removing "build" from default so that consumers also run codegen -->
<PrivateAssets>contentfiles;analyzers</PrivateAssets>
</PackageReference>
</ItemGroup>
I assume you are compiling the base struct into an assembly in your package. Bond codegen assumes that the generated code and the runtime library exactly match, so I've used an exact match version bound in the PackageReference: [9.0.3]
You said that you want your consumers to be able to derive from your Bond structs, so you'll probably also want to configure their BondImportPath to include the .bond file inside your package. To do this, you need to
make sure the .bond files are included in the package and
add a package .props file to set the BondImportPath to the package directory with said .bond files.
The make sure the .bond files are included in the package, add something like this to your package's .csproj file:
<ItemGroup>
<None Include="bond/**">
<Pack>true</Pack>
<PackagePath>build/bond/</PackagePath>
</None>
</ItemGroup>
This assumes that your .bond files live in a bond\ subdirectory.
To automatically add something to BondImportPath, you need to add a package .props file that will be automatically imported by consumers. Create a file named ExactNameOfPackage.props with the following content:
<Project>
<ItemGroup>
<BondImportDirectory Include="$(MSBuildThisFileDirectory)bond" />
</ItemGroup>
</Project>
This .props file also needs to be packed. Add this to your project's .csproj file:
<ItemGroup>
<None Include="ExactNameOfPackage.props">
<Pack>true</Pack>
<PackagePath>build/</PackagePath>
</None>
</ItemGroup>
Now, the consumer can just use a PackageReference. Any .bond files in their project will be compiled automatically, and they can use import "you-file.bond" to refer to a .bond file in your package.
Build assets do not flow transitively. The NuGet 5+ buildTransitive feature looks like it solves this, but I haven't experimented with it.
Here are the complete project files I used. The complete code is in my GitHub repository, export-bond-file-nuget.
lib-with-bond.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<PackageId>LibWithBond</PackageId>
<Version>1.2.0</Version>
</PropertyGroup>
<ItemGroup>
<None Include="LibWithBond.props">
<Pack>true</Pack>
<PackagePath>build/</PackagePath>
</None>
<None Include="bond/**">
<Pack>true</Pack>
<PackagePath>build/bond/</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Bond.CSharp" Version="[9.0.3]">
<PrivateAssets>contentfiles;analyzers</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
LibWithBond.props
<Project>
<ItemGroup>
<BondImportDirectory Include="$(MSBuildThisFileDirectory)bond" />
</ItemGroup>
</Project>
consume-lib.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- I had a local NuGet source pointing to the output of running
`dotnet pack` on lib-with-bond.csproj -->
<PackageReference Include="LibWithBond" Version="1.2.0" />
</ItemGroup>
</Project>
I was able to make this work for a PackageReference. My initial experiments making it work for ProjectReference were not successful, and I ran out of time to work more on this answer.

Running MS Fakes before build properly while using the new NetSDK project format

I am trying to get Microsoft Fakes to work properly with my unit test project (net47) wherein my project file format is using the new NetSDK format.
With Visual Studio 2019, I can add a Fakes Assembly and things seem to work fine, until we try to build/run the tests on our build agent. It seems as though when you compile/build a project the fakes assemblies are generated, but they are done either in parallel or after the build (I'm not sure). This same problem happens on my development machine running Visual Studio 2019 Enterprise.
I noticed that if no FakesAssemblies folder exists, one is created during the build, but the compilation fails because none of the *.Fakes namespaces were discovered. A second compilation/build works because now the FakesAssemblies folder is populated. One thing to note is that I took the dll file for Microsoft Fakes and put it in a NuGet package in our companies private feed, that way I can pull it down as a NuGet package instead of a reference.
SampleUnitTests.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net47</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
<PackageReference Include="Microsoft.QualityTools.Testing.Fakes" Version="16.0.28621.142" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
</ItemGroup>
<ItemGroup>
<Fakes Include="Fakes\*.fakes" />
<Reference Include="FakesAssemblies\*.dll" />
</ItemGroup>
</Project>

How to include a library in .NET Core 2.0

I don't know much about .NET yet, so I guess I'm missing something obvious.
I created a library (targeted as a DLL file, set for .NET standard 2.0), packaged it both as a DLL file and as a NuGet package. Now I want to use the library in another project, on ASP.NET Core 2.0. How should I do it?
I am currently on a Linux VM, so I use Visual Studio Code, and therefore I would prefer some solution without using the full Visual Studio. I tried some solutions using the full Visual Studio, but that didn't work for me, because I haven't found a reference explorer anywhere.
You would have to reference your library in the .csproj file:
An empty .csproj file would look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
Now, you can have two types of references:
Project Reference - You have a project that serves as a class library in your solution and you want to reference it directly:
<ProjectReference Include="..\..\src\mylib.csproj" />
Package Reference - You have a link to a NuGet package:
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
Inside your .csproj file, the references should be inside an "ItemGroup" block, and each reference type should have its own "ItemGroup".
Here's an example of a .csproj file with some package references and some project references:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\mylib.csproj" />
<ProjectReference Include="..\..\src\mylib2.csproj" />
</ItemGroup>
</Project>
A lot of people recommend one of two solutions:
Copy the library into your solution folder.
cp -r foo/foo ./foo
dotnet sln add foo/foo.csproj
cd bar
dotnet add reference ../foo/foo.csproj
This is a terrible solution.
Don't do this (i.e., copy and paste your library code every time you want to use it. It is bad for obvious reasons).
Setup a local NuGet repository, copy your library into the local repository, and then add it.
nuget add -name "Local" -source /home/doug/packages
nuget add ~/foo/foo.nupkg -source /home/doug/packages
Then install the package:
cd bar
dotnet add package foo
This is an acceptable solution, but the workflow is quite irritating if you are actively working on your library (foo), because the -source path must be absolute.
--
I recommend you look at dotnet add package with local package file, which explains how you can have a local cache of any custom .nupkg files you want to work with.
Basically, just drop this into your solution folder:
File NuGet.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="local" value="./packages" />
</packageSources>
</configuration>
(Notice that ./packages is a relative path, that will work even when you check your project out on an entirely different machine or OS.)
Now if you call dotnet add package X it will also look for any file called x.nupkg in your ./packages/ folder.
Now if you want to use any custom local library, all you need to do is:
cp ~/foo/foo.nupkg ./packages
cd bar
dotnet add package foo
(Note: by default NuGet caches your .nupkg files in ~/.nuget and will restore packages from that folder if you call dotnet add package X, even if you have a different X.nupkg in your local ./packages folder. You may find the command dotnet nuget locals all --clear useful if you encounter strange behaviour to ensure you're getting the exact version of the .nupkg file you want, not some arbitrary cached version)
Another way to reference the local package in the .csproj file:
<ItemGroup>
<Reference Include="MyAssembly">
<HintPath>path\to\MyAssembly.dll</HintPath>
</Reference>
</ItemGroup>
Given that the DLL file you want to reference in the new ASP.NET Core 2.0 project is relatively fresh, I suspect you will need to make changes to this original DLL file as you develop the ASP.NET project.
In this situation I would add the original DLL project as part of the ASP.NET solution so you can work on both sets of source code, including setting of breakpoints within the same solution workspace.
NuGet packaging of the original DLL project can be delayed until the first release of your whole combined solution has stabilised and you want to make that DLL file available to a larger developer audience beyond the scope of your ASP.NET application.
A good solution will be to add the library (.dll file) that you want to use to the Project's References of your project in which you want to use the library:
Right Click on the project → Add → Reference → Project → Browse → Path_to_your_generated_library (.dll)
This will automatically generate the following node in the .csproj file:
<ItemGroup>
<Reference Include="DotNetCoreClassLibraryCodeParser">
<HintPath>..\..\DotNetCoreClassLibrary\DotNetCoreClassLibrary\bin\Debug\netcoreapp2.1\DotNetCoreClassLibrary.dll</HintPath>
</Reference>
</ItemGroup>

Categories