Mongo .net client schema evolution error - c#

i am using mongo .net client and using the collection to objects features. issue i have come across in schema evolution
when i rename a field in my class for example change field name from Comment to Comments and i make this change in my class, i get an exception from Mongo when i perform a fetch.
my expectation is that mongo client will ignore fields that exists in the collection but doesnt exists in my .net class.
will be happy if its possible without doing the transformation between bson and .net class.

If you want to continue to use the old name you could use the BsonElement attribute:
class Demo {
[BsonElement("Comment")]
public string Comments { get; set; }
}
Using that syntax would tell the MongoDB C# driver to find the data for the Comments property/field in a field in the document in a field named Comment. That would mean that you don't need to worry about moving/copying the data from the old location. It's often used so that you can use longer friendly names in source code, while minimizing the actual BSON Document size (as the full property name is always stored in the document in the database collection). When shortening, you might for example just use:
[BsonElement("c")]
public string Comments { get; set; }
Some of the MongoDB drivers don't have this functionality (and I wish they did!).
Secondly, you could also just add a special attribute to your class ignore all unknown elements for the class and not thrown an exception:
[BsonIgnoreExtraElements]
public Demo {
public string Comments { get; set; }
}
Then, if a field named Comment is found, but can't be matched to a property of your C# class, it will be ignored. I'll often use this during development as the schema changes, but then remove it later so that I can catch unexpected fields.
Or you can also use the BsonClassMap to make similar changes:
BsonClassMap.RegisterClassMap<Demo>(cm => {
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
There are even some more options documented here if you want complete control.

Related

How to tell MessageQueue.SendMessageConnection how to "XMLize" the object to be sent?

I'm working in C# with a System.Messaging.MessageQueue.SendMessageConnection for sending an object, containing some parameters, something like:
_sendQueue.Send(myObject, ...);
My myObject is an object, containing some attributes, like Field1.
I'm checking how my messages get sent, using:
Computer Management
Services and Applications
Message Queuing
Private Queues
open the right queue, and in the "queue messages", right-click and check "Properties", "Body".
There I see tags like:
<Field1>content_Field1</Field1>
Instead of this, I would like to see something like:
<F1>content_Field1</F1>
Is there an easy mapping between the attributes in my object and the XML tags I would like to be used?
Thanks in advance
That's actually quite easy to do. Check out Control XML serialization using attributes :
By default, an XML element name is determined by the class or member name. In a simple class named Book, a field named ISBN will produce an XML element tag , as shown in the following example.
public class Book
{
public string ISBN;
}
// When an instance of the Book class is serialized, it might
// produce this XML:
// <ISBN>1234567890</ISBN>.
This default behavior can be changed if you want to give the element a new name. The following code shows how an attribute enables this by setting the ElementName property of a XmlElementAttribute.
public class TaxRates {
[XmlElement(ElementName = "TaxRate")]
public decimal ReturnTaxRate;
}
- Microsoft article as of 2017-03-30, various authors (emphasis by me)
The whole article is about a ~6minutes read and I really recommend it.

How to update all document fields except specified ones in mongodb

I present a simple model:
public class UserDocument
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string DisplayName { get; set; }
public List<string> Friends { get; set; }
}
I am using the latest C# driver which has the ability to replace a document using a C# object which will automatically update all its fields. Problem is I want to update all fields except for the user friends, because it's a field containing the object relations to other documents. Of course I can manually update each field of the ones I want to get updated, which here are just two.
But this example is simple just to make my point. In reality the fields are much more and it would be harder to update each field. That would require a single line for each one to use the Set operator. Also, newly-added fields would have to be supported in the same way as opposed to updating to automatically just works.
Is there a way to achieve that - automatically update all fields with just specifying a list of excluded fields?
There is no way, using the provided builders to have a "blacklist" update which excludes only specific fields.
You can query the old document, copy the old values of these fields to the new instance and then replace it entirely in the database.
You can also generate such an update command by iterating over the fields using reflection.
But the MongoDB driver doesn't offer such a query built in.
I figured out a way to do this with MongoDB using Javascript/NodeJS, but maybe the logic can translate to C#?
I wanted to update all fields without having to actually explicitly state them (all fields except for one, it turned out).
Attempted update of all document fields:
await examCollection.findOneAndUpdate(
{_id: new ObjectID(this.examId)},
{$set: this.data}
)
...except, this.data happened to have _id in it as well, which I didn't want to update. (In fact, it gave me an error, because _id is immutable.)
So, for my workaround, I ended up "deleting" all fields on the object that I didn't want to update (i.e. _id).
Successful update of all non-specified document fields:
// (1) specify fields that I don't want updated (aka get rid of them from object) (similar option in C#?)
delete this.data._id
//delete this.data.anotherField
//delete this.data.anotherField2
//delete this.data.anotherField3
// (2) update MongoDB document
await examCollection.findOneAndUpdate(
{_id: new ObjectID(this.examId)},
{$set: this.data}
)
This was much easier than explicitly stating all the fields I did want to update, because there were A LOT, and they could potentially change in the future (new fields added, fields deleted, etc.).
Hopefully this strategy can help!
Note: In reality, I did my "field specifying" earlier in another file, rather than immediately before updating like it shows in the example, but same effect.

An easy way to validate an XML against a C# Class

I use the XML format to keep settings for my C# project.
Theses XMLs are deserialized into C# classes.
Often enough, a class design changes but I forget to update the XML. Deserialization usually works fine and missing elements just get their default values.
This behavior is not desirable for me. I would like to have automatic validation that asserts the class and the XML have exactly the same structure.
I know I can create XML schemas (e.g using XSD) for my classes, but I could not figure an easy way to do it automatically and not manually (recall my classes' design changes often). Also, this solution seems kind of unnatural. I do not really need XML schemas. I have classes and their serialized XML instances. Adding schemas seems superfluous.
Thanks a bunch.
Why you don't create a method in your settings class that can be invoked after xml deserialization?
Suppose you have a class like this:
class Settings {
public string Property1 { get; set; }
public int Property2 { get; set; }
public bool IsValid() {
if(string.IsNullOrEmpty(Property1)) return false;
if(Property2 == 0) return false;
}
}
Using IsValid you can check everything in your class. Please, remember that this is an example. I think is good to manage object validation. If you change something in the time, you can edit the validation method to check new situations.
To go with Roberto's idea and take it a step further you could get all the properties via reflection:
var props = yourClass.GetType().GetProperties()
Inside of your validation function you could loop over those properties with:
foreach(var prop in props) // or foreach(var prop in yourClass.GetType().GetProperties())
{
//...Validation of property
}
If one of the properties has its standard-value you throw a custom exception that tells you you did not update your XML-file properly.
You can implement this using Version Tolerant Serialization (VTS) https://msdn.microsoft.com/en-us/library/ms229752%28v=vs.110%29.aspx
The Serialization Callbacks is what you are looking for in the VTS capabilities

How to Optionally exclude a class member when serializing to XML

I just took over a fairly large project. One of my tasks is to modify the code that gets rate quotes from FedEx. Currently, in order to get a set of rate quotes for a shipment that includes quotes for each "ServiceType" (Ground, 2-day, overnight, etc.) the code makes one call for each type. FedEx offers a web service that is used to get this information. After doing a little research, it looks like this web service can return multiple ServiceType quotes with a single round-trip. To do this, I'm supposed to "leave the service type out of the request." (Here's the question that pointed me in that direction.)
So I know that I can exclude the service type property from the serialization by decorating the property with, [System.Xml.Serialization.XmlIgnoreAttribute()]. But how can I do that only when I want results for all the ServiceType values and still be able to pass a single service type for the cases where I know what shipping method the user wants?
EDIT: FedEx's documentation indicates that this field is optional. Some basic testing shows that excluding it from the request with the XmlIgnoreAttribute does return data for multiple ServiceTypes.
If you implement a public bool ShouldSerializeXXX() function alongside the XXX property being serialized, XmlSerializer will ignore the corresponding XXX property when the function returns false. You'll have to have some basis for setting this (maybe the XXX property can be null? or you can grab some other state to make the decision.)
So, something along these lines:
public class MyClass
{
public ServiceType? ServiceType { get; set; }
public bool ShouldSerializeServiceType() { return ServiceType.HasValue; }
}

MongoDB C# official driver : Mapping objects to short names to limit space

I searching a way to map the Bson objects defined using readable names ("category") to shorts names ("ct") and limit the space occuped by the items names in the main document base. I have seen this using others Drivers but what about using official Driver. How i can make, where is the best place to define. Can use longnames in queries and retrieve short contents?.
thanks.
Since nobody has actually given the answer to the question, here it is.
With the official driver you can do this by decorating a property name with BsonElement. For example:
public class SomeClass
{
public BsonObjectId Id { get; set; }
[BsonElement("dt")]
public DateTime SomeReallyLongDateTimePropertyName { get; set; }
}
Now the driver will use "dt" as the BSON property name.
However, at this time there is no way to query using the POCO property name. You would need to use "dt" in your queries. There is a separate project that is built on top of the C# driver that provides LINQ style querying capabilities, but I have not tested it to verify if it will do what you are asking.
It's better to keep models clean. And MongoDB.Driver allows to do it outside.
BsonClassMap.RegisterClassMap<SomeClass>(x =>
{
x.AutoMap();
x.GetMemberMap(m => m.SomeReallyLongDateTimePropertyName).SetElementName("dt");
});
Consider a record
{ last_name : "Smith", best_score: 3.9 }
The strings "last_name" and "best_score" will be stored in each object's BSON. Using shorter strings would save space:
{ lname : "Smith", score : 3.9 }
Would save 9 bytes per document. This of course reduces expressiveness to the programmer and is not recommended unless you have a collection where this is of significant concern.
Field names are not stored in indexes as indexes have a predefined structure. Thus, shortening field names will not help the size of indexes. In general it is not necessary to use short field names.
check source for more details
but other side is described in famous topic "You saved 5 cents, and your code is not readable, congrats!"
And my own opinion that short name is bad way.

Categories

Resources