簡體   English   中英

MongoDB C#驅動程序類型鑒別器,泛型類繼承自非泛型基類

[英]MongoDB C# driver type discriminators with generic class inheriting from non-generic base class

我正在嘗試使用官方C#驅動程序存儲從mongodb中的非泛型基類繼承的泛型類的對象列表。

我的代碼看起來像這樣:

abstract class MyAbstractClass {}

class MyGenericClass<T>: MyAbstractClass
{
    [BsonRequired]
    public List<T> Values = new List<T>();

    public MyGenericClass(IEnumerable<T> values) 
    {
        Values.AddRange(values);
    }
}

class MyContainerClass
{
    [BsonId]
    public string Id;

    [BsonRequired]
    public List<MyAbstractClass> ValueCollections = new List<MyAbstractClass>();

    public MyContainerClass()
    {
        Id = Guid.NewGuid().ToString();
    }
}

在測試時,我創建了一個容器對象並用泛型類的實例填充它,如下所示:

var container = new MyContainerClass();
container.ValueCollections.Add(new MyGenericClass<string>(new[]{"foo","bar","baz"}));

當我將其保存到數據庫時,添加的文檔如下所示:

{
"_id": "c5cf5cd1-843f-4d5d-ba8f-5859ae62fd1d",
"ValueCollections": [
    {
        "_t": "MyGenericClass`1",
        "Values": [
            "foo",
            "bar",
            "baz"
        ]
    }
]
}

類型鑒別器獲取類型“MyGenericClass'1”而不是“MyGenericClass'1 [System.String]”,這意味着它不能再次反序列化。

此外,當嘗試從數據庫加載這些對象時,我收到一條錯誤消息:無法創建抽象類的實例。 但是類型鑒別器(如果它是正確的)應該允許驅動程序看到它不應該創建MyAbstractClass類型的對象但是MyGenericClass

所以我的問題是:1。為什么我會收到此錯誤? 2.為什么不正確地序列化鑒別器?

感謝您的時間。

經過一些實驗,我發現你可以編寫自己的鑒別器約定。 我無法理解為什么,但默認的鑒別器約定似乎使用類型類的Name屬性,而不是FullName,這使得它對泛型類沒用。

我最終使用此代碼:

class FooDiscriminatorConvention : IDiscriminatorConvention
{
    public string ElementName
    {
        get { return "_t"; }
    }

    public Type GetActualType(MongoDB.Bson.IO.BsonReader bsonReader, Type nominalType)
    {
        if(nominalType!=typeof(MyAbstractClass))
            throw new Exception("Cannot use FooDiscriminator for type " + nominalType);

        var ret = nominalType;

        var bookmark = bsonReader.GetBookmark();
        bsonReader.ReadStartDocument();
        if (bsonReader.FindElement(ElementName))
        {
            var value = bsonReader.ReadString();

            ret = Type.GetType(value);

            if(ret==null)
                throw new Exception("Could not find type " + value);

            if(!ret.IsSubclassOf(typeof(MyAbstractClass)))
                throw new Exception("Database type does not inherit from MyAbstractClass.");
        }

        bsonReader.ReturnToBookmark(bookmark);

        return ret;
    }

    public BsonValue GetDiscriminator(Type nominalType, Type actualType)
    {
        if (nominalType != typeof(MyAbstractClass))
            throw new Exception("Cannot use FooDiscriminator for type " + nominalType);

        return actualType.FullName;
    }
}

並注冊

BsonSerializer.RegisterDiscriminatorConvention(typeof(MyGenericClass<>), new FooDiscriminatorConvention()); //is this needed?
BsonSerializer.RegisterDiscriminatorConvention(typeof(MyAbstractClass), new FooDiscriminatorConvention());

我還必須使基類非抽象,以避免“無法克里特實例的抽象類”錯誤。 能夠擁有一個抽象基類會很好,但由於派生類是通用的,我不能使用BsonKnownTypes。

也許它有點晚了但我們遇到了與泛型類型相同的問題,它們被保存為Name類型而不是FullName。 我們最初使用BsonSerializer.RegisterDiscriminator為我們項目中的所有類型,甚至使所有類型都通用,但經過大量的測試和失敗后我們最終得到了BsonClassMap

非工作方法......

      KnownPayloadTypes
            .Concat(KnownMessageTypes)
            .ConcatOne(typeof(object))
            .ConcatOne(typeof(Message))
            .ForEach(t => BsonSerializer.RegisterDiscriminator(t, t.FullName));

工作...

    private static readonly IEnumerable<Type> KnownMessageTypes = KnownPayloadTypes
        .Select(t => typeof(Message<>).MakeGenericType(t));

      KnownPayloadTypes
            .Concat(KnownMessageTypes)
            .ConcatOne(typeof(object))
            .ConcatOne(typeof(Message))
            .ForEach(t =>
            {
                var bsonClassMap = new BsonClassMap(t);
                bsonClassMap.AutoMap();
                bsonClassMap.SetDiscriminator(t.FullName);
                BsonClassMap.RegisterClassMap(bsonClassMap);
            }
            );

上面的工作對我們來說有一些小改動。 非常感謝你的幫助DukeOf1Cat! 並感謝傑夫拉斯穆森幫助我們解決這個問題! 我們在嘗試創建自定義序列化程序,不同的RegisterClassMap lambda等之后終於找到了這個。

我們對上述解決方案的更改就在這里。

class IRestrictionDiscriminatorConvention : IDiscriminatorConvention
{
    public string ElementName
    {
        get { return "_t"; }
    }

    public Type GetActualType(BsonReader bsonReader, Type nominalType)
    {
        //Edit: added additional check for list
        if (nominalType != typeof(IRestriction) && nominalType != typeof(List<IRestriction>))
            throw new Exception("Cannot use IRestrictionDiscriminatorConvention for type " + nominalType);

        var ret = nominalType;

        var bookmark = bsonReader.GetBookmark();
        bsonReader.ReadStartDocument();
        if (bsonReader.FindElement(ElementName))
        {
            var value = bsonReader.ReadString();

            ret = Type.GetType(value);

            if (ret == null)
                throw new Exception("Could not find type from " + value);
            //Edit: doing the checking a little different
            if (!typeof(IRestriction).IsAssignableFrom(ret) && !ret.IsSubclassOf(typeof(IRestriction)))
                throw new Exception("type is not an IRestriction");
        }

        bsonReader.ReturnToBookmark(bookmark);

        return ret;
    }

    public BsonValue GetDiscriminator(Type nominalType, Type actualType)
    {
        if (nominalType != typeof(IRestriction) && nominalType != typeof(List<IRestriction>))
            throw new Exception("Cannot use FooDiscriminator for type " + nominalType);

        /*Edit: had to change this because we were getting Unknown discriminator value 
       'PackNet.Common.Interfaces.RescrictionsAndCapabilities.BasicRestriction`1[[System.String, ... */
        return actualType.AssemblyQualifiedName;
    }
}

我們遇到問題的課程是這樣的:

public class BasicRestriction<T> : IRestriction
{
    public T Value { get; set; }

    public BasicRestriction()
    {
    }
    public BasicRestriction(T value)
    {
        Value = value;
    }
}

並且包含Irestrictions列表的類永遠不會被反序列化,因為當它到達I限制列表時,每個都有一個_t鑒別器值“BasicRestriction`1”用於泛型類型:

public class Carton{ public List<IRestriction> Restrictions { get; set; }}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM