vendredi 3 juillet 2015

MongoDB with C# driver 2.0 is frozen

I am running a C# app with MongoDB 2.0 driver and ran into the following error when I ran all my tests:

System.InvalidOperationException : Class map for <TopClassName> has been frozen and no further changes are allowed.

System.AggregateException : One or more errors occurred.  ----> MongoDB.Bson.BsonException : Unable to find a matching member to provide the value for parameter 'inBed'

However, running every single test one at a time gives no errors. So for some reason the class map is frozen...

Here is what I have (P.S, if you spot something that is way off, let me know):

A hierarchy with 3 abstract classes and one concrete class:

public abstract class BaseEvent
{
    [BsonId] 
    public Guid Id;
    public List<int> Data;
    public dynamic Condition;
    public string TimeStamp;

    [BsonConstructor]
    protected BaseEvent(List<int> data, dynamic condition)
    {
        Data = data;
        Condition = condition;
        TimeStamp = DateTime.Now.ToString();
        Id = Guid.NewGuid();
    }
}

public abstract class Basic : BaseEvent
{
    [BsonConstructor]
    protected Basic(List<int> data, bool condition) : base(data, condition)
    {}
}

public abstract class BedEvent : Basic
{
    [BsonConstructor]
    protected BedEvent(List<int> data, bool inBed) : base(data, inBed)
    {}
}

public class DummyBed : BedEvent
{
    [BsonConstructor]
    public DummyBed(List<int> data, bool inBed) : base(data, inBed)
    {
        RegisterHelper.RegisterNewClass(this);
    }
}

The RegisterHelper is a singleton that is kept alive, more on that a bit down. The constructor of my app will register the top class BaseEvent:

var baseEvent = BsonClassMap.RegisterClassMap<BaseEvent>(cm =>
{
    cm.SetIsRootClass(true);
    cm.MapMember(c => c.Data);
    cm.MapMember(c => c.Condition);
    cm.MapMember(c => c.TimeStamp);
    cm.MapIdMember(c => c.Id).SetIdGenerator(GuidGenerator.Instance);
});

RegisterHelper.ClassRegister.Add(baseEvent);

For each concrete class, as the DummyBed, I call a homebrewed (and likely to be the cause of the problem) register method.

This method looks at all the baseclasses and, if not found on a list, adds them recursively to all classes above them self, much like this:

public static void RegisterNewClass<T>(T theObject)
{
    ...
    if (!lvl3Found)
    {
        var lvl3Map = new BsonClassMap(lvl3Type); //lvl3Type is a System.Type
        lvl3Map.SetDiscriminator(lvl3Type.Name);
        ClassRegister.Add(lvl3Map);
        BsonClassMap.RegisterClassMap(lvl3Map);
        lvl4Map.AddKnownType(lvl3Type);
    }
    ...
}

Running tests:

[TestFixture]
public class InsertEventIntoDatabaseTest
{
    private EventDatabase _eventDatabase;

    [SetUp]
    public void Setup()
    {
        _eventDatabase = new EventDatabase();
        _eventDatabase.EmptyDatabase(); //Clean slate each time
    }

    [TearDown]
    public void TearDown()
    {
        _eventDatabase = null;
    }

    [Test]
    public void GetSubTypeDocument_FindDummyBed_Success()
    {
        var bed = new DummyBed(new List<int>() { 1, 2, 3}, true);

        _eventDatabase.InsertEventInDatabase(bed);
        var doc = _eventDatabase.GetDocument();

        _eventDatabase.GetSubTypeDocument(typeof(DummyBed));

        Assert.That(doc, Is.TypeOf<DummyBed>()); //Great success
    }

    [Test]
    public void FindTypeEvents_FindTwo_Succes()
    {
        var data = new List<int>() { 1, 2, 3 };
        var bed1 = new DummyBed(data, true);
        var bed2 = new DummyBed(data, true);

        _eventDatabase.InsertEventInDatabase(bed1);
        _eventDatabase.InsertEventInDatabase(bed2);

        var foundEvents = _eventDatabase.FindTypeEvents(typeof(BedEvent));
        Assert.That(foundEvents.Count, Is.EqualTo(2)); // Frozen
    }
    ...
}

However - running each test by it self gives only green check marks. So it is down to how fast it can handle events.

The database is cleared each time a new test is run, so there should be no operations running.

The methods called should wait until they finish:

public void InsertEventInDatabase(BaseEvent inputBaseEvent)
{
    inputBaseEvent.Condition = (inputBaseEvent.Condition is bool ? (inputBaseEvent.Condition == true ? 100 : 0) : inputBaseEvent.Condition);

    var collection = _database.GetCollection<dynamic>(DatabaseCollection);
    collection.InsertOneAsync(inputBaseEvent).Wait(); //Should wait, right?
}

public List<BaseEvent> FindTypeEvents(Type typeFilter)
{
    var name = _database.GetCollection<BaseEvent>(DatabaseCollection)
        .Find(x => x.Id != Guid.Empty)
        .ToListAsync();

    return name.Result; //Should wait here as well, right?
}

Any suggestions on where to look for the cause of the problem? It is quite limit with information on the interwebs after the April update of the C# driver, so any suggestions are welcome.

Aucun commentaire:

Enregistrer un commentaire