How to use protobuf-net with immutable value types?

后端 未结 2 587
野趣味
野趣味 2021-02-13 16:04

Suppose I have an immutable value type like this:

[Serializable]
[DataContract]
public struct MyValueType : ISerializable
{
private readonly int _x;
private read         


        
2条回答
  •  天涯浪人
    2021-02-13 16:59

    The selected answer didn't work for me since the link is broken and I cannot see the MyValueTypeViaFields code.

    In any case I have had the same exception No parameterless constructor found for my class:

    [ProtoContract]
    public class FakeSimpleEvent
        : IPersistableEvent
    {
        [ProtoMember(1)]
        public Guid AggregateId { get; }
        [ProtoMember(2)]
        public string Value { get; }
        public FakeSimpleEvent(Guid aggregateId, string value)
        {
            AggregateId = aggregateId;
            Value = value;
        }
    }
    

    when deserializing it with the following code:

    public class BinarySerializationService
        : IBinarySerializationService
    {
        public byte[] ToBytes(object obj)
        {
            if (obj == null) throw new ArgumentNullException(nameof(obj));
            using (var memoryStream = new MemoryStream())
            {
                Serializer.Serialize(memoryStream, obj);
                var bytes = memoryStream.ToArray();
                return bytes;
            }
        }
    
        public TType FromBytes(byte[] bytes)
            where TType : class
        {
            if (bytes == null) throw new ArgumentNullException(nameof(bytes));
            var type = typeof(TType);
            var result = FromBytes(bytes, type);
            return (TType)result;
        }
    
        public object FromBytes(byte[] bytes, Type type)
        {
            if (bytes == null) throw new ArgumentNullException(nameof(bytes));
            int length = bytes.Length;
            using (var memoryStream = new MemoryStream())
            {
                memoryStream.Write(bytes, 0, length);
                memoryStream.Seek(0, SeekOrigin.Begin);
                var obj = Serializer.Deserialize(type, memoryStream);
                return obj;
            }
        }
    }
    

    being called like var dataObject = (IPersistableEvent)_binarySerializationService.FromBytes(data, eventType);

    My message class FakeSimpleEvent has indeed parameterless constructor because I want it immutable.

    I am using protobuf-net 2.4.0 and I can confirm that it supports complex constructors and immutable message classes. Simply use the following decorator

    [ProtoContract(SkipConstructor = true)]
    

    If true, the constructor for the type is bypassed during deserialization, meaning any field initializers or other initialization code is skipped.

    UPDATE 1: (20 June 2019) I don't like polluting my classes with attributes that belong to protobuffer because the domain model should be technology-agnostic (other than dotnet framework's types of course)

    So for using protobuf-net with message classes without attributes and without parameterless constructor (i.e: immutable) you can have the following:

    public class FakeSimpleEvent
        : IPersistableEvent
    {
        public Guid AggregateId { get; }
        public string Value { get; }
        public FakeSimpleEvent(Guid aggregateId, string value)
        {
            AggregateId = aggregateId;
            Value = value;
        }
    }
    

    and then configure protobuf with the following for this class.

    var fakeSimpleEvent = RuntimeTypeModel.Default.Add(typeof(FakeSimpleEvent), false);
    fakeSimpleEvent.Add(1, nameof(FakeSimpleEvent.AggregateId));
    fakeSimpleEvent.Add(2, nameof(FakeSimpleEvent.Value));
    fakeSimpleEvent.UseConstructor = false;
    

    This would be the equivalent to my previous answer but much cleaner.

    PS: Don't mind the IPersistableEvent. It's irrelevant for the example, just a marker interface I use somewhere else

提交回复
热议问题