2014-09-17 1 views
2

공식 C# 드라이버를 사용하여 mongodb의 비 제너릭 기본 클래스에서 상속 된 일반 클래스의 개체 목록을 저장하려고합니다.MongoDB 일반 클래스가 아닌 일반 클래스를 가진 C# 드라이버 유형 식별자가

내 코드는 다음과 같습니다

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(); 
    } 
} 

테스트, I는 컨테이너 객체를 만들고 같은, 일반 클래스의 인스턴스와 그것을 채울 때 :

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

나는이를 저장하면 DB를이 문서는 다음과 같이 추가 :

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

유형 판별 입력 얻는다 "을 MyGener "MyGenericClass'1 [System.String]"대신 "icClass'1"을 사용하면 다시 deserialize 할 수 없습니다.

또한 DB에서 이러한 개체를로드하려고하면 오류 메시지가 나타납니다. 추상 클래스의 인스턴스를 만들 수 없습니다. 그러나 유형 판별 자 (올바른 경우)는 드라이버가 MyAbstractClass 유형이지만 MyGenericClass 유형의 객체를 생성하지 않아야 함을 알 수 있어야합니다.

내 질문은 다음과 같습니다. 1.이 오류가 발생하는 이유는 무엇입니까? 2. 왜 discriminator를 올바르게 직렬화하지 않습니까?

감사합니다.

답변

4

몇 가지 실험을 마친 후에 나는 자신의 판별 자 규칙을 작성할 수 있음을 알게되었습니다. 이유를 이해할 수는 없지만 기본 discriminator 규칙은 FullName 대신 Type 클래스의 Name 속성을 사용하는 것으로 보입니다. FullName은 일반 클래스에서 쓸모 없게 만듭니다. 나는 또한의 "수 없습니다 크레타 인스턴스를 방지하기 위해, 기본 클래스가 아닌 추상적했습니다

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

함께

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; 
    } 
} 

을 그리고 등록 :

내가 대신이 코드를 사용하여 종료 추상 클래스 "오류. 추상 기본 클래스를 가질 수 있으면 좋겠지 만 파생 클래스가 일반 클래스이므로 BsonKnownTypes를 사용할 수 없습니다.

1

위의 내용은 몇 가지 사소한 변경 사항을 통해 해결되었습니다. DukeOf1Cat 도와 주셔서 대단히 감사합니다! 그리고 Jeff Rasmussen에게 감사의 말을 전합니다! 우리는 마침내 사용자 정의 serializer, 다른 RegisterClassMap 람다 등을 만들려고 시도한 후에 이것을 발견했습니다.

위 솔루션에 대한 변경 사항은 다음과 같습니다.

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의 목록이 포함 된 클래스는 직렬화되지 않을 수 각각 _t했다 일반 유형 "BasicRestriction`1"의 판별 값 :

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

은 어쩌면 조금 늦게하지만 우리는 일반적인 유형과 같은 문제가 있었다, 그들은 형식으로 저장된 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); 
      } 
      );