Protobuf C# Generated Class usage with Entity Framework Repeated Fields not settable

c# entity-framework-core protocol-buffers


I am having some issues integrating some Protobuf message generated C# classes with Entity Framework Core. I am able to use the protobuf generated classes in most cases to store information correctly into both sqlite and mysql databases. The issue comes when the protobuf message uses a repeated variable. I am able to store the repeated field into the database by serialising to a list of strings but when trying to read back from the database I get this error.

Unhandled Exception: System.InvalidOperationException: No backing field could be found for property '<propertyname>' of entity type '<entityname>' and the property does not have a setter.

From searching it seems that a design decision was made that when generating the protobuf class that repeated fields should not have a setter method only a get and this is causing issues when the Entity framework based functions are trying to write the parsed result back into the repeated field variable.

I was wondering if anyone has any ideas of potential ways to work around this issue of not having a setter within the Entity Framework or if there is a better way to achieve this type of functionality of storing Protobuf generated messages into databases.

For reference I have the entity property setup like this currently:

entity.Property(e => e.StationTypes)
      types => types.ToString(),
      column => ConvertToRepeatedField(column)

Where the data is written as a JSON style string into the database and then Converted back into a repeated field using the custom conversions.

Thanks For any help in advance.

Edit: Currently using the Google.Protobuf (3.6.1) Library

9/18/2019 2:09:04 AM

Expert Answer

The problem here is that for the message:

syntax = "proto3";
message Foo {
    repeated string bar = 1;

protoc generates:

  private readonly pbc::RepeatedField<string> bar_ = new pbc::RepeatedField<string>();
  public pbc::RepeatedField<string> Bar {
    get { return bar_; }

But this url_ does not match any of the expected patterns described here; specifically, in this case, EF-Core would have accepted _Bar, _bar, m_Bar, or m_bar - but not bar_.

If you're amenable to changing library, you could try using protobuf-net; protogen (protobuf-net's equivalent to protoc) generates:

    [global::ProtoBuf.ProtoMember(1, Name = @"bar")]
    public global::System.Collections.Generic.List<string> Bars { get; }
        = new global::System.Collections.Generic.List<string>();

Now; it isn't entirely clear whether EF will understand this pattern either, but this is, ultimately, the Roslyn auto-generated pattern (the field will be <Bars>k__BackingField), so it is at least worth a try. Most ORMs and serializers understand the Roslyn patterns.

Otherwise, you'll need to look at the later parts of the documentation above, which describes ways of explicitly configuring the name of the backing field.

9/18/2019 6:50:06 AM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow