Permalink
Browse files

Merged JornWildt's TypeConverter code.

  • Loading branch information...
atheken committed Aug 4, 2010
1 parent 698c298 commit 60723272c838f948a266dd1dc90632a3cd1e28b8
@@ -5,6 +5,8 @@
using Xunit;
using Norm.BSON;
using System.Linq;
+using System.Globalization;
+using Norm.Configuration;
namespace Norm.Tests
{
@@ -467,6 +469,35 @@ public void SerializesExtraPropertiesToExpandoCollection()
Assert.Equal(expando["State"], address.State);
Assert.Equal(expando["Zip"], address.Zip);
}
-
+
+ [Fact]
+ public void SerializesCultureInfo()
+ {
+ var s1 = new CultureInfoDTO() { Culture = CultureInfo.GetCultureInfo("en-US") };
+ var bytes = BsonSerializer.Serialize(s1);
+ var s2 = BsonDeserializer.Deserialize<CultureInfoDTO>(bytes);
+ Assert.Equal(s1.Culture, s2.Culture);
+ }
+
+ [Fact]
+ public void SerializesClassWithCustomValueObjectUsingCustomTypeConverter()
+ {
+ IMongoConfigurationMap cfg = new MongoConfigurationMap();
+ cfg.TypeConverterFor<NonSerializableValueObject, NonSerializableValueObjectTypeConverter>();
+ BsonSerializer.UseConfiguration(cfg);
+
+ // Verify that a contained, normally unserializable, value can be serialized with a proper type converter
+ var s1 = new NonSerializableClass()
+ {
+ Value = new NonSerializableValueObject("12345"),
+ Text = "Abc"
+ };
+ var bytes = BsonSerializer.Serialize(s1);
+ var s2 = BsonDeserializer.Deserialize<NonSerializableClass>(bytes);
+ Assert.Equal(s1.Value.Number, s2.Value.Number);
+ Assert.Equal(s1.Text, s2.Text);
+
+ BsonSerializer.UseConfiguration(null);
+ }
}
}
@@ -17,6 +17,7 @@ public MongoConfigurationTests()
MongoConfiguration.RemoveMapFor<Shopper>();
MongoConfiguration.RemoveMapFor<Cart>();
MongoConfiguration.RemoveMapFor<TestProduct>();
+ MongoConfiguration.RemoveTypeConverterFor<NonSerializableValueObject>();
using (var admin = new MongoAdmin(TestHelper.ConnectionString()))
{
@@ -202,7 +203,7 @@ public void Are_Queries_Fully_Linqified()
}
});
- var deepQuery = shoppers.Where(x => x.Cart.CartSuppliers.Any(y=>y.Name == "Supplier1")).ToList();
+ var deepQuery = shoppers.Where(x => x.Cart.CartSuppliers.Any(y => y.Name == "Supplier1")).ToList();
Assert.Equal("John", deepQuery[0].Name);
Assert.Equal("Cart1", deepQuery[0].Cart.Name);
Assert.Equal(1, deepQuery.Count);
@@ -255,7 +256,7 @@ public void Subclass_Adheres_To_Superclass_Fluent_Alias()
var found = mongo.GetCollection<SuperClassObjectFluentMapped>("Fake").Find();
Assert.Equal(2, found.Count());
- Assert.Equal(obj1.Id, found.ElementAt(0).Id) ;
+ Assert.Equal(obj1.Id, found.ElementAt(0).Id);
Assert.Equal("Prod1", found.ElementAt(0).Title);
Assert.Equal(obj2.Id, found.ElementAt(1).Id);
Assert.Equal("Prod2", found.ElementAt(1).Title);
@@ -276,21 +277,13 @@ public void Subclassed_Type_Is_Returned_When_Superclass_Is_Used_For_The_Collecti
Assert.Equal(typeof(SubClassedObjectFluentMapped), found.ElementAt(0).GetType());
}
}
-
+
[Fact]
- public void Can_fluently_configure_discriminator_for_all_implementations_of_an_interface()
+ public void Can_Register_TypeConverter()
{
- MongoConfiguration.Initialize(r => r.AddMap<DiscriminationMap>());
-
- using (var mongo = Mongo.Create(TestHelper.ConnectionString()))
- {
- var obj1 = new InterfacePropertyContainingClass();
- mongo.GetCollection<InterfacePropertyContainingClass>().Insert(obj1);
- var found = mongo.GetCollection<InterfacePropertyContainingClass>().Find();
-
- Assert.Equal(1, found.Count());
- Assert.Equal(typeof(NotDiscriminatedClass), found.ElementAt(0).InterfaceProperty.GetType());
- }
+ MongoConfiguration.Initialize(c => c.TypeConverterFor<NonSerializableValueObject, NonSerializableValueObjectTypeConverter>());
+ IBsonTypeConverter converter = MongoConfiguration.ConfigurationContainer.GetTypeConverterFor(typeof(NonSerializableValueObject));
+ Assert.Equal(typeof(NonSerializableValueObjectTypeConverter), converter.GetType());
}
}
}
View
@@ -12,6 +12,7 @@
using System.ComponentModel;
using Norm.BSON;
using System.Collections;
+using System.Globalization;
namespace Norm.Tests
{
@@ -125,12 +126,15 @@ public IQueryable<Post> Posts
get { return _provider.GetCollection<Post>().AsQueryable(); }
}
+ #region IDisposable Members
public void Dispose()
{
_provider.Dispose();
}
+ #endregion
+
public T MapReduce<T>(string map, string reduce)
{
T result = default(T);
@@ -532,6 +536,55 @@ public class ChildGeneralDTO : GeneralDTO
public bool IsOver9000 { get; set; }
}
+ public class CultureInfoDTO
+ {
+ public CultureInfo Culture { get; set; }
+ }
+
+ public class NonSerializableClass
+ {
+ public NonSerializableValueObject Value { get; set; }
+ public string Text { get; set; }
+ }
+
+ public class NonSerializableValueObject
+ {
+ // Stuff a few properties in here that Norm normally cannot handle
+ private ArgumentException ex { get; set; }
+ private NonSerializableValueObject MakeNormCrashReference { get; set; }
+
+ public string Number { get; private set; }
+
+ public NonSerializableValueObject(string number)
+ {
+ Number = number;
+ MakeNormCrashReference = this;
+ }
+ }
+
+ public class NonSerializableValueObjectTypeConverter : IBsonTypeConverter
+ {
+ #region IBsonTypeConverter Members
+
+ public Type SerializedType
+ {
+ get { return typeof(string); }
+ }
+
+ public object ConvertToBson(object data)
+ {
+ return ((NonSerializableValueObject)data).Number;
+ }
+
+ public object ConvertFromBson(object data)
+ {
+ return new NonSerializableValueObject((string)data);
+ }
+
+ #endregion
+ }
+
+
[MongoDiscriminated]
public class SuperClassObject
{
@@ -7,12 +7,13 @@
using Norm.BSON.DbTypes;
using Norm.Configuration;
+
namespace Norm.BSON
{
/// <summary>
/// The bson serializer.
/// </summary>
- internal class BsonSerializer
+ internal class BsonSerializer : BsonSerializerBase
{
private static readonly IDictionary<Type, BSONTypes> _typeMap = new Dictionary<Type, BSONTypes>
{
@@ -212,6 +213,13 @@ private void SerializeMember(string name, object value)
}
var type = value.GetType();
+ IBsonTypeConverter converter = Configuration.GetTypeConverterFor(type);
+ if (converter != null)
+ {
+ value = converter.ConvertToBson(value);
+ }
+
+ type = value.GetType();
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
@@ -1,17 +1,18 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using Norm.Configuration;
+
+
namespace Norm.BSON
{
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
- using System.Text;
- using System.Text.RegularExpressions;
-
/// <summary>
/// BSON Deserializer
/// </summary>
- public class BsonDeserializer
+ public class BsonDeserializer : BsonSerializerBase
{
private static readonly Type _IEnumerableType = typeof(IEnumerable);
private static readonly Type _IDictionaryType = typeof(IDictionary<,>);
@@ -163,13 +164,36 @@ private object DeserializeValue(Type type, BSONTypes storedType)
}
/// <summary>
- /// Deserializes the value.
+ /// Applies optional type conversion and deserializes the value.
/// </summary>
/// <param retval="type">The type.</param>
/// <param retval="storedType">Type of the stored.</param>
/// <param retval="container">The container.</param>
/// <returns></returns>
private object DeserializeValue(Type type, BSONTypes storedType, object container)
+ {
+ IBsonTypeConverter converter = Configuration.GetTypeConverterFor(type);
+ if (converter != null)
+ {
+ Type serializedType = converter.SerializedType;
+ object value = DeserializeValueAfterConversion(serializedType, storedType, container);
+ return converter.ConvertFromBson(value);
+ }
+ else
+ {
+ return DeserializeValueAfterConversion(type, storedType, container);
+ }
+ }
+
+
+ /// <summary>
+ /// Deserializes the value after any type conversion has been applied.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="storedType">Type of the stored.</param>
+ /// <param name="container">The container.</param>
+ /// <returns></returns>
+ private object DeserializeValueAfterConversion(Type type, BSONTypes storedType, object container)
{
if (storedType == BSONTypes.Null)
{
@@ -0,0 +1,34 @@
+using Norm.Configuration;
+
+
+namespace Norm.BSON
+{
+ public class BsonSerializerBase
+ {
+ private static IMongoConfigurationMap _configuration;
+ protected static IMongoConfigurationMap Configuration
+ {
+ get
+ {
+ if (_configuration != null)
+ return _configuration;
+ else
+ return MongoConfiguration.ConfigurationContainer;
+ }
+ set
+ {
+ _configuration = value;
+ }
+ }
+
+ /// <summary>
+ /// Use a specific configuration instance instead of the default.
+ /// </summary>
+ /// <remarks>This is by no way thread safe and is only intended for use in the internal automated tests.</remarks>
+ /// <param name="config"></param>
+ public static void UseConfiguration(IMongoConfigurationMap config)
+ {
+ Configuration = config;
+ }
+ }
+}
@@ -0,0 +1,12 @@
+using System;
+
+
+namespace Norm.BSON
+{
+ public interface IBsonTypeConverter
+ {
+ Type SerializedType { get; }
+ object ConvertToBson(object data);
+ object ConvertFromBson(object data);
+ }
+}
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+
+
+namespace Norm.BSON.TypeConverters
+{
+ public class CultureInfoTypeConverter : IBsonTypeConverter
+ {
+ #region IBsonTypeConverter Members
+
+ public Type SerializedType
+ {
+ get { return typeof(string); }
+ }
+
+ public object ConvertToBson(object data)
+ {
+ return ((CultureInfo)data).Name;
+ }
+
+ public object ConvertFromBson(object data)
+ {
+ return new CultureInfo((string)data);
+ }
+
+ #endregion
+ }
+}
@@ -1,11 +1,18 @@
+using System.Globalization;
+using Norm.BSON.TypeConverters;
namespace Norm.Configuration
{
/// <summary>
/// Mongo configuration container
/// </summary>
public class ConfigurationContainer : MongoConfigurationMap, IConfigurationContainer
{
+ public ConfigurationContainer()
+ {
+ TypeConverterFor<CultureInfo, CultureInfoTypeConverter>();
+ }
+
/// <summary>
/// Registers a Mongo Configuration Map by calling the default
/// constructor of T (so that's where you should add your mapping logic)
@@ -1,4 +1,6 @@
using System;
+using Norm.BSON;
+
namespace Norm.Configuration
{
@@ -22,6 +24,12 @@ public interface IMongoConfigurationMap : IHideObjectMembers
/// <typeparam retval="T">The type for which to remove fluent mappings.</typeparam>
void RemoveFor<T>();
+ void TypeConverterFor<TClr, TCnv>() where TCnv : IBsonTypeConverter, new();
+
+ IBsonTypeConverter GetTypeConverterFor(Type t);
+
+ void RemoveTypeConverterFor<TClr>();
+
/// <summary>
/// Gets the retval of the type's collection.
/// </summary>
Oops, something went wrong.

0 comments on commit 6072327

Please sign in to comment.