Skip to content

Commit 8a86707

Browse files
craiggwilsonrstam
authored andcommitted
added interface serializer and support for top-level serializer specificity to allow for customer serializers to get injected/built at runtime.
1 parent 7978dcc commit 8a86707

File tree

8 files changed

+205
-24
lines changed

8 files changed

+205
-24
lines changed

MongoDB.Bson/MongoDB.Bson.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@
258258
<Compile Include="Serialization\Serializers\Int16Serializer.cs" />
259259
<Compile Include="Serialization\Serializers\Int32Serializer.cs" />
260260
<Compile Include="Serialization\Serializers\Int64Serializer.cs" />
261+
<Compile Include="Serialization\Serializers\InterfaceSerializer.cs" />
261262
<Compile Include="Serialization\Serializers\IPAddressSerializer.cs" />
262263
<Compile Include="Serialization\Serializers\IPEndPointSerializer.cs" />
263264
<Compile Include="Serialization\Serializers\KeyValuePairSerializer.cs" />

MongoDB.Bson/Serialization/BsonDefaultSerializationProvider.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ public IBsonSerializer GetSerializer(Type type)
183183
return collectionSerializer;
184184
}
185185

186+
// we'll try our best by attempting to find a discriminator hoping it points
187+
// us to a concrete type with a serializer.
188+
if (type.IsInterface)
189+
{
190+
return InterfaceSerializer.Instance;
191+
}
192+
186193
return null;
187194
}
188195

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* Copyright 2010-2013 10gen Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.IO;
19+
using System.Linq;
20+
using System.Text;
21+
22+
using MongoDB.Bson.IO;
23+
using MongoDB.Bson.Serialization;
24+
25+
namespace MongoDB.Bson.Serialization.Serializers
26+
{
27+
/// <summary>
28+
/// Represents a serializer for Interfaces.
29+
/// </summary>
30+
public class InterfaceSerializer : BsonBaseSerializer
31+
{
32+
// private static fields
33+
private static InterfaceSerializer __instance = new InterfaceSerializer();
34+
35+
// constructors
36+
/// <summary>
37+
/// Initializes a new instance of the InterfaceSerializer class.
38+
/// </summary>
39+
public InterfaceSerializer()
40+
{
41+
}
42+
43+
// public static properties
44+
/// <summary>
45+
/// Gets an instance of the InterfaceSerializer class.
46+
/// </summary>
47+
public static InterfaceSerializer Instance
48+
{
49+
get { return __instance; }
50+
}
51+
52+
// public methods
53+
/// <summary>
54+
/// Deserializes a document from a BsonReader.
55+
/// </summary>
56+
/// <param name="bsonReader">The BsonReader.</param>
57+
/// <param name="nominalType">The nominal type of the document.</param>
58+
/// <param name="options">The serialization options.</param>
59+
/// <returns>A document.</returns>
60+
public override object Deserialize(
61+
BsonReader bsonReader,
62+
Type nominalType,
63+
IBsonSerializationOptions options)
64+
{
65+
if (!nominalType.IsInterface)
66+
{
67+
var message = string.Format("Nominal type must be an interface, not {0}.", nominalType.FullName);
68+
throw new ArgumentException(message, "nominalType");
69+
}
70+
71+
var discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(nominalType);
72+
var actualType = discriminatorConvention.GetActualType(bsonReader, nominalType);
73+
if (actualType == nominalType)
74+
{
75+
var message = string.Format("Unable to determine actual type of object to deserialize. NominalType is the interface {0}.", nominalType);
76+
throw new FileFormatException(message);
77+
}
78+
79+
return Deserialize(bsonReader, nominalType, actualType, options);
80+
}
81+
82+
/// <summary>
83+
/// Deserializes a document from a BsonReader.
84+
/// </summary>
85+
/// <param name="bsonReader">The BsonReader.</param>
86+
/// <param name="nominalType">The nominal type of the document.</param>
87+
/// <param name="actualType">The actual type of the document..</param>
88+
/// <param name="options">The serialization options.</param>
89+
/// <returns>A document.</returns>
90+
public override object Deserialize(
91+
BsonReader bsonReader,
92+
Type nominalType,
93+
Type actualType,
94+
IBsonSerializationOptions options)
95+
{
96+
var serializer = BsonSerializer.LookupSerializer(actualType);
97+
return serializer.Deserialize(bsonReader, nominalType, actualType, options);
98+
}
99+
100+
/// <summary>
101+
/// Serializes a document to a BsonWriter.
102+
/// </summary>
103+
/// <param name="bsonWriter">The BsonWriter.</param>
104+
/// <param name="nominalType">The nominal type.</param>
105+
/// <param name="value">The document.</param>
106+
/// <param name="options">The serialization options.</param>
107+
public override void Serialize(
108+
BsonWriter bsonWriter,
109+
Type nominalType,
110+
object value,
111+
IBsonSerializationOptions options)
112+
{
113+
if (!nominalType.IsInterface)
114+
{
115+
var message = string.Format("Nominal type must be an interface, not {0}.", nominalType.FullName);
116+
throw new ArgumentException(message, "nominalType");
117+
}
118+
119+
if (value == null)
120+
{
121+
bsonWriter.WriteNull();
122+
}
123+
else
124+
{
125+
var actualType = value.GetType();
126+
var serializer = BsonSerializer.LookupSerializer(actualType);
127+
serializer.Serialize(bsonWriter, nominalType, value, options);
128+
}
129+
}
130+
}
131+
}

MongoDB.Driver/Communication/Messages/MongoReplyMessage.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,20 @@ namespace MongoDB.Driver.Internal
2525
internal class MongoReplyMessage<TDocument> : MongoMessage
2626
{
2727
// private fields
28-
private BsonBinaryReaderSettings _readerSettings;
28+
private readonly BsonBinaryReaderSettings _readerSettings;
29+
private readonly IBsonSerializer _serializer;
2930
private ResponseFlags _responseFlags;
3031
private long _cursorId;
3132
private int _startingFrom;
3233
private int _numberReturned;
3334
private List<TDocument> _documents;
3435

3536
// constructors
36-
internal MongoReplyMessage(BsonBinaryReaderSettings readerSettings)
37+
internal MongoReplyMessage(BsonBinaryReaderSettings readerSettings, IBsonSerializer serializer)
3738
: base(MessageOpcode.Reply)
3839
{
3940
_readerSettings = readerSettings;
41+
_serializer = serializer;
4042
}
4143

4244
// internal properties
@@ -117,10 +119,10 @@ internal void ReadFrom(BsonBuffer buffer, IBsonSerializationOptions serializatio
117119

118120
using (var bsonReader = new BsonBinaryReader(sliceBuffer, true, _readerSettings))
119121
{
120-
var document = (TDocument)BsonSerializer.Deserialize(bsonReader, typeof(TDocument), serializationOptions);
122+
var document = (TDocument)_serializer.Deserialize(bsonReader, typeof(TDocument), serializationOptions);
121123
_documents.Add(document);
122124
}
123125
}
124126
}
125127
}
126-
}
128+
}

MongoDB.Driver/Communication/MongoConnection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using MongoDB.Bson;
2323
using MongoDB.Bson.IO;
2424
using MongoDB.Bson.Serialization;
25+
using MongoDB.Bson.Serialization.Serializers;
2526
using MongoDB.Driver.Communication.Security;
2627

2728
namespace MongoDB.Driver.Internal
@@ -267,7 +268,7 @@ internal CommandResult RunCommand(
267268
GuidRepresentation = GuidRepresentation.Unspecified,
268269
MaxDocumentSize = _serverInstance.MaxDocumentSize
269270
};
270-
var reply = ReceiveMessage<BsonDocument>(readerSettings, null);
271+
var reply = ReceiveMessage<BsonDocument>(readerSettings, BsonDocumentSerializer.Instance, null);
271272
if (reply.NumberReturned == 0)
272273
{
273274
var message = string.Format("Command '{0}' failed. No response returned.", commandName);
@@ -285,6 +286,7 @@ internal CommandResult RunCommand(
285286

286287
internal MongoReplyMessage<TDocument> ReceiveMessage<TDocument>(
287288
BsonBinaryReaderSettings readerSettings,
289+
IBsonSerializer serializer,
288290
IBsonSerializationOptions serializationOptions)
289291
{
290292
if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
@@ -304,7 +306,7 @@ internal MongoReplyMessage<TDocument> ReceiveMessage<TDocument>(
304306
using (var bsonBuffer = new BsonBuffer(byteBuffer, true))
305307
{
306308
byteBuffer.MakeReadOnly();
307-
var reply = new MongoReplyMessage<TDocument>(readerSettings);
309+
var reply = new MongoReplyMessage<TDocument>(readerSettings, serializer);
308310
reply.ReadFrom(bsonBuffer, serializationOptions);
309311
return reply;
310312
}
@@ -373,7 +375,7 @@ internal WriteConcernResult SendMessage(BsonBuffer buffer, MongoRequestMessage m
373375
GuidRepresentation = message.WriterSettings.GuidRepresentation,
374376
MaxDocumentSize = _serverInstance.MaxDocumentSize
375377
};
376-
var replyMessage = ReceiveMessage<BsonDocument>(readerSettings, null);
378+
var replyMessage = ReceiveMessage<BsonDocument>(readerSettings, BsonDocumentSerializer.Instance, null);
377379
var getLastErrorResponse = replyMessage.Documents[0];
378380
writeConcernResult = new WriteConcernResult();
379381
writeConcernResult.Initialize(getLastErrorCommand, getLastErrorResponse);

MongoDB.Driver/MongoCollection.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@ public virtual FindAndModifyResult FindAndRemove(IMongoQuery query, IMongoSortBy
543543
/// <returns>A <see cref="MongoCursor{TDocument}"/>.</returns>
544544
public virtual MongoCursor<TDocument> FindAs<TDocument>(IMongoQuery query)
545545
{
546-
return new MongoCursor<TDocument>(this, query, _settings.ReadPreference);
546+
var serializer = BsonSerializer.LookupSerializer(typeof(TDocument));
547+
return new MongoCursor<TDocument>(this, query, _settings.ReadPreference, serializer);
547548
}
548549

549550
/// <summary>
@@ -554,7 +555,8 @@ public virtual MongoCursor<TDocument> FindAs<TDocument>(IMongoQuery query)
554555
/// <returns>A <see cref="MongoCursor{TDocument}"/>.</returns>
555556
public virtual MongoCursor FindAs(Type documentType, IMongoQuery query)
556557
{
557-
return MongoCursor.Create(documentType, this, query, _settings.ReadPreference);
558+
var serializer = BsonSerializer.LookupSerializer(documentType);
559+
return MongoCursor.Create(documentType, this, query, _settings.ReadPreference, serializer);
558560
}
559561

560562
/// <summary>

MongoDB.Driver/MongoCursor.cs

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Linq;
2020
using MongoDB.Bson;
2121
using MongoDB.Bson.Serialization;
22+
using MongoDB.Bson.Serialization.Serializers;
2223
using MongoDB.Driver.Builders;
2324

2425
namespace MongoDB.Driver
@@ -30,10 +31,11 @@ namespace MongoDB.Driver
3031
public abstract class MongoCursor : IEnumerable
3132
{
3233
// private fields
33-
private MongoServer _server;
34-
private MongoDatabase _database;
35-
private MongoCollection _collection;
36-
private IMongoQuery _query;
34+
private readonly MongoCollection _collection;
35+
private readonly MongoDatabase _database;
36+
private readonly IMongoQuery _query;
37+
private readonly IBsonSerializer _serializer;
38+
private readonly MongoServer _server;
3739
private IMongoFields _fields;
3840
private BsonDocument _options;
3941
private QueryFlags _flags;
@@ -51,12 +53,14 @@ public abstract class MongoCursor : IEnumerable
5153
/// <param name="collection">The collection.</param>
5254
/// <param name="query">The query.</param>
5355
/// <param name="readPreference">The read preference.</param>
54-
protected MongoCursor(MongoCollection collection, IMongoQuery query, ReadPreference readPreference)
56+
/// <param name="serializer">The serializer.</param>
57+
protected MongoCursor(MongoCollection collection, IMongoQuery query, ReadPreference readPreference, IBsonSerializer serializer)
5558
{
56-
_server = collection.Database.Server;
57-
_database = collection.Database;
5859
_collection = collection;
60+
_database = collection.Database;
61+
_server = collection.Database.Server;
5962
_query = query;
63+
_serializer = serializer;
6064
_readPreference = readPreference;
6165
}
6266

@@ -224,6 +228,14 @@ public virtual IBsonSerializationOptions SerializationOptions
224228
}
225229
}
226230

231+
/// <summary>
232+
/// Gets the serializer.
233+
/// </summary>
234+
public virtual IBsonSerializer Serializer
235+
{
236+
get { return _serializer; }
237+
}
238+
227239
/// <summary>
228240
/// Gets whether the cursor has been frozen to prevent further changes.
229241
/// </summary>
@@ -241,13 +253,14 @@ public virtual bool IsFrozen
241253
/// <param name="collection">The collection to query.</param>
242254
/// <param name="query">A query.</param>
243255
/// <param name="readPreference">The read preference.</param>
256+
/// <param name="serializer">The serializer.</param>
244257
/// <returns>A cursor.</returns>
245-
public static MongoCursor Create(Type documentType, MongoCollection collection, IMongoQuery query, ReadPreference readPreference)
258+
public static MongoCursor Create(Type documentType, MongoCollection collection, IMongoQuery query, ReadPreference readPreference, IBsonSerializer serializer)
246259
{
247260
var cursorDefinition = typeof(MongoCursor<>);
248261
var cursorType = cursorDefinition.MakeGenericType(documentType);
249-
var constructorInfo = cursorType.GetConstructor(new Type[] { typeof(MongoCollection), typeof(IMongoQuery), typeof(ReadPreference) });
250-
return (MongoCursor)constructorInfo.Invoke(new object[] { collection, query, readPreference });
262+
var constructorInfo = cursorType.GetConstructor(new Type[] { typeof(MongoCollection), typeof(IMongoQuery), typeof(ReadPreference), typeof(IBsonSerializer)});
263+
return (MongoCursor)constructorInfo.Invoke(new object[] { collection, query, readPreference, serializer });
251264
}
252265

253266
// public methods
@@ -261,14 +274,36 @@ public virtual MongoCursor<TDocument> Clone<TDocument>()
261274
return (MongoCursor<TDocument>)Clone(typeof(TDocument));
262275
}
263276

277+
/// <summary>
278+
/// Creates a clone of the cursor.
279+
/// </summary>
280+
/// <typeparam name="TDocument">The type of the documents returned.</typeparam>
281+
/// <param name="serializer">The serializer to use.</param>
282+
/// <returns>A clone of the cursor.</returns>
283+
public virtual MongoCursor<TDocument> Clone<TDocument>(IBsonSerializer serializer)
284+
{
285+
return (MongoCursor<TDocument>)Clone(typeof(TDocument), serializer);
286+
}
287+
264288
/// <summary>
265289
/// Creates a clone of the cursor.
266290
/// </summary>
267291
/// <param name="documentType">The type of the documents returned.</param>
268292
/// <returns>A clone of the cursor.</returns>
269293
public virtual MongoCursor Clone(Type documentType)
270294
{
271-
var clone = Create(documentType, _collection, _query, _readPreference);
295+
return Clone(documentType, _serializer);
296+
}
297+
298+
/// <summary>
299+
/// Creates a clone of the cursor.
300+
/// </summary>
301+
/// <param name="documentType">The type of the documents returned.</param>
302+
/// <param name="serializer">The serializer to use.</param>
303+
/// <returns>A clone of the cursor.</returns>
304+
public virtual MongoCursor Clone(Type documentType, IBsonSerializer serializer)
305+
{
306+
var clone = Create(documentType, _collection, _query, _readPreference, serializer);
272307
clone._options = _options == null ? null : (BsonDocument)_options.Clone();
273308
clone._flags = _flags;
274309
clone._skip = _skip;
@@ -312,7 +347,7 @@ public virtual BsonDocument Explain()
312347
public virtual BsonDocument Explain(bool verbose)
313348
{
314349
_isFrozen = true;
315-
var clone = Clone<BsonDocument>();
350+
var clone = Clone<BsonDocument>(BsonDocumentSerializer.Instance);
316351
clone.SetOption("$explain", true);
317352
clone._limit = -clone._limit; // TODO: should this be -1?
318353
var explanation = clone.FirstOrDefault();
@@ -647,8 +682,9 @@ public class MongoCursor<TDocument> : MongoCursor, IEnumerable<TDocument>
647682
/// <param name="collection">The collection.</param>
648683
/// <param name="query">The query.</param>
649684
/// <param name="readPreference">The read preference.</param>
650-
public MongoCursor(MongoCollection collection, IMongoQuery query, ReadPreference readPreference)
651-
: base(collection, query, readPreference)
685+
/// <param name="serializer">The serializer.</param>
686+
public MongoCursor(MongoCollection collection, IMongoQuery query, ReadPreference readPreference, IBsonSerializer serializer)
687+
: base(collection, query, readPreference, serializer)
652688
{
653689
}
654690

MongoDB.Driver/MongoCursorEnumerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ private MongoReplyMessage<TDocument> GetReply(MongoConnection connection, MongoR
371371
{
372372
var readerSettings = _cursor.Collection.GetReaderSettings(connection);
373373
connection.SendMessage(message, null, _cursor.Database.Name); // write concern doesn't apply to queries
374-
var reply = connection.ReceiveMessage<TDocument>(readerSettings, _cursor.SerializationOptions);
374+
var reply = connection.ReceiveMessage<TDocument>(readerSettings, _cursor.Serializer, _cursor.SerializationOptions);
375375
_responseFlags = reply.ResponseFlags;
376376
_openCursorId = reply.CursorId;
377377
return reply;

0 commit comments

Comments
 (0)