5

I understand that I must have public read and write properties on my class for MongoDB driver to serialize/deserialize my objects. however I want to know whether there is method/preferred method for hiding the write properties from the rest of my code?

eg.

class Product
{
    private List<Release> releases;

    public List<Release> Releases
    {
        get
        {
            return new List<Release>(releases); //I can protect 'releases' when reading by passing a copy of it
        }
        set
        {
            releases = value; //BUT how can I protect release when writing?
        }
    }
}

I want MongoDB to be able to serialize/deserialize my types but I don't want the rest of my code to be able to overwrite it's fields / properties that should otherwise have been private. Is there a pattern to handle this? I have thought about having a separate ProductDoc class which is just used as a intermediary for getting Product objects into and out of MongoDB, but I'm not sure whether there is a better solution to this.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Daniel Robinson
  • 13,806
  • 18
  • 64
  • 112

3 Answers3

5

I have not worked with mongo for a long time for now. But you may try to read this thread MongoDb Map Setters or try to make your setter protected like this:

public List<Release> Releases
{
    get
    {
        return new List<Release>(releases); //I can protect 'releases' when reading by passing a copy of it
    }
    protected set
    {
        releases = value; //BUT how can I protect release when writing?
    }
}
Community
  • 1
  • 1
petro.sidlovskyy
  • 5,075
  • 1
  • 25
  • 29
  • even with protected and private setter mongo driver will be able to serialize and deserialize my class ? I only ask because the mongo docs says that to be serializable it must have public getter and setter properties and a no argument public constructor. – Daniel Robinson Sep 04 '12 at 14:28
  • 1
    It works. Test applications are always a good way to prove out an api. – Craig Wilson Sep 06 '12 at 15:36
  • This solution kinda works but it also introduces a potential undetectable build issue. It is true that by making the SET private or protected MongoDB can still serialize and deserialize the object and you can't build if you attempt to set the value of the property but if you do something like .Add(new Release{}) not only will your project build but you will modify the collection. – TGardner Oct 10 '13 at 17:57
2

The best answer on this page currently has some flaws which are important to understand.

As the solution is currently written:

public List<Release> Releases
{
    get
    {
        return new List<Release>(releases); //I can protect 'releases' when reading by passing a copy of it
    }
    protected set
    {
        releases = value; //BUT how can I protect release when writing?
    }
}

This doesn't work as written.

obj.Releases.Add(new Release());

Would affect the underlying collection by adding a new release to it. Which is in direct contradiction to the stated goal of making the set routine private.

However if you change the exposed property type to implement IEnumerable instead of List and return a ReadOnly version of the list. Such as...

public IEnumerable<Release> Releases
{
    get
    {
        return new List<Release>(releases).AsReadOnly();
    }
    protected set
    {
        releases = value;
    }
}

Then both

obj.Releases.Add(new Release());

and

obj.Releases = new List<Release>();

will both throw build errors and prevent modifying the underlying collection.

TGardner
  • 186
  • 3
  • 8
2

Another approach, if your properties are set by the constructor is to keep them read only and to use MapCreator to tell MongoDB how to create an instance of your class passing in the properties you want to set.

e.g. I have a class called Time with three readonly properties: Hour, Minute and Second and a public constructor that takes an hour, a minute and a second value.

Here's how I get MongoDB to store those three values in the database and to construct new Time objects during deserialization.

BsonClassMap.RegisterClassMap<Time>(cm =>
{
    cm.AutoMap();
    cm.MapCreator(p => new Time(p.Hour, p.Minute, p.Second));
    cm.MapProperty(p => p.Hour);
    cm.MapProperty(p => p.Minute);
    cm.MapProperty(p => p.Second);
}
Ian Mercer
  • 38,490
  • 8
  • 97
  • 133