BsonElement attribute and custom deserialization logic with MongoDB C# driver

巧了我就是萌 提交于 2019-12-25 07:07:23

问题


Consider the following example:

    public class Foo
    {
        private string _text;

        [BsonElement("text"), BsonRequired]
        public string Text
        {
            get { return _text; }
            set
            {
                _text = value;
                Bar(_text);
            }
        }

        private void Bar(string text)
        {
            //Only relevant when Text is set by the user of the class,
            //not during deserialization
        }
    }

The setter of the Text property and, subsequently, the method Bar are called both when the user of the class assigns a new value to the property and during object deserialization by the MongoDB C# driver. What I need is to ensure that Bar is called only when the Text property is set by the user and not during deserialization.

I see two solutions which don't really suit me:

The first is to move the BsonElement attribute to the backing field. However, as far as I know, the BsonElement attribute is used in query building by the MongoDB C# driver, so I will lose the ability to use the Text property in queries.

The second solution is to make the Text setter private and add a method through which the user of the class will set the Text property, and in which the Bar method would be called. However, the Text setter is used very often in the existing solution, and I'm a bit reluctant to change 70+ calls across all files. Plus, the code will become less readable.

Is there any cleaner way to separate deserialization and user-prompted property change while retaining the BsonElement attribute on the property?


回答1:


Why not create a seperate property for the users and for the DB for the same private variable, something like this,

public class Foo
{
    private string _text;

    [BsonElement("text"), BsonRequired]
    public string TextDB
    {
        get { return _text; }
        set
        {
            _text = value;
        }
    }

    [BsonIgnore]
    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            Bar(_text);
        }
    }

    private void Bar(string text)
    {
        //Only relevant when Text is set by the user of the class,
        //not during deserialization
    }
}



回答2:


You can use a little trick an implement a kind of property listener.

The usage would be:

// Working with some foo here...
var foo = new Foo();
foo.Text = "Won't fire anything";

using (var propertyListener = new FooPropertiesListener(foo))
{
    foo.Text = "Something will fire you listener";
}

// Some more work with foo here...
foo.Text = "Won't fire anything";

And the implementation behind it, something like:

FooPropertiesListener

public class FooPropertiesListener : IDisposable
{
    private readonly Foo Foo;

    public FooPropertiesListener(Foo foo)
    {
        this.Foo = foo;
        this.Foo.PropertiesListener = this;
    }

    public void Bar(string text)
    {
        //Only relevant when Text is set by the user of the class,
        //not during deserialization
    }

    public void Dispose()
    {
        Foo.PropertiesListener = null;
    }
}

Foo

public class Foo
{
    internal FooPropertiesListener PropertiesListener;

    private string _text;

    [BsonElement("text"), BsonRequired]
    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            if (PropertiesListener != null)
            {
                PropertiesListener.Bar(_text);
            }
        }
    }
}



回答3:


I know this question is old, but I'd still like to help for other people stumbling on this issue as I have done.

It basically boils down to something very simple: serialization and deserialization are not limited to public fields and properties!

The next example will cover the original question without having to invent dubious secondary properties:

public class Foo
{
    [BsonElement("Text"), BsonRequired]
    private string _text;

    [BsonIgnore]
    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            Bar(_text);
        }
    }

    private void Bar(string text)
    {
        //Only relevant when Text is set by the user of the class,
        //not during deserialization
    }
}

Simply put your BsonElement class on the backing field and tell it to BsonIgnore the property. You can do whatever you like in the getter and setter without having to worry about deserialization which now occurs on private field level.

Hope this helps somebody!



来源:https://stackoverflow.com/questions/31406063/bsonelement-attribute-and-custom-deserialization-logic-with-mongodb-c-sharp-driv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!