Je pense que cela peut être un bug ou une limitation avec l'API RuntimeTypeModel
.
La méthode qui détermine si ValueMember
est une collection est RuntimeTypeModel.ResolveListTypes()
. Il essaie d'inférer le type d'élément de collection lorsque le itemType
entrant est nul. Lors de la construction d'un contrat pour NotACollectionHolder
en utilisant uniquement des attributs statiques, par exemple en faisant:
var model = TypeModel.Create();
var schema = model.GetSchema(typeof(NotACollectionHolder));
Alors ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour(bool isEnum, ProtoMemberAttribute normalizedAttribute)
est appelé à créer une initialiser le ValueMember
. Il a la logique suivante:
// check for list types
ResolveListTypes(model, effectiveType, ref itemType, ref defaultType);
// but take it back if it is explicitly excluded
if(itemType != null)
{ // looks like a list, but double check for IgnoreListHandling
int idx = model.FindOrAddAuto(effectiveType, false, true, false);
if(idx >= 0 && model[effectiveType].IgnoreListHandling)
{
itemType = null;
defaultType = null;
}
}
Notez la vérification explicite IgnoreListHandling
? Cela empêche correctement some_objects
d'être sérialisé en tant que collection.
À l'inverse, si l'ajoute ValueMember
programme comme suit:
var model = TypeModel.Create();
var meta = model.Add(typeof(NotACollectionHolder), false);
var member = meta.AddField(1, "some_objects", null, null);
var schema = model.GetSchema(typeof(NotACollectionHolder));
Alors MetaType.AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
est appelée, qui fait simplement:
ResolveListTypes(model, miType, ref itemType, ref defaultType);
avis il n'y a pas de contrôle pour IgnoreListHandling
? C'est la cause de votre problème.
Malheureusement, ValueType.itemType
est en lecture seule et MetaType[int fieldNumber]
est get-only, donc il ne semble pas y avoir une API simple à appeler pour résoudre ce problème. Vous pourriez envisager reporting an issue.
La seule solution que je pourrais trouver est d'introduire un substitut pour le type NotACollectionHolder
comme ceci:
[ProtoContract]
internal class NotACollectionHolderSurrogate
{
[ProtoMember(1)]
internal NotACollectionSurrogate some_objects;
public static implicit operator NotACollectionHolder(NotACollectionHolderSurrogate input)
{
if (input == null)
return null;
return new NotACollectionHolder { some_objects = input.some_objects };
}
public static implicit operator NotACollectionHolderSurrogate(NotACollectionHolder input)
{
if (input == null)
return null;
return new NotACollectionHolderSurrogate { some_objects = input.some_objects };
}
}
[ProtoContract]
internal class NotACollectionSurrogate
{
[ProtoMember(1)]
public int some_data;
public static implicit operator NotACollection(NotACollectionSurrogate input)
{
if (input == null)
return null;
return new NotACollection { some_data = input.some_data };
}
public static implicit operator NotACollectionSurrogate(NotACollection input)
{
if (input == null)
return null;
return new NotACollectionSurrogate { some_data = input.some_data };
}
}
Et puis faire:
var model = TypeModel.Create();
model.Add(typeof(NotACollectionHolder), false).SetSurrogate(typeof(NotACollectionHolderSurrogate));
var schema = model.GetSchema(typeof(NotACollectionHolder));
Le contrat généré est aussi nécessaire:
message NotACollectionHolderSurrogate {
optional NotACollectionSurrogate some_objects = 1;
}
message NotACollectionSurrogate {
optional int32 some_data = 1 [default = 0];
}