Recently we changed our serializer and deserializer to Protobuff-net from BinaryFormatter.
In this, I’m facing some problems, after deserializing some of the values are still getting as null.
The Core.Shared library contains two classes – PrimaryKeyObject and TrackingObject
namespace Core.Shared {
[DataContract]
[Serializable]
[ProtoContract]
public class PrimaryKeyObject
{
[DataMember(Name = "Y1")]
[ProtoMember(401)]
internal int? __primaryKeyId;
[XmlIgnore]
public int? PrimaryKeyId
{
get
{
return __primaryKeyId;
}
}
public void SetPrimaryKeyId(int? primaryKeyId)
{
__primaryKeyId = primaryKeyId;
}
}
}
namespace Core.Shared
{
[DataContract]
[Serializable]
[ProtoContract]
public class TrackingObject : PrimaryKeyObject
{
[DataMember(Name = "Z1")]
[ProtoMember(301)]
internal HashSet<string> __propertyChangedList;
[DataMember(Name = "Z2")]
[ProtoMember(302)]
internal EntityStateFlag __entityState;
[DataMember(Name = "Z3")]
[ProtoMember(303)]
public byte[] ROW_VERSION { get; set; }
[DataMember(Name = "Z4")]
[ProtoMember(304)]
internal List<ObservableObject> __removedObjectsFromRelatedLists;
private bool _changeTrackingDisabled = false;
public ObservableObject()
: base()
{
}
public ObservableObject(EntityStateFlag entityState)
{
__entityState = entityState;
}
[XmlIgnore]
public EntityStateFlag EntityState
{
get
{
if (__entityState == 0)
__entityState = EntityStateFlag.Detached;
return __entityState;
}
set
{
if (value != __entityState)
{
__entityState = value;
if ((value == EntityStateFlag.Unchanged || value == EntityStateFlag.Detached) && __propertyChangedList != null)
__propertyChangedList = null;
}
}
}
}
[Flags]
public enum EntityStateFlag
{
Unchanged = 2,
Added = 4,
Deleted = 8,
Modified = 16,
Detached = 32
}
}
Then we have a Shared library – these classes are usually autogenerated from XML files.
Here as an example, we have a class called – AnsprechpartnerFunktionDetailDC and AbrufauftragUmlagerungEinsatzstoffeITypedDc
namespace Shared
{
[DataContract]
[Serializable]
[ProtoContract]
public partial class AnsprechpartnerFunktionDetailDC : TrackingObject
{
public AnsprechpartnerFunktionDetailDC() : base()
{
}
public AnsprechpartnerFunktionDetailDC(EntityStateFlag entityState)
{
EntityState = entityState;
}
[DataMember(Name = "A1")]
[ProtoMember(1)]
internal int __ID_ANSPRECHPARTNER_FUNKTION;
[XmlIgnore]
public int ID_ANSPRECHPARTNER_FUNKTION
{
get
{
return __ID_ANSPRECHPARTNER_FUNKTION;
}
set
{
if (value != __ID_ANSPRECHPARTNER_FUNKTION)
{
__ID_ANSPRECHPARTNER_FUNKTION = value;
SetPrimaryKeyId(value);
}
}
}
[DataMember(Name = "A2")]
[ProtoMember(2)]
internal string __BEZEICHNUNG;
[XmlIgnore]
public string BEZEICHNUNG
{
get
{
return __BEZEICHNUNG;
}
set
{
if (value != __BEZEICHNUNG)
{
__BEZEICHNUNG = value;
}
}
}
[DataMember(Name = "A3")]
[ProtoMember(3)]
internal bool __AKTIV;
[XmlIgnore]
public bool AKTIV
{
get
{
return __AKTIV;
}
set
{
if (value != __AKTIV)
{
__AKTIV = value;
}
}
}
}
}
namespace Shared
{
[DataContract]
[Serializable]
[ProtoContract]
public partial class AbrufauftragUmlagerungEinsatzstoffeITypedDc : TrackingObject
{
public AbrufauftragUmlagerungEinsatzstoffeITypedDc() : base()
{
}
public AbrufauftragUmlagerungEinsatzstoffeITypedDc(EntityStateFlag entityState)
{
EntityState = entityState;
}
[DataMember(Name = "A1")]
[ProtoMember(11)]
public int IdMaterial { get; set; }
[DataMember(Name = "A2")]
[ProtoMember(12)]
public decimal? BenotigteMenge { get; set; }
}
}
In another project, we have further processes for the creation of data, serialization, and deserialization.
static void Main(string[] args)
{
RegisterTypesForObject();
Console.WriteLine("Hello, World!");
MemoryStream objectSerializer = new MemoryStream();
objectSerializer.Seek(0, SeekOrigin.Begin);
var x1 = new AbrufauftragUmlagerungEinsatzstoffeITypedDc(EntityModel.Shared.EntityStateFlag.Added)
{
IdMaterial = 101,
BenotigteMenge = 102,
ROW_VERSION = new byte[] { 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 }
};
WriteNext(objectSerializer, x1);
var x2 = new AnsprechpartnerFunktionDetailDC(EntityModel.Shared.EntityStateFlag.Added)
{
ID_ANSPRECHPARTNER_FUNKTION = 111,
AKTIV = true,
BEZEICHNUNG = "sample test",
ROW_VERSION = new byte[] { 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 }
};
WriteNext(objectSerializer, x2);
objectSerializer.Seek(0, SeekOrigin.Begin);
var i1 = DeSerialized<AbrufauftragUmlagerungEinsatzstoffeITypedDc>(objectSerializer);
var i2 = DeSerialized<AnsprechpartnerFunktionDetailDC>(objectSerializer);
}
static readonly Dictionary<int, Type> typeLookup = new Dictionary<int, Type>();
static void WriteNext(Stream stream, object obj)
{
Type type = obj.GetType();
if (!typeLookup.ContainsValue(type))
{
var key = typeLookup.Count > 0 ? typeLookup.Max(i => i.Key) : 0;
typeLookup.Add(key + 1, type);
int field = typeLookup.Single(pair => pair.Value == type).Key;
Serializer.NonGeneric.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Base128, field);
}
}
static T DeSerialized<T>(Stream stream)
{
object obj;
if (Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128, field => typeLookup[field], out obj))
{
if (obj.GetType() == typeof(T))
return (T)obj;
}
return default(T);
}
public static void RegisterTypesForObject()
{
var assemblyLocation = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Assembly assembly = Assembly.LoadFile(assemblyLocation + @"Shared.dll");
// Specify the target namespace
string targetNamespace = "Shared";
// Get all derived classes from the namespace
var derivedClasses = GetDerivedClassesFromNamespace<ObservableObject>(targetNamespace, assembly);
int i = 100;
// Loop through each derived class and output its name
foreach (var type in derivedClasses)
{
if (type == typeof(ObservableObject))
continue;
Type currentType = type;
while (currentType != null && currentType.BaseType != typeof(object))
{
RuntimeTypeModel.Default[currentType.BaseType].AddSubType(i, currentType);
currentType = currentType.BaseType;
i++;
}
}
}
public static IEnumerable<Type> GetDerivedClassesFromNamespace<TBase>(string targetNamespace, Assembly assembly)
{
var baseType = typeof(TBase);
return assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract &&
baseType.IsAssignableFrom(t) && // Check if it inherits from the base class
t.Namespace == targetNamespace) // Check if it's in the target namespace
.ToList();
}
}
The RegisterTypesForObject method registers its base and derived types at the start of the program the WriteNext Method is used to Serialize and the DeSerialized Method as a deserializer.
After Deserialized of the ‘AnsprechpartnerFunktionDetailDC’ and ‘AbrufauftragUmlagerungEinsatzstoffeITypedDc’ the ROW_VERSION and PrimaryKeyId is null.
(AbrufauftragUmlagerungEinsatzstoffeITypedDc – here currently not used primaryKeyid)
In this case, what did I miss and How can keep ROW_VERSION and PrimaryKeyId values after deserialized?
2