|
| 1 | +.. _csharp-class-mapping: |
| 2 | + |
| 3 | +============= |
| 4 | +Class Mapping |
| 5 | +============= |
| 6 | + |
| 7 | +.. contents:: On this page |
| 8 | + :local: |
| 9 | + :backlinks: none |
| 10 | + :depth: 2 |
| 11 | + :class: singlecol |
| 12 | + |
| 13 | +Overview |
| 14 | +-------- |
| 15 | + |
| 16 | +In this guide, you can learn how to customize the way the {+driver-long+} |
| 17 | +maps BSON documents to and from {+language+} classes. You should read this page |
| 18 | +to learn more about the default class mapping behavior, or if you need to |
| 19 | +customize the way the driver serializes or deserializes your data. |
| 20 | + |
| 21 | +Automatic Class Mapping |
| 22 | +----------------------- |
| 23 | + |
| 24 | +When you use a class, rather than a ``BsonDocument``, to represent data in a |
| 25 | +MongoDB collection, the {+driver-short+} automatically creates a **class map** |
| 26 | +that it uses to serialize or deserialize your data. It does this mapping by matching |
| 27 | +the name of the field in the document to the name of the property in the class. |
| 28 | + |
| 29 | +.. important:: |
| 30 | + |
| 31 | + The type of the property in your class should match the type of the field in |
| 32 | + the document. The {+driver-short+} instantiates a serializer based on the |
| 33 | + type of the property in your class. If the types don't match when the driver |
| 34 | + attempts to deserialize the data, the serializer throws an exception. |
| 35 | + |
| 36 | +Manually Creating A Class Map |
| 37 | +----------------------------- |
| 38 | + |
| 39 | +You can bypass the driver's automatic class mapping functionality, and manually |
| 40 | +define the class map by using the ``RegisterClassMap()`` method. |
| 41 | + |
| 42 | +The following example defines a ``Person`` class: |
| 43 | + |
| 44 | +.. code-block:: csharp |
| 45 | + |
| 46 | + public class Person |
| 47 | + { |
| 48 | + public string Name { get; set; } |
| 49 | + public int Age { get; set; } |
| 50 | + public List<string> Hobbies {get; set;} |
| 51 | + } |
| 52 | + |
| 53 | +The following code demonstrates how to register the class map for the ``Person`` |
| 54 | +class: |
| 55 | + |
| 56 | +.. code-block:: csharp |
| 57 | + |
| 58 | + BsonClassMap.RegisterClassMap<Person>(classMap => |
| 59 | + { |
| 60 | + classMap.MapMember(p => p.Name); |
| 61 | + classMap.MapMember(p => p.Age); |
| 62 | + classMap.MapMember(p => p.Hobbies); |
| 63 | + }); |
| 64 | + |
| 65 | +.. important:: |
| 66 | + |
| 67 | + You must register a class map *before* it's needed in your code. We recommend |
| 68 | + registering class maps prior to initializing a connection with MongoDB. |
| 69 | + |
| 70 | +You can also manually map a subset of class properties, while still |
| 71 | +allowing the driver to automatically map the remaining properties. To do this, |
| 72 | +register a class map and call the ``AutoMap()`` method before manually |
| 73 | +specifying your properties. |
| 74 | + |
| 75 | +In the following code example, the ``AutoMap()`` method maps all properties |
| 76 | +of the ``Person`` class, then manually adjusts the mapping for the ``Hobbies`` |
| 77 | +field: |
| 78 | + |
| 79 | +.. code-block:: csharp |
| 80 | + |
| 81 | + BsonClassMap.RegisterClassMap<Person>(classMap => |
| 82 | + { |
| 83 | + classMap.AutoMap(); |
| 84 | + classMap.MapMember(p => p.Hobbies).SetElementName("favorite_hobbies"); |
| 85 | + }); |
| 86 | + |
| 87 | +Customize Class Serialization |
| 88 | +----------------------------- |
| 89 | + |
| 90 | +You can customize how the driver serializes and deserializes documents at the class |
| 91 | +level by using attributes with the class or by calling methods while registering |
| 92 | +a class map. |
| 93 | + |
| 94 | +Ignore Extra Elements |
| 95 | +~~~~~~~~~~~~~~~~~~~~~ |
| 96 | + |
| 97 | +When a BSON document is deserialized to a {+language+} class, the {+driver-short+} |
| 98 | +looks at the name of each field in the document and tries to find a matching property |
| 99 | +name in the class. By default, if a field in the document doesn't have a match in the class, |
| 100 | +the driver throws an exception. |
| 101 | + |
| 102 | +You can choose to ignore any elements that do |
| 103 | +not have a matching class property by using the ``BsonIgnoreExtraElements`` attribute. |
| 104 | +This prevents the driver from throwing an exception, and maps any other fields |
| 105 | +that have matching class properties. |
| 106 | + |
| 107 | +The following example shows how to add a ``BsonIgnoreExtraElements`` attribute |
| 108 | +to a class. |
| 109 | + |
| 110 | +.. code-block:: csharp |
| 111 | + |
| 112 | + [BsonIgnoreExtraElements] |
| 113 | + public class Person |
| 114 | + { |
| 115 | + public string Name { get; set; } |
| 116 | + public int Age { get; set; } |
| 117 | + public List<string> Hobbies {get; set;} |
| 118 | + } |
| 119 | + |
| 120 | +You can also ignore any extra elements when registering a class map: |
| 121 | + |
| 122 | +.. code-block:: csharp |
| 123 | + |
| 124 | + BsonClassMap.RegisterClassMap<Person>(classMap => |
| 125 | + { |
| 126 | + classMap.AutoMap(); |
| 127 | + classMap.SetIgnoreExtraElements(true); |
| 128 | + }); |
| 129 | + |
| 130 | +Using Class Discriminators |
| 131 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 132 | + |
| 133 | +You can specify **discriminators** to help identify **polymorphic** classes that |
| 134 | +are serialized to the same collection. Polymorphic classes are classes that |
| 135 | +inherit properties and methods from a parent class. A discriminator is an |
| 136 | +element that's added to a document to identify which class the document was |
| 137 | +serialized from. |
| 138 | + |
| 139 | +You can specify a discriminator using the ``BsonDiscriminator`` attribute as |
| 140 | +follows: |
| 141 | + |
| 142 | +.. code-block:: csharp |
| 143 | + |
| 144 | + [BsonDiscriminator("personClass")] |
| 145 | + public class Person |
| 146 | + { |
| 147 | + public string Name { get; set; } |
| 148 | + public int Age { get; set; } |
| 149 | + public List<string> Hobbies {get; set;} |
| 150 | + } |
| 151 | + |
| 152 | +You can also specify a discriminator when registering a class map as follows: |
| 153 | + |
| 154 | +.. code-block:: csharp |
| 155 | + |
| 156 | + BsonClassMap.RegisterClassMap<Person>(classMap => |
| 157 | + { |
| 158 | + classMap.AutoMap(); |
| 159 | + classMap.SetDiscriminator("personClass"); |
| 160 | + }); |
| 161 | + |
| 162 | +In BSON, discriminators have the field name ``_t``. |
| 163 | + |
| 164 | +The following example shows how a document from the ``Person`` class with the |
| 165 | +"personClass" discriminator appears in the collection after serialization: |
| 166 | + |
| 167 | +.. code-block:: json |
| 168 | + |
| 169 | + { "_id": "...", "_t": "personClass", "Name": "...", "Age": "...", "Hobbies": [...]} |
| 170 | + |
| 171 | +.. TODO: Link to page on polymorphism/discriminators |
| 172 | + |
| 173 | +Mapping with Constructors |
| 174 | +------------------------- |
| 175 | + |
| 176 | +By default, the {+driver-short+} can automatically map a class only if the class has |
| 177 | +a constructor with no arguments. If you want the driver to use a constructor that accepts |
| 178 | +one or more arguments, you can add the ``BsonConstructor`` attribute to the constructor. |
| 179 | +In this case, the driver the driver examines the types to determine how to map the |
| 180 | +constructor arguments to class properties or fields. |
| 181 | + |
| 182 | +When the driver creates a class map for the following ``Person`` class, it will use the |
| 183 | +constructor marked with the ``BsonConstructor`` attribute. The ``name`` argument will |
| 184 | +map to the ``Name`` property and the ``age`` argument will map to the ``Age`` property. |
| 185 | + |
| 186 | +.. code-block:: csharp |
| 187 | + :emphasize-lines: 7 |
| 188 | + |
| 189 | + public class Person |
| 190 | + { |
| 191 | + public string Name { get; set; } |
| 192 | + public int Age { get; set; } |
| 193 | + public List<string> Hobbies {get; set;} |
| 194 | + |
| 195 | + [BsonConstructor] |
| 196 | + public Person(string name, string age) |
| 197 | + { |
| 198 | + Name = name; |
| 199 | + Age = age; |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | +.. tip:: Multiple ``BsonConstructor`` attributes |
| 204 | + |
| 205 | + If there is more than one constructor with the ``BsonConstructor`` |
| 206 | + attribute, the driver uses the constructor that has the most |
| 207 | + parameters with matching fields in the document. |
| 208 | + |
| 209 | +You can also specify the constructor to use when registering your class map: |
| 210 | + |
| 211 | +.. code-block:: csharp |
| 212 | + :emphasize-lines: 17 |
| 213 | + |
| 214 | + public class Person |
| 215 | + { |
| 216 | + public string Name { get; set; } |
| 217 | + public int Age { get; set; } |
| 218 | + public List<string> Hobbies {get; set;} |
| 219 | + |
| 220 | + public Person(string name, string age) |
| 221 | + { |
| 222 | + Name = name; |
| 223 | + Age = age; |
| 224 | + } |
| 225 | + } |
| 226 | + |
| 227 | + BsonClassMap.RegisterClassMap<Person>(classMap => |
| 228 | + { |
| 229 | + classMap.AutoMap(); |
| 230 | + classMap.MapCreator(p => new Person(p.Name, p.Age)); |
| 231 | + }); |
| 232 | + |
| 233 | +Customize Property Serialization |
| 234 | +-------------------------------- |
| 235 | + |
| 236 | +You can customize how the driver serializes a class property by |
| 237 | +adding attributes to the property. For more information about custom |
| 238 | +property serialization, see :ref:`csharp-custom-serialization`. |
| 239 | + |
| 240 | +Support Extra Elements |
| 241 | +~~~~~~~~~~~~~~~~~~~~~~ |
| 242 | + |
| 243 | +You can design your {+language+} class to store any extra elements in your |
| 244 | +document that don't have matching class properties. To do this your class must |
| 245 | +have a ``BsonDocument`` type property to hold the extra elements. |
| 246 | + |
| 247 | +The following code uses the ``BsonExtraElements`` attribute with the |
| 248 | +``ExtraElements`` property to direct the driver to store extra elements: |
| 249 | + |
| 250 | +.. code-block:: csharp |
| 251 | + |
| 252 | + public class Person |
| 253 | + { |
| 254 | + public string Name { get; set; |
| 255 | + public int Age { get; set; } |
| 256 | + public List<string> Hobbies {get; set;} |
| 257 | + [BsonExtraElements] |
| 258 | + public BsonDocument ExtraElements {get; set;} |
| 259 | + } |
| 260 | + |
| 261 | +You can also support extra elements when initializing a class map as follows: |
| 262 | + |
| 263 | +.. code-block:: csharp |
| 264 | + |
| 265 | + BsonClassMap.RegisterClassMap<Person>(classMap => |
| 266 | + { |
| 267 | + classMap.AutoMap(); |
| 268 | + classMap.MapExtraElementsMember(p => p.ExtraElements); |
| 269 | + }); |
| 270 | + |
| 271 | +.. note:: |
| 272 | + |
| 273 | + After the driver serializes a class with extra elements back to BSON, the |
| 274 | + extra elements may not be in the same order as they were in the original |
| 275 | + document. |
| 276 | + |
| 277 | +Dynamically Serialize Properties |
| 278 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 279 | + |
| 280 | +You can use a method to determine whether or not to serialize a property. For |
| 281 | +the driver to automatically use the method when serializing, you must prefix the |
| 282 | +method name with ``ShouldSerialize`` followed by the name of the property that |
| 283 | +the method applies to. When the driver sees a method with this naming |
| 284 | +convention, it uses that method to determine whether or not to serialize |
| 285 | +properties that have the provided property name. |
| 286 | + |
| 287 | +The following example creates a method that only serializes the ``Age`` property |
| 288 | +if its value is not equal to ``0``. The driver does not serialize any properties |
| 289 | +whose values don't meet this requirement: |
| 290 | + |
| 291 | +.. code-block:: csharp |
| 292 | + |
| 293 | + public class Person |
| 294 | + { |
| 295 | + public string Name { get; set; } |
| 296 | + public int Age { get; set; } |
| 297 | + public List<string> Hobbies {get; set;} |
| 298 | + |
| 299 | + public bool ShouldSerializeAge() |
| 300 | + { |
| 301 | + return Age != 0; |
| 302 | + } |
| 303 | + } |
| 304 | + |
| 305 | +You can also specify the method while registering a class map: |
| 306 | + |
| 307 | +.. code-block:: csharp |
| 308 | + |
| 309 | + BsonClassMap.RegisterClassMap<Employee>(classMap => |
| 310 | + { |
| 311 | + classMap.AutoMap(); |
| 312 | + classMap.MapMember(p => c.Age).SetShouldSerializeMethod( |
| 313 | + obj => ((Person) obj).Age != 0 |
| 314 | + ); |
| 315 | + }); |
| 316 | + |
| 317 | +Additional Information |
| 318 | +---------------------- |
| 319 | + |
| 320 | +For more information on using {+language+} classes, see :ref:`csharp-poco`. |
| 321 | + |
| 322 | +API Documentation |
| 323 | +----------------- |
| 324 | + |
| 325 | +- `BsonClassMap |
| 326 | + <{+api-root+}/T_MongoDB_Bson_Serialization_BsonClassMap_1.htm>`__ |
| 327 | +- `RegisterClassMap |
| 328 | + <{+api-root+}/M_MongoDB_Bson_Serialization_BsonClassMap_RegisterClassMap__1_1.htm>`__ |
| 329 | +- `AutoMap |
| 330 | + <{+api-root+}/M_MongoDB_Bson_Serialization_BsonClassMap_AutoMap.htm>`__ |
0 commit comments