When attempting to serialize an object using Google's Protocol Buffers I get the following error:
Type is not expected, and no contract can be inferred:
I have three projects:
The Library contains a *.proto file. This contains several different message
objects, each with properties that are either strings or int32. There's two "wrapper" objects, one named Request
(and the other Response
). There's nothing obviously complicated.
The NuGet packages for this Library are:
<PackageReference Include="Google.Protobuf" Version="3.17.3" />
<PackageReference Include="Grpc" Version="2.40.0" />
<PackageReference Include="Grpc.Tools" Version="2.40.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
It may be that some aren't necessary....
So the idea is that my .NET 4.8 WebAPI will receive the Request
object (that is one of these classes defined in the proto file) and then return the Response
object (also defined in this proto file).
However....my test is failing before it even gets the chance to make the Http call to the WebAPI...
My .NET 5 Test project has the following NuGet package:
<PackageReference Include="protobuf-net" version="3.0.101" />
It also has a package reference to my .NET Standard library (so knows about Request
and Response
, and the NuGet packages that it has.
In my test I create a new Request
object (from the namespace SomeNamespace
defined in the proto file) and populate it's properties.
I then attempt to serialize it using:
using MemoryStream stream = new();
ProtoBuf.Serializer.Serialize<Request>(stream, objectToBeSerialized);
And that's where the error occurs:
System.InvalidOperationException HResult=0x80131509 Message=Type is not expected, and no contract can be inferred: SomeNamespace.Request Source=protobuf-net.Core StackTrace: at ProtoBuf.Internal.ThrowHelper.ThrowInvalidOperationException(String message, Exception innerException) in //src/protobuf-net.Core/Internal/ThrowHelper.cs:line 56 at ProtoBuf.Meta.TypeModel.ThrowUnexpectedType(Type type, TypeModel model) in //src/protobuf-net.Core/Meta/TypeModel.cs:line 1648 at ProtoBuf.Meta.TypeModel.SerializeRootFallback(State& state, Object value) in //src/protobuf-net.Core/Meta/TypeModel.cs:line 282 at ProtoBuf.Meta.TypeModel.SerializeImpl[T](State& state, T value) in //src/protobuf-net.Core/Meta/TypeModel.cs:line 358 at ProtoBuf.Serializer.Serialize[T](Stream destination, T instance, Object userState) in //src/protobuf-net/Serializer.Serialize.cs:line 33 at ProtoBuf.Serializer.Serialize[T](Stream destination, T instance) in //src/protobuf-net/Serializer.Serialize.cs:line 20 at TestNamespace.TempTests.d__0.MoveNext() in c:\SomePath\TempTests.cs:line 53
I'm not clear why this might be failing.
Ideally, I'd like to standardise on best practices, so if I'm not using the ideal NuGet packages then please let me know.
In protobuf, when working with a .proto, there are two sets of tools involved:
Now, what we also need to know is that there are multiple implementations of protobuf in .NET-land; relevant to this question, there is the Google implementation, and protobuf-net (there are others, but they don't relate to this question).
What I think you've done is: you've used the Google tools for step 1 ("protoc") but protobuf-net for step 2. The tools for 1 and 2 need to match: Google and Google is fine; protobuf-net and protobuf-net is fine - but not crossovers.
So, either:
a. use the Google runtime library instead of protobuf-net ("Google.Protobuf", as a nuget package), or b: use protobuf-net's schema tools and code gen (see https://protobuf-net.github.io/protobuf-net/contract_first.html, including the links at the bottom for other options)
If I'm right in my hunch, please let me know - I'll try to make protobuf-net recognize this scenario and at least offer suitable guidance