Выбранный ответ не работает для меня, так как ссылка не работает, и я не вижу код MyValueTypeViaFields
.
В любом случае у меня было такое же исключение No parameterless constructor found
для моего класса:
[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;
}
}
при десериализации со следующим кодом:
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<TType>(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;
}
}
}
вызывается как var dataObject = (IPersistableEvent)_binarySerializationService.FromBytes(data, eventType);
Мой класс сообщений FakeSimpleEvent
действительно имеет конструктор без параметров, потому что я хочуон неизменный.
Я использую protobuf-net 2.4.0
и могу подтвердить, что он поддерживает сложные конструкторы и неизменяемые классы сообщений.Просто используйте следующий декоратор
[ProtoContract(SkipConstructor = true)]
Если true, конструктор для типа обойден во время десериализации, что означает, что любые инициализаторы полей или другой код инициализации пропускаются.
ОБНОВЛЕНИЕ 1: (20 июня 2019 г.) Мне не нравится загрязнять мои классы атрибутами, которые принадлежат протобуферу, потому что модель предметной области должна быть технологически независимой (кроме, конечно, типов фреймворка dotnet)
Таким образом, для использования protobuf-net с классами сообщений без атрибутов и без конструктора без параметров (т.е. неизменяемого) вы можете иметь следующее:
public class FakeSimpleEvent
: IPersistableEvent
{
public Guid AggregateId { get; }
public string Value { get; }
public FakeSimpleEvent(Guid aggregateId, string value)
{
AggregateId = aggregateId;
Value = value;
}
}
, а затем сконфигурировать protobuf со следующим для этого класса.
var fakeSimpleEvent = RuntimeTypeModel.Default.Add(typeof(FakeSimpleEvent), false);
fakeSimpleEvent.Add(1, nameof(FakeSimpleEvent.AggregateId));
fakeSimpleEvent.Add(2, nameof(FakeSimpleEvent.Value));
fakeSimpleEvent.UseConstructor = false;
Это было бы эквивалентно моему предыдущему ответу, но намного чище.
PS: Не обращайте внимания на IPersistableEvent
.Для примера это не имеет значения, просто интерфейс маркера, который я использую где-то еще