Question

J'ai une hiérarchie de classes qui ressemble à ceci. Ces classes contiennent beaucoup d'autres détails que j'exclus. Ceci est une simplification de se concentrer sur l'aspect sérialisation de ces classes.

[ProtoInclude(1, typeof(Query<bool>))]
[ProtoInclude(2, typeof(Query<string>))]
[ProtoInclude(3, typeof(Query<int>))]
[ProtoInclude(4, typeof(Query<decimal>))]
[ProtoInclude(5, typeof(Query<DataSet>))]
abstract class Query
{
    public string Result { get; set; }
}
[ProtoInclude(1, typeof(SpecialQuery)]
abstract class Query<T> : Query
{
    public new T Result { get; set; }
}
abstract class SpecialQuery : Query<DataSet>
{
    public new string Result { get; set; }
}

J'ai aussi environ 150 descendants générés automatiquement de la requête générique, avec une grande variété de types génériques. Par exemple:

[ProtoContract]
class W : Query<bool>
{
}
[ProtoContract]
class X : Query<string>
{
}
[ProtoContract]
class Y : Query<int>
{
}
[ProtoContract]
class Z : SpecialQuery
{
}

J'ai aussi AutoGénéré [ProtoInclude] pour tous ces types. Par exemple:

[ProtoInclude(1, typeof(W)]
[ProtoInclude(2, typeof(X)]
[ProtoInclude(3, typeof(Y)]
[ProtoInclude(4, typeof(Z)]

La question est, comment puis-je déployer ces 150 ProtoIncludes? J'ai essayé différentes combinaisons qui semblent logiques, mais je reçois diverses exceptions en fonction des attributs qui sont présents où. Les types qui sont en fait besoin sérialisation dans l'exemple ci-dessus serait W, X, Y, Z, seulement il y a environ 150 d'entre eux.

Peut-Protobuf net face même avec quelque chose comme ça, ou devrais-je essayer un autre type de sérialisation?

Était-ce utile?

La solution

OK; la question mise à jour, je comprends un peu plus. J'espère que les médicaments génériques au milieu du modèle d'objet sont en effet vont rendre la vie ... « fun ». Il ne fonctionne pas « hors de la boîte »; Je regardais pour voir s'il y avait quelques ajustements simples que je pourrais faire pour le soutenir, mais il a commencé à obtenir laid assez rapidement. Je pense qu'il serait préférable de supprimer tout simplement la nécessité pour le générique au milieu, si possible - peut-être en conservant une interface générique (plutôt que la classe générique). Voici un code que fait travail; comment cela correspond à votre code ... Je ne peux pas dire à 100%. Notez que vous ne devez pas utiliser les trucs de TypeDescriptor (etc) - il me semblait que, puisque vous utilisez le code-gen cela pourrait faire des choses plus faciles ...

(je n'ai pas vérifié les trucs DataSet - juste les trucs de classe)

using System;
using System.ComponentModel;
using System.Data;
using System.IO;
using NUnit.Framework;
using ProtoBuf;

[TestFixture]
public class ComplexGenericTest
{
    [Test]
    public void TestX()
    {
        Query query = new X { Result = "abc" };
        Assert.AreEqual(typeof(string), query.GetQueryType());
        Query clone = Serializer.DeepClone<Query>(query);
        Assert.IsNotNull(clone);
        Assert.AreNotSame(clone, query);
        Assert.IsInstanceOfType(query.GetType(), clone);
        Assert.AreEqual(((X)query).Result, ((X)clone).Result);
    }
    [Test]
    public void TestY()
    {
        Query query = new Y { Result = 1234};
        Assert.AreEqual(typeof(int), query.GetQueryType());
        Query clone = Serializer.DeepClone<Query>(query);
        Assert.IsNotNull(clone);
        Assert.AreNotSame(clone, query);
        Assert.IsInstanceOfType(query.GetType(), clone);
        Assert.AreEqual(((Y)query).Result, ((Y)clone).Result);
    }

}
public static class QueryExt {
    public static Type GetQueryType(this IQuery query)
    {
        if (query == null) throw new ArgumentNullException("query");
        foreach (Type type in query.GetType().GetInterfaces())
        {
            if (type.IsGenericType
                && type.GetGenericTypeDefinition() == typeof(IQuery<>))
            {
                return type.GetGenericArguments()[0];
            }
        }
        throw new ArgumentException("No typed query implemented", "query");
    }
}
public interface IQuery
{
    string Result { get; set; }
}
public interface IQuery<T> : IQuery
{
    new T Result { get; set; }
}

[ProtoInclude(21, typeof(W))]
[ProtoInclude(22, typeof(X))]
[ProtoInclude(23, typeof(Y))]
[ProtoInclude(25, typeof(SpecialQuery))]
[ProtoContract]
abstract class Query : IQuery
{
    public string Result
    {
        get { return ResultString; }
        set { ResultString = value; }
    }
    protected abstract string ResultString { get; set; }

    // these are to allow simple ResultString implementations
    // without the codegen having to worry about int.Parse etc
    protected static string FormatQueryString<T>(T value)
    {
        return TypeDescriptor.GetConverter(typeof(T))
            .ConvertToInvariantString(value);
    }
    protected static T ParseQueryString<T>(string value)
    {
        return (T) TypeDescriptor.GetConverter(typeof(T))
            .ConvertFromInvariantString(value);
    }
}
[ProtoContract]
[ProtoInclude(21, typeof(Z))]
abstract class SpecialQuery : Query, IQuery<DataSet>
{

    public new DataSet Result { get; set; }

    [ProtoMember(1)]
    protected override string ResultString
    {
        get {
            if (Result == null) return null;
            using (StringWriter sw = new StringWriter())
            {
                Result.WriteXml(sw, XmlWriteMode.WriteSchema);
                return sw.ToString();
            }
        }
        set {
            if (value == null) { Result = null; return; }
            using (StringReader sr = new StringReader(value))
            {
                DataSet ds = new DataSet();
                ds.ReadXml(sr, XmlReadMode.ReadSchema);
            }
        }
    }
}

[ProtoContract]
class W : Query, IQuery<bool>
{
    [ProtoMember(1)]
    public new bool Result { get; set; }

    protected override string ResultString
    {
        get {return FormatQueryString(Result); }
        set { Result = ParseQueryString<bool>(value); }
    }
}
[ProtoContract]
class X : Query, IQuery<string>
{
    [ProtoMember(1)]
    public new string Result { get; set; }

    protected override string ResultString
    {
        get { return Result ; }
        set { Result = value; }
    }
}
[ProtoContract]
class Y : Query, IQuery<int>
{
    [ProtoMember(1)]
    public new int Result { get; set; }

    protected override string ResultString
    {
        get { return FormatQueryString(Result); }
        set { Result = ParseQueryString<int>(value); }
    }
}
[ProtoContract]
class Z : SpecialQuery
{
}

Autres conseils

Je ne suis pas 100% sûr que je comprends le scénario que vous voulez modéliser; cependant, [ProtoInclude] semble qu'un seul niveau de l'héritage loin.

Si je comprends bien, essayez ce qui suit: Notez que vous devez connaître les types génériques potentiels au moment de la compilation au moment:

using System;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(2, typeof(Response))]
[ProtoInclude(3, typeof(Query))]
class Packet
{
    [ProtoMember(1)]
    int ID;
}
[ProtoContract]
[ProtoInclude(1, typeof(Response<int>))]
[ProtoInclude(2, typeof(Response<decimal>))]
[ProtoInclude(3, typeof(Response<string>))]
class Response : Packet
{
}
[ProtoContract]
class Response<T> : Response
{
    [ProtoMember(2)]
    public T Value;

    public override string ToString()
    {
        return typeof(T).Name + ": " + Value;
    }
}
static class Program
{
    static void Main()
    {
        Packet packet = new Response<int> { Value = 123 };
        Packet clone = Serializer.DeepClone<Packet>(packet);
        Console.WriteLine(clone.ToString()); // should be int/123
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top