DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Coding
  3. Languages
  4. Circular Reference with Mongo Appender can Crash Your Process

Circular Reference with Mongo Appender can Crash Your Process

Ricci Gian Maria user avatar by
Ricci Gian Maria
·
Jun. 07, 12 · Interview
Like (0)
Save
Tweet
Share
5.74K Views

Join the DZone community and get the full member experience.

Join For Free

I’ve blogged some days ago on the possibility to save log4net logs inside a Mongo database, but you should be aware that this technique can be dangerous if your objects have circular references. A circular reference happens when object A reference object B and object B directly or indirectly reference object A again and this is a high risk when you work with Mongo Serializer.

Mongo Serializer does not likes circular references (it is perfectly acceptable, because documents with circular references cannot be saved into a document database), but the problem is: if you try to serialize an object that has a circular reference you will get a StackOverflowException and your process will crash, as stated in official documentation from MSDN

Starting with the .NET Framework version 2.0, a StackOverflowException object cannot be caught by a try-catch block and the corresponding process is terminated by default. Consequently, users are advised to write their code to detect and prevent a stack overflow.

If you remember how I modified MongoDb log4net appender, I decided to save into MongoDB complex objects with this code:

if (compositeProperties != null && compositeProperties.Count > 0)
{
    var properties = new BsonDocument();
    foreach (DictionaryEntry entry in compositeProperties)
    {
        BsonValue value;
        if (!BsonTypeMapper.TryMapToBsonValue(entry.Value, out value))
        {
            properties[entry.Key.ToString()] = entry.Value.ToBsonDocument();
        }
        else
        {
            properties[entry.Key.ToString()] = value;
        }
    }
    toReturn["customproperties"] = properties;
}

The key point is in entry.Value.ToBsonDocument(), because if someone store in log4Net global context an object that contains a circular reference, your program will be terminated the next call to log4net, because the StackOverflowException could not be caught.

This is especially annoying when you want to store in your log object that comes from Database with an ORM like NHibernate, because every object that has a Bag reference, usually get hydrated with a PersistentBag, an internal class by nhibernate, that has a circular reference. A simple solution to this process is telling MONGO which serializer to use for such a specific types.

The technique is simple, Mongo drivers provide the ability to register custom Serialization provider easily

BsonSerializer.RegisterSerializationProvider(new LoggerBsonSerializerProvider());

And this is the code of the class that implements the ISerializationProvider interface

public class LoggerBsonSerializerProvider : IBsonSerializationProvider
{
    public IBsonSerializer GetSerializer(Type type)
    {
        if (type.FullName.Contains("Nhibernate", StringComparison.OrdinalIgnoreCase))
        {
               return BsonNullSerializer.Instance;
        }
        return null;
    }
}

The only function you need to implement is the GetSerializer, in this simple example, for all types that contains NHibernate string in it, simply return a BsonNullSerializer. That basically tells Mongo Serializer to ignore that types. This is in my opinion the best approach because it avoids the risk of serializing NHibernate internal classes that actually can throw a StackOverflowException. If you want to serialize NHibernate PersistentGenericBag but you do not want to risk a circular reference you can use this code instead.

public class LoggerBsonSerializerProvider : IBsonSerializationProvider
{
    public IBsonSerializer GetSerializer(Type type)
    {
        if (type.FullName.Contains("Nhibernate", StringComparison.OrdinalIgnoreCase))
        {
            if (type.TypeImplementsGenericInterface(typeof(IList<>)))
            {
                    return EnumerableSerializer.Instance;
            }
            return BsonNullSerializer.Instance;
        }
        return null;
    }
}
 
public static Boolean TypeImplementsGenericInterface(this Type typeToCheck, Type interfaceToLookFor)
{
    return typeToCheck.GetInterface(interfaceToLookFor.Name) != null;
}

The main difference is:, for each NHibernate internal type that implement a generic IList<> I tell Mongo to serialize using the EnumerableSerializer, this kind of serializer avoid the circular reference problem, because the PersistentGenericBag is handled as a IList<> ignoring its real properties. This approach is still not safe, because you need to be sure that the collection was already loaded from database or the object is not detached, to avoid an exception during logging because the collection cannot be initialized. This type of exception is catchable, so it can be a minor issue because you can handle it with a simple try catch inside the Mongo Appender.

Object (computer science) Crash (computing)

Published at DZone with permission of Ricci Gian Maria, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 7 Most Sought-After Front-End Frameworks for Web Developers
  • How To Build an Effective CI/CD Pipeline
  • Use Golang for Data Processing With Amazon Kinesis and AWS Lambda
  • Distributed Tracing: A Full Guide

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: