A null reference exception occurs on the dsl object''s simplemonikerresolver dispose method
Hi,
A null reference exception occurs on the dsl object's simplemonikerresolver dispose method in a particular situation. I try to save a dsl model element on an object oriented database (db4o). The reflection mechanisms used by this product seems to disturb the resolution of references between dsl model elements.
I have the following code:
Code Snippet
// dslModel
Sample.RentalDsl.RentalModel rentalModel;
// accessDb4o
Db4objects.Db4o.IObjectContainer db = Db4objects.Db4o.Db4oFactory.OpenFile(Program.DbFileName);
try
{
// loadDsl
Microsoft.VisualStudio.Modeling.Store newStore = new Microsoft.VisualStudio.Modeling.Store();
Type[] metaTypes = new Type[] {
typeof(Microsoft.VisualStudio.Modeling.Diagrams.CoreDesignSurfaceDomainModel),
typeof(Sample.RentalDsl.RentalDslDomainModel)};
newStore.LoadDomainModels(metaTypes);
using (Microsoft.VisualStudio.Modeling.Transaction t = newStore.TransactionManager.BeginTransaction("Loading Model"))
{
rentalModel = Sample.RentalDsl.RentalDslSerializationHelper.Instance.LoadModel(newStore, Program.ModelFileName, null, null);
t.Commit();
}
if (rentalModel.Persons.Count < 1)
{
Console.WriteLine("The model doesn't have any person element");
}
else
{
//savePersonElementByUsingDb4o
db.Set(rentalModel.Persons[0]); // The NullReferenceException occurs when executing this line.
}
}
finally
{
db.Close();
}
RentalModel.Persons contains 2 elements.
The line db.Set(rentalModel.Persons[0]); raise a null reference exception as described bellow:
MESSAGE:
Object reference not set to an instance of an object.
SOURCE:
Microsoft.VisualStudio.Modeling.Sdk
STACK TRACE:
at Microsoft.VisualStudio.Modeling.SimpleMonikerResolver.Dispose(Boolean disposing)
at Microsoft.VisualStudio.Modeling.SimpleMonikerResolver.Finalize()
Does anyone experienced the same issue (on any other situation) ?
Best regards,
Raskal
[2240 byte] By [
raskal] at [2008-1-9]
Aside from this post, I have also filed a bug on Microsoft Connect where I mentioned that this issue was found in a VS 2005 environment. Microsoft since then reproduced the issue and let me know that it also occurs in a VS 2008 B2 environment.
I note that your transaction is not a serialization transaction. I don't know if this will cause your problem, but if you pass true as the second parameter to the transaction constructor, you will mark it as a serialization transaction - this allows links to exist in a partially instantiated state. -- Steve
Steve,
I tested your suggestion without success.
After a bit of investigation, it turns out that the portion of code that raises the null reference exception is due to the fact that the store field of the SimpleMonikerResolver class is not tested to ensure that it is not null before accessing its Disposed property:
SimpleMonikerResolver.Dispose
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing && !this.store.Disposed) <== BREAKS HERE
{
this.store.EventManagerDirectory.DeserializationBeginning -= new EventHandler(this.OnDeserializationBeginning);
this.store.EventManagerDirectory.DeserializationEnding -= new EventHandler(this.OnDeserializationEnding);
this.store.StoreDisposing -= new EventHandler(this.OnStoreDisposing);
}
this.lookupTable.Clear();
this.newElements.Clear();
this.topLevelSerializationTransactionMarker = null;
this.store = null;
this.disposed = true;
}
}
The issue seems to be due to db4o that uses weak references during its reflection process thus triggering the GC that then calls the Finalize which in turns calls the Dispose method.
We manage to workaround this issue by implementing our custom moniker(implementing IMonikerResolver and IDisposable) and reference them into the store as described bellow:
Adding custom MonikerResolver
newStore.AddMonikerResolver(CoreDesignSurfaceDomainModel.DomainModelId, new MyMonikerResolver());
newStore.AddMonikerResolver(CoreDomainModel.DomainModelId, new MyMonikerResolver());
...
The idea behind all this is to be able to save a DSL model somewhere else than just in a file. Saving it in a file makes perfect sense in a development environment but raises issues when operating in a supervision context where the model can be modified based on external data.
We cannot rely on a SaveModel serialization mecanism because of its cost for each modification (we are not in our case in the situation of saving a file from within Visual Studio). Have you any suggestion ? Two solutions are studied : object database (db4o) and xml database (based on SQL Server's xml features)
Regards,
Pascal.
You can listen to store changes by registering events, and in the event handlers you could store data externally. Would that help?
-- Steve
Steve,
We're glad that you suggested this solution as that was one of the options that we wanted to evaluate, i.e. test xml database solution : subscribe to store events (Store.EventManagerDirectory.ElementAdded, ...) and save model's modifications via sqlxml within Sql Server 2005. We will test this solution today and let you know our findings.
Regarding object database solution(db4o), unfortunately, if we use C#, we can't benefit from the SimpleMonikerResolver class's logic. Indeed, a "feature" of the C# destructor is that it always calls the base class Finalize method that raises currently an exception.
So, either we select VB to break the Finalize method inheritance (this a great promotion for VB ;o) ), either we can make our own SimpleMonikerResolver implementation but baring in mind that we might not have access to all the classes required to match SimpleMonikerResolver implementation. But, of course, we hope that MS qualifies this issue as a bug.
Pascal
I debugged through the repro you posted with your bug, and it appears that db4o is creating unitialized instances of objects while traversing the object graph to serialize it. It does this through System.Runtime.Serialization.FormatterServices.GetUninitializedObject() and then discards these instances in an uninitialized state without suppressing finalization on them. This is the only way that the null reference exception could occur because the field it occurs on is only assigned in the constructor and so could not be null through normal code paths. This looks to me like a bug in db4o - it shouldn't be creating uninitialized instances and then letting them be finalized if it has not initialized them so that they are in a valid state. It seems to be doing this just to test create objects, so judicious use of GC.SuppressFinalize() on the tests instances would not only avoid this problem but also avoid causing unecessary finalization load on the garbage collector. Anyway as it is currently implemented it causes finalizers to be executed on uninitialized objects, which explains the the null-ref exception that occurs the SimpleMonikerResolver.Dispose(bool).
Just in case that is not clear, here is a litle console pogram that demonstrates what is happening:
Code Snippet
using
System; using
System.Collections.Generic; class
Wibble {
Dictionary<string,int> dummy = new Dictionary<string,int>(); ~Wibble() { dummy.Clear(); }
}
class
Program {
static void Main(string[] args) {
object o = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(Wibble)); // GC.SuppressFinalize(o); // Uncomment me to prevent crash o =
null; GC.Collect(); GC.WaitForPendingFinalizers(); // Bang! }
}
Frankly, however, I don't think this worth pursuing. IMS model elements reference the IMS store in which they belong, so you'll end up saving out the entire store into your ODB, which is (I assume) why the moniker resolvers are being reached by db4o's object graph traversal, since these are reachable in various ways from the Store, and are thus reachable from any model element as well. Somehow I doubt that a reconstituting the entire IMS and everything reachable from it will be successful.
Regards
Blair
Hi Blair,
Thanks for your detailled feedback and great job.
It is true that saving the entire store is not the best way to go, this solution had been planned before a detailed knowledge of dsl object graph (and store's existence). In this regard, I am investigating another solution where the modifications detected thanks to subscribing to the store's events are stored as xml data into Sql Server as Steve mentioned in a previous post.
Anyway, I will follow up with db4o to let them know about your finding, I contacted them in relation to this issue and they thought it was a DSL problem. With your feedback, they will probably identify flaws in their API
Best regards,
Pascal
Hi,
As a follow up and to conclude this thread, the solution based on subscribing to the store events and combining with a Sql Server database and xml data have proved to be the most effective way to achieve the functionalities we want to implement.
We are to able to keep our database based repository in sync with our DSL model. Thanks to anyone who contributed to the thread.
Pascal