From 9f6cbd86e3b0e02962e983de0a494c74aafa4ba6 Mon Sep 17 00:00:00 2001 From: csteeg Date: Thu, 7 Oct 2010 21:36:03 +0200 Subject: [PATCH 1/8] First checkin to allow ignoring of properties fluently --- NoRM.Tests/MongoConfigurationTests.cs | 10 +++++ NoRM.Tests/NoRM.Tests.csproj | 4 +- NoRM/BSON/ReflectionHelper.cs | 3 +- NoRM/Configuration/IMongoConfigurationMap.cs | 1 + .../ITypedConfigurationGeneric.cs | 6 +++ NoRM/Configuration/MongoConfiguration.cs | 16 ++++++++ NoRM/Configuration/MongoConfigurationMap.cs | 15 +++++++ .../MongoTypeConfigurationGeneric.cs | 39 ++++++++++++++----- .../PropertyMappingExpression.cs | 9 +++++ NoRM/NoRM.csproj | 4 +- 10 files changed, 93 insertions(+), 14 deletions(-) diff --git a/NoRM.Tests/MongoConfigurationTests.cs b/NoRM.Tests/MongoConfigurationTests.cs index b31bab8f..7a1c087b 100644 --- a/NoRM.Tests/MongoConfigurationTests.cs +++ b/NoRM.Tests/MongoConfigurationTests.cs @@ -115,6 +115,16 @@ public void Mongo_Configuration_Can_Remove_Mapping() Assert.AreEqual("LastName", MongoConfiguration.GetPropertyAlias(typeof(User), "LastName")); } + [Test] + public void Mongo_Configuration_Can_Ignore_Property() + { + MongoConfiguration.Initialize(r => r.For(u => u.IgnoreProperty(h => h.LastName))); + //confirm that MagicProperty is not created + var reflectionHelper = new ReflectionHelper(typeof (User)); + Assert.True(reflectionHelper.GetProperties().Any(p => p.Name == "FirstName")); + Assert.False(reflectionHelper.GetProperties().Any(p => p.Name == "LastName")); + } + [Test] public void Mongo_Configuration_Remove_Mapping_Of_Norm_Types_Fails() { diff --git a/NoRM.Tests/NoRM.Tests.csproj b/NoRM.Tests/NoRM.Tests.csproj index 41a9fb9c..cb967ac5 100644 --- a/NoRM.Tests/NoRM.Tests.csproj +++ b/NoRM.Tests/NoRM.Tests.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU diff --git a/NoRM/BSON/ReflectionHelper.cs b/NoRM/BSON/ReflectionHelper.cs index e15ecfcc..e1701614 100644 --- a/NoRM/BSON/ReflectionHelper.cs +++ b/NoRM/BSON/ReflectionHelper.cs @@ -244,7 +244,8 @@ public static PropertyInfo FindIdProperty(Type type) foreach (var property in properties) { if (property.GetCustomAttributes(_ignoredType, true).Length > 0 || - property.GetIndexParameters().Length > 0) + property.GetIndexParameters().Length > 0 || + MongoConfiguration.IsPropertyIgnored(property.DeclaringType, property.Name)) { continue; } diff --git a/NoRM/Configuration/IMongoConfigurationMap.cs b/NoRM/Configuration/IMongoConfigurationMap.cs index a98e80ac..8afe8065 100644 --- a/NoRM/Configuration/IMongoConfigurationMap.cs +++ b/NoRM/Configuration/IMongoConfigurationMap.cs @@ -55,5 +55,6 @@ public interface IMongoConfigurationMap : IHideObjectMembers string GetPropertyAlias(Type type, string propertyName); string GetTypeDescriminator(Type type); + bool IsPropertyIgnored(Type type, string propertyName); } } \ No newline at end of file diff --git a/NoRM/Configuration/ITypedConfigurationGeneric.cs b/NoRM/Configuration/ITypedConfigurationGeneric.cs index 5f3579db..d1d66178 100644 --- a/NoRM/Configuration/ITypedConfigurationGeneric.cs +++ b/NoRM/Configuration/ITypedConfigurationGeneric.cs @@ -25,5 +25,11 @@ public interface ITypeConfiguration : ITypeConfiguration /// The unconventional Id propery. /// void IdIs(Expression> idProperty); + + /// + /// Ignores the given property + /// + ///The property to ignore during serialization + void IgnoreProperty(Expression> property); } } \ No newline at end of file diff --git a/NoRM/Configuration/MongoConfiguration.cs b/NoRM/Configuration/MongoConfiguration.cs index 9319f38f..36f1209e 100644 --- a/NoRM/Configuration/MongoConfiguration.cs +++ b/NoRM/Configuration/MongoConfiguration.cs @@ -1,4 +1,5 @@ using System; +using Norm.Attributes; using Norm.BSON; namespace Norm.Configuration @@ -16,6 +17,7 @@ public static class MongoConfiguration { internal static event Action TypeConfigurationChanged; + private static readonly Type _ignoredType = typeof(MongoIgnoreAttribute); private static readonly object _objectLock = new object(); private static IConfigurationContainer _configuration; @@ -103,6 +105,20 @@ public static string GetPropertyAlias(Type type, string propertyName) return _configuration != null ? _configuration.GetConfigurationMap().GetPropertyAlias(type, propertyName) : propertyName; } + /// + /// Given the type, and the propertyname, + /// see if it should be ignored for serialization + /// + /// The type. + /// Name of the property. + /// + /// True if the property should be ignored; false otherwise + /// + public static bool IsPropertyIgnored(Type type, string propertyName) + { + return (_configuration != null ? _configuration.GetConfigurationMap().IsPropertyIgnored(type, propertyName) : false); + } + public static IBsonTypeConverter GetBsonTypeConverter(Type t) { return _configuration != null ? _configuration.GetTypeConverterFor(t) : null; diff --git a/NoRM/Configuration/MongoConfigurationMap.cs b/NoRM/Configuration/MongoConfigurationMap.cs index 1230515a..b2633543 100644 --- a/NoRM/Configuration/MongoConfigurationMap.cs +++ b/NoRM/Configuration/MongoConfigurationMap.cs @@ -137,6 +137,21 @@ public string GetPropertyAlias(Type type, string propertyName) return retval; } + /// + /// Given the type, and the propertyname, + /// see if it should be ignored for serialization + /// + /// The type. + /// Name of the property. + /// + /// True if the property should be ignored; false otherwise + /// + public bool IsPropertyIgnored(Type type, string propertyName) + { + var map = MongoTypeConfiguration.PropertyMaps; + return map.ContainsKey(type) && map[type].ContainsKey(propertyName) && map[type][propertyName].Ignore; + } + /// /// Gets the fluently configured discriminator type string for a type. /// diff --git a/NoRM/Configuration/MongoTypeConfigurationGeneric.cs b/NoRM/Configuration/MongoTypeConfigurationGeneric.cs index acff7c72..bd08deb3 100644 --- a/NoRM/Configuration/MongoTypeConfigurationGeneric.cs +++ b/NoRM/Configuration/MongoTypeConfigurationGeneric.cs @@ -21,6 +21,19 @@ private void CheckForPropertyMap(Type typeKey) } } + private PropertyMappingExpression CheckForPropertyExpression(Expression> idProperty) + { + var propertyName = ReflectionHelper.FindProperty(idProperty); + var typeKey = typeof(T); + CheckForPropertyMap(typeKey); + var expression = PropertyMaps[typeKey].ContainsKey(propertyName) ? + PropertyMaps[typeKey][propertyName] : + new PropertyMappingExpression(); + PropertyMaps[typeKey][propertyName] = expression; + + return expression; + } + /// /// Looks up property names for use with aliases. /// @@ -28,12 +41,10 @@ private void CheckForPropertyMap(Type typeKey) /// public IPropertyMappingExpression ForProperty(Expression> sourcePropery) { - var propertyName = ReflectionHelper.FindProperty(sourcePropery); - var typeKey = typeof(T); - CheckForPropertyMap(typeKey); - var expression = new PropertyMappingExpression { SourcePropertyName = propertyName }; - PropertyMaps[typeKey][propertyName] = expression; + var expression = CheckForPropertyExpression(sourcePropery); + expression.SourcePropertyName = ReflectionHelper.FindProperty(sourcePropery); MongoConfiguration.FireTypeChangedEvent(typeof(T)); + return expression; } @@ -44,10 +55,20 @@ public IPropertyMappingExpression ForProperty(Expression> source /// public void IdIs(Expression> idProperty) { - var propertyName = ReflectionHelper.FindProperty(idProperty); - var typeKey = typeof (T); - CheckForPropertyMap(typeKey); - PropertyMaps[typeKey][propertyName] = new PropertyMappingExpression {IsId = true}; + var expression = CheckForPropertyExpression(idProperty); + expression.IsId = true; + MongoConfiguration.FireTypeChangedEvent(typeof(T)); + } + + /// + /// Ignores the given property + /// + ///The property to ignore during serialization + public void IgnoreProperty(Expression> property) + { + var expression = CheckForPropertyExpression(property); + expression.Ignore = true; + MongoConfiguration.FireTypeChangedEvent(typeof(T)); } /// diff --git a/NoRM/Configuration/PropertyMappingExpression.cs b/NoRM/Configuration/PropertyMappingExpression.cs index e4199570..d8626edc 100644 --- a/NoRM/Configuration/PropertyMappingExpression.cs +++ b/NoRM/Configuration/PropertyMappingExpression.cs @@ -1,4 +1,6 @@  +using System; + namespace Norm.Configuration { /// @@ -18,12 +20,19 @@ public class PropertyMappingExpression : IPropertyMappingExpression /// True if the property is the entity's Id. internal bool IsId { get; set; } + /// + /// Gets or sets whether the property should be ignored. + /// + /// True if the property is to be ignored. + internal bool Ignore { get; set; } + /// /// Gets or sets the retval of the source property. /// /// The retval of the source property. public string SourcePropertyName { get; set; } + /// /// Uses the alias for a given type's property. /// diff --git a/NoRM/NoRM.csproj b/NoRM/NoRM.csproj index b1406fc2..c5a64f16 100644 --- a/NoRM/NoRM.csproj +++ b/NoRM/NoRM.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU From f0db4f599c9e27ece63949b0b5aca0e898a4edf3 Mon Sep 17 00:00:00 2001 From: csteeg Date: Fri, 8 Oct 2010 13:54:04 +0200 Subject: [PATCH 2/8] Allow non-nullable Id's to use the HiLoIdGenerator as well --- NoRM/Collections/MongoCollectionGeneric.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NoRM/Collections/MongoCollectionGeneric.cs b/NoRM/Collections/MongoCollectionGeneric.cs index 830d9f93..29608395 100644 --- a/NoRM/Collections/MongoCollectionGeneric.cs +++ b/NoRM/Collections/MongoCollectionGeneric.cs @@ -879,6 +879,8 @@ private void TrySettingId(IEnumerable entities) Dictionary> knownTypes = new Dictionary> { { typeof(long?), () => GenerateId() }, { typeof(int?), () => Convert.ToInt32(GenerateId()) }, + { typeof(long), () => GenerateId() }, + { typeof(int), () => Convert.ToInt32(GenerateId()) }, { typeof(ObjectId), () => ObjectId.NewObjectId() } }; From 0b9aa28dc4a2a018cc833e3a38528b6f74d993cd Mon Sep 17 00:00:00 2001 From: csteeg Date: Fri, 8 Oct 2010 17:24:31 +0200 Subject: [PATCH 3/8] Fix for IList, IEnumerable, ICollections properties to be restored by using the current value of the property --- NoRM/BSON/BsonDeserializer.cs | 63 +++++++++++++--------- NoRM/Collections/MongoCollectionGeneric.cs | 2 +- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/NoRM/BSON/BsonDeserializer.cs b/NoRM/BSON/BsonDeserializer.cs index 26ac2d9c..d7b6c777 100644 --- a/NoRM/BSON/BsonDeserializer.cs +++ b/NoRM/BSON/BsonDeserializer.cs @@ -159,33 +159,42 @@ private void NewDocument(int length) /// Type of the stored. /// private object DeserializeValue(Type type, BSONTypes storedType) - { - return DeserializeValue(type, storedType, null); - } - - /// - /// Applies optional type conversion and deserializes the value. - /// - /// The type. - /// Type of the stored. - /// The container. - /// - 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); + object value = DeserializeValueAfterConversion(serializedType, storedType); return converter.ConvertFromBson(value); } else { - return DeserializeValueAfterConversion(type, storedType, container); - } + return DeserializeValueAfterConversion(type, storedType); + } + } + + /// + /// Applies optional type conversion and deserializes the value. + /// + /// The type. + /// Type of the stored. + /// The container. + /// + private object DeserializeList(Type type, object container) + { + IBsonTypeConverter converter = Configuration.GetTypeConverterFor(type); + if (converter != null) + { + Type serializedType = converter.SerializedType; + object value = ReadList(serializedType, container); + return converter.ConvertFromBson(value); + } + else + { + return ReadList(type, container); + } } - /// /// Deserializes the value after any type conversion has been applied. /// @@ -193,7 +202,7 @@ private object DeserializeValue(Type type, BSONTypes storedType, object containe /// Type of the stored. /// The container. /// - private object DeserializeValueAfterConversion(Type type, BSONTypes storedType, object container) + private object DeserializeValueAfterConversion(Type type, BSONTypes storedType) { if (storedType == BSONTypes.Null) { @@ -226,7 +235,7 @@ private object DeserializeValueAfterConversion(Type type, BSONTypes storedType, } if (_IEnumerableType.IsAssignableFrom(type) || storedType == BSONTypes.Array) { - return ReadList(type, container); + return ReadList(type, null); } if (type == typeof(bool)) { @@ -342,18 +351,22 @@ private object ReadObject(Type type) NewDocument(length); } } - object container = null; - if (property != null && property.Setter == null) - { - container = property.Getter(instance); + + var propertyType = property != null ? property.Type : _typeMap.ContainsKey(storageType) ? _typeMap[storageType] : typeof(object); + object value = null; + if (!isNull) + { + if (_IEnumerableType.IsAssignableFrom(type) || storageType == BSONTypes.Array) + value = DeserializeList(type, property.Getter(instance)); + else + value = DeserializeValue(propertyType, storageType); } - var propertyType = property != null ? property.Type : _typeMap.ContainsKey(storageType) ? _typeMap[storageType] : typeof(object); - var value = isNull ? null : DeserializeValue(propertyType, storageType, container); + if (property == null) { ((IExpando)instance)[name] = value; } - else if (container == null && value != null && property.Setter != null) + else if (property.Setter != null) { property.Setter(instance, value); } diff --git a/NoRM/Collections/MongoCollectionGeneric.cs b/NoRM/Collections/MongoCollectionGeneric.cs index 29608395..06f65b08 100644 --- a/NoRM/Collections/MongoCollectionGeneric.cs +++ b/NoRM/Collections/MongoCollectionGeneric.cs @@ -892,7 +892,7 @@ private void TrySettingId(IEnumerable entities) foreach (var entity in entities) { var value = idProperty.Getter(entity); - if (value == null) + if (value == null || idProperty.Type.IsValueType && value.Equals(Activator.CreateInstance(idProperty.Type))) { idProperty.Setter(entity, knownTypes[idProperty.Type]()); } From 46d50dad9cc32bb9377ad864bfe71ebbccedcfeb Mon Sep 17 00:00:00 2001 From: "tadeusz.wojcik" Date: Mon, 11 Oct 2010 23:50:22 +0200 Subject: [PATCH 4/8] added support for not nullable int and long to be id property generated by HiLo when that property equals zero --- NoRM.Tests/MongoCollectionTests.cs | 86 ++++++++++++++++++++++ NoRM/Collections/MongoCollectionGeneric.cs | 23 ++++-- 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/NoRM.Tests/MongoCollectionTests.cs b/NoRM.Tests/MongoCollectionTests.cs index 163a859b..8f30e56c 100644 --- a/NoRM.Tests/MongoCollectionTests.cs +++ b/NoRM.Tests/MongoCollectionTests.cs @@ -382,8 +382,86 @@ public void InsertingANewEntityWithNullableIntGeneratesAKey() Assert.NotNull(testint._id); Assert.AreNotEqual(0, testint._id.Value); } + } + + + + [Test] + public void when_inserting_a_new_entity_with_int_id_equals_zero_generates_a_key() + { + using (var mongo = Mongo.Create(TestHelper.ConnectionString())) + { + var testint = new IntId {Name = "first"} ; + mongo.GetCollection("Fake").Insert(testint); + + Assert.NotNull(testint.Id); + Assert.AreNotEqual(0, testint.Id); + } + } + + [Test] + public void when_inserting_a_22_new_entities_with_int_id_equals_zero_generates_a_unique_key_for_each_of_them() + { + using (var mongo = Mongo.Create(TestHelper.ConnectionString())) + { + var idents = new List(); + for (int i = 0; i <22; i++) + { + var testint = new IntId() ; + mongo.GetCollection("Fake").Insert(testint); + idents.Add(testint.Id); + } + + var list = mongo.GetCollection("Fake").Find(new { _id = Q.In(idents.ToArray()) }); + + foreach (var item in list) + { + Assert.True(idents.Contains(item.Id)); + } + + Assert.AreEqual(idents.Distinct().Count(), list.Select(x => x.Id).Distinct().Count()); + } + } + + + [Test] + public void when_inserting_a_new_entity_with_long_type_id_equals_zero_generates_a_key() + { + using (var mongo = Mongo.Create(TestHelper.ConnectionString())) + { + var testint = new LongId() { Name = "first" }; + mongo.GetCollection("Fake").Insert(testint); + + Assert.NotNull(testint.Id); + Assert.AreNotEqual(0, testint.Id); + } + } + + [Test] + public void when_inserting_a_22_new_entities_with_long_type_id_equals_zero_generates_a_unique_key_for_each_of_them() + { + using (var mongo = Mongo.Create(TestHelper.ConnectionString())) + { + var idents = new List(); + for (int i = 0; i < 22; i++) + { + var testint = new LongId(); + mongo.GetCollection("Fake").Insert(testint); + idents.Add(testint.Id); + } + + var list = mongo.GetCollection("Fake").Find(new { _id = Q.In(idents.ToArray()) }); + + foreach (var item in list) + { + Assert.True(idents.Contains(item.Id)); + } + + Assert.AreEqual(idents.Distinct().Count(), list.Select(x => x.Id).Distinct().Count()); + } } + [Test] public void InsertingANewEntityWithNullableIntGeneratesAKeyComplex() { @@ -520,6 +598,8 @@ public void StringAsIdentifierDoesTranslation() } } + + private class StringIdentifier { [MongoIdentifier] @@ -531,6 +611,12 @@ private class IntId { public int Id { get; set; } public string Name { get; set; } + } + + private class LongId + { + public long Id { get; set; } + public string Name { get; set; } } } } \ No newline at end of file diff --git a/NoRM/Collections/MongoCollectionGeneric.cs b/NoRM/Collections/MongoCollectionGeneric.cs index 830d9f93..e8b75baf 100644 --- a/NoRM/Collections/MongoCollectionGeneric.cs +++ b/NoRM/Collections/MongoCollectionGeneric.cs @@ -67,7 +67,7 @@ public IQueryable AsQueryable() /// /// True if the type of this collection can be updated /// (i.e. the Type specifies "_id", "ID", or a property with the attributed "MongoIdentifier"). - /// + /// |?/**/ public bool Updateable { get @@ -106,11 +106,18 @@ public void Save(T entity) var helper = TypeHelper.GetHelperForType(typeof(T)); var idProperty = helper.FindIdProperty(); var id = idProperty.Getter(entity); - if (id == null && - (typeof(ObjectId).IsAssignableFrom(idProperty.Type)) || + if ((id == null && + ((typeof(ObjectId).IsAssignableFrom(idProperty.Type)) || (typeof(long?).IsAssignableFrom(idProperty.Type)) || - (typeof(int?).IsAssignableFrom(idProperty.Type)) ) + (typeof(int?).IsAssignableFrom(idProperty.Type)))) || + + (idProperty.Type == typeof(int) && id.Equals(0)) || + (idProperty.Type == typeof(long) && id.Equals((long)0)) + + ) { + + Insert(entity); } else @@ -879,19 +886,25 @@ private void TrySettingId(IEnumerable entities) Dictionary> knownTypes = new Dictionary> { { typeof(long?), () => GenerateId() }, { typeof(int?), () => Convert.ToInt32(GenerateId()) }, + { typeof(int), () => Convert.ToInt32(GenerateId()) }, + { typeof(long), () => GenerateId() }, { typeof(ObjectId), () => ObjectId.NewObjectId() } }; if (typeof(T) != typeof(Object) && typeof(T).GetInterface("IUpdateWithoutId") == null) { var idProperty = TypeHelper.GetHelperForType(typeof(T)).FindIdProperty(); + if (idProperty != null && knownTypes.ContainsKey(idProperty.Type) && idProperty.Setter != null) { foreach (var entity in entities) { var value = idProperty.Getter(entity); - if (value == null) + if (value == null || + (idProperty.Type==typeof(int) && value.Equals(0) )|| + idProperty.Type==typeof(long) && value.Equals((long)0)) { + idProperty.Setter(entity, knownTypes[idProperty.Type]()); } } From bb786cccc2524a91dc3c36ad060eeb950d38dfb7 Mon Sep 17 00:00:00 2001 From: "tadeusz.wojcik" Date: Tue, 12 Oct 2010 01:19:26 +0200 Subject: [PATCH 5/8] added fluent mapping methods which allow specify in configurationmappings ignored/ignoredifnull/immutable properties instead of attributes --- NoRM.Tests/MongoConfigurationTests.cs | 38 ++++++++++++++++- NoRM.Tests/TestClasses.cs | 19 +++++++++ NoRM/BSON/MagicProperty.cs | 12 ++++-- NoRM/BSON/ReflectionHelper.cs | 3 +- NoRM/Configuration/IMongoConfigurationMap.cs | 9 +++- .../IPropertyMappingExpression.cs | 18 +++++++- NoRM/Configuration/MongoConfiguration.cs | 17 ++++++++ NoRM/Configuration/MongoConfigurationMap.cs | 41 +++++++++++++++++-- NoRM/Configuration/MongoTypeConfiguration.cs | 1 + .../PropertyMappingExpression.cs | 22 +++++++++- 10 files changed, 167 insertions(+), 13 deletions(-) diff --git a/NoRM.Tests/MongoConfigurationTests.cs b/NoRM.Tests/MongoConfigurationTests.cs index b31bab8f..b83de724 100644 --- a/NoRM.Tests/MongoConfigurationTests.cs +++ b/NoRM.Tests/MongoConfigurationTests.cs @@ -224,8 +224,42 @@ public void Are_Queries_Fully_Linqified() Assert.AreEqual("Cart1", deepQuery[0].Cart.Name); Assert.AreEqual(1, deepQuery.Count); } - } - + } + + [Test] + public void should_ignore_name_property_when_inserting__as_specified_in_mappings() + { + + MongoConfiguration.Initialize(c => c.AddMap()); + using ( + Shoppers shoppers = + new Shoppers(Mongo.Create(TestHelper.ConnectionString("pooling=false", "test", null, null)))) + { + shoppers.Drop(); + shoppers.Add(new Shopper + { + Id = ObjectId.NewObjectId(), + Name = "John", + + }); + + shoppers.Add(new Shopper + { + Id = ObjectId.NewObjectId(), + Name = "Jane", + + }); + + + var deepQuery = shoppers.ToList(); + + Assert.IsNull(deepQuery[0].Name); + Assert.IsNull(deepQuery[1].Name); + + + } + } + [Test] public void Can_correctly_determine_collection_name() { diff --git a/NoRM.Tests/TestClasses.cs b/NoRM.Tests/TestClasses.cs index 920a4c5c..bbd153db 100644 --- a/NoRM.Tests/TestClasses.cs +++ b/NoRM.Tests/TestClasses.cs @@ -879,6 +879,25 @@ public ShopperMap() } } + public class ShopperMapWithIgnoreImmutableAndIgnoreIfNullConfigurationForProperties:MongoConfigurationMap + { + public ShopperMapWithIgnoreImmutableAndIgnoreIfNullConfigurationForProperties() + { + For( + config=> config.ForProperty(x => x.Name).Ignore() + ); + + For( + config=> + { + + config.ForProperty(x => x.Product).IgnoreIfNull(); + config.ForProperty(x => x.Name).Immutable(); + + } + ); + } + } internal class IdMap0 { public ObjectId _ID { get; set; } diff --git a/NoRM/BSON/MagicProperty.cs b/NoRM/BSON/MagicProperty.cs index 2ab64a55..aaa2d26a 100644 --- a/NoRM/BSON/MagicProperty.cs +++ b/NoRM/BSON/MagicProperty.cs @@ -1,8 +1,9 @@ using System; using System.Reflection; using Norm.Attributes; -using System.ComponentModel; - +using System.ComponentModel; +using Norm.Configuration; + namespace Norm.BSON { /// @@ -27,8 +28,11 @@ public class MagicProperty public MagicProperty(PropertyInfo property, Type declaringType) { _property = property; - this.IgnoreIfNull = property.GetCustomAttributes(_ignoredIfNullType, true).Length > 0; - this.Immutable = property.GetCustomAttributes(_immutableType, true).Length > 0; + this.IgnoreIfNull = property.GetCustomAttributes(_ignoredIfNullType, true).Length > 0 || + MongoConfiguration.IsPropertyIgnoredWhenNull(declaringType,property.Name); + this.Immutable = property.GetCustomAttributes(_immutableType, true).Length > 0 || + MongoConfiguration.IsPropertyImmutable(declaringType,property.Name); + var props = property.GetCustomAttributes(_defaultValueType, true); if (props.Length > 0) { diff --git a/NoRM/BSON/ReflectionHelper.cs b/NoRM/BSON/ReflectionHelper.cs index e15ecfcc..397e8d8b 100644 --- a/NoRM/BSON/ReflectionHelper.cs +++ b/NoRM/BSON/ReflectionHelper.cs @@ -244,7 +244,8 @@ public static PropertyInfo FindIdProperty(Type type) foreach (var property in properties) { if (property.GetCustomAttributes(_ignoredType, true).Length > 0 || - property.GetIndexParameters().Length > 0) + property.GetIndexParameters().Length > 0|| + MongoConfiguration.IsPropertyIgnored(property.DeclaringType,property.Name)) { continue; } diff --git a/NoRM/Configuration/IMongoConfigurationMap.cs b/NoRM/Configuration/IMongoConfigurationMap.cs index a98e80ac..8039cc4e 100644 --- a/NoRM/Configuration/IMongoConfigurationMap.cs +++ b/NoRM/Configuration/IMongoConfigurationMap.cs @@ -52,7 +52,14 @@ public interface IMongoConfigurationMap : IHideObjectMembers /// /// Type's property alias if configured; otherwise null /// - string GetPropertyAlias(Type type, string propertyName); + string GetPropertyAlias(Type type, string propertyName); + + bool IsPropertyIgnored(Type type, string propertyName); + + bool IsPropertyIgnoredWhenNull(Type type, string propertyName); + + bool IsPropertyImmutable(Type type, string propertyName); + string GetTypeDescriminator(Type type); } diff --git a/NoRM/Configuration/IPropertyMappingExpression.cs b/NoRM/Configuration/IPropertyMappingExpression.cs index 0c40dfe6..6d346232 100644 --- a/NoRM/Configuration/IPropertyMappingExpression.cs +++ b/NoRM/Configuration/IPropertyMappingExpression.cs @@ -18,6 +18,22 @@ public interface IPropertyMappingExpression : IHideObjectMembers /// /// The alias. /// - void UseAlias(string alias); + void UseAlias(string alias); + + /// + /// Ignores property if the value is null. + /// + void IgnoreIfNull(); + /// + /// Indicates that the BSON serializer should ignore property. + /// + void Ignore(); + + /// + /// Ignores property on updates, but not on inserts, i.e. this is write-once value + /// + /// + void Immutable(); + } } \ No newline at end of file diff --git a/NoRM/Configuration/MongoConfiguration.cs b/NoRM/Configuration/MongoConfiguration.cs index 9319f38f..6db7d3ea 100644 --- a/NoRM/Configuration/MongoConfiguration.cs +++ b/NoRM/Configuration/MongoConfiguration.cs @@ -101,8 +101,25 @@ public static void Initialize(Action action) public static string GetPropertyAlias(Type type, string propertyName) { return _configuration != null ? _configuration.GetConfigurationMap().GetPropertyAlias(type, propertyName) : propertyName; + } + + public static bool IsPropertyIgnored(Type type, string propertyName) + { + return _configuration != null ? _configuration.GetConfigurationMap().IsPropertyIgnored(type, propertyName) : false; + } + + public static bool IsPropertyIgnoredWhenNull(Type type, string propertyName) + { + return _configuration != null ? _configuration.GetConfigurationMap().IsPropertyIgnoredWhenNull(type, propertyName) : false; + } + + public static bool IsPropertyImmutable(Type type, string propertyName) + { + return _configuration != null ? _configuration.GetConfigurationMap().IsPropertyImmutable(type, propertyName) : false; } + + public static IBsonTypeConverter GetBsonTypeConverter(Type t) { return _configuration != null ? _configuration.GetTypeConverterFor(t) : null; diff --git a/NoRM/Configuration/MongoConfigurationMap.cs b/NoRM/Configuration/MongoConfigurationMap.cs index 1230515a..296bdaac 100644 --- a/NoRM/Configuration/MongoConfigurationMap.cs +++ b/NoRM/Configuration/MongoConfigurationMap.cs @@ -126,7 +126,7 @@ public string GetPropertyAlias(Type type, string propertyName) } else if (map.ContainsKey(type) && map[type].ContainsKey(propertyName)) { - retval = map[type][propertyName].Alias; + retval = map[type][propertyName].Alias??propertyName; } else if (discriminator != null && discriminator != type ) { @@ -135,8 +135,43 @@ public string GetPropertyAlias(Type type, string propertyName) retval = this.GetPropertyAlias(discriminator, propertyName); } return retval; - } - + } + + public bool IsPropertyIgnored(Type type, string propertyName) + { + var map = MongoTypeConfiguration.PropertyMaps; + var retValue = false; + if (map.ContainsKey(type) && map[type].ContainsKey(propertyName)) + { + retValue = map[type][propertyName].IsIgnored; + } + return retValue; + } + + public bool IsPropertyIgnoredWhenNull(Type type, string propertyName) + { + var map = MongoTypeConfiguration.PropertyMaps; + var retValue = false; + if (map.ContainsKey(type) && map[type].ContainsKey(propertyName)) + { + retValue = map[type][propertyName].IsIgnoredWhenNull; + } + return retValue; + } + + public bool IsPropertyImmutable(Type type, string propertyName) + { + var map = MongoTypeConfiguration.PropertyMaps; + var retValue = false; + if (map.ContainsKey(type) && map[type].ContainsKey(propertyName)) + { + retValue = map[type][propertyName].IsImmutable; + } + return retValue; + } + + + /// /// Gets the fluently configured discriminator type string for a type. /// diff --git a/NoRM/Configuration/MongoTypeConfiguration.cs b/NoRM/Configuration/MongoTypeConfiguration.cs index ad9835c7..b2aa0569 100644 --- a/NoRM/Configuration/MongoTypeConfiguration.cs +++ b/NoRM/Configuration/MongoTypeConfiguration.cs @@ -14,6 +14,7 @@ public class MongoTypeConfiguration private static readonly Dictionary _summaryTypes = new Dictionary(); private static readonly Dictionary _discriminatedTypes = new Dictionary(); + /// /// Gets the property maps. /// diff --git a/NoRM/Configuration/PropertyMappingExpression.cs b/NoRM/Configuration/PropertyMappingExpression.cs index e4199570..fe2b9b10 100644 --- a/NoRM/Configuration/PropertyMappingExpression.cs +++ b/NoRM/Configuration/PropertyMappingExpression.cs @@ -1,4 +1,6 @@  +using System; + namespace Norm.Configuration { /// @@ -11,6 +13,9 @@ public class PropertyMappingExpression : IPropertyMappingExpression /// /// The alias. internal string Alias { get; set; } + internal bool IsIgnored { get; set; } + internal bool IsIgnoredWhenNull { get; set; } + internal bool IsImmutable { get; set; } /// /// Gets or sets whether the property is the Id for the entity. @@ -33,6 +38,21 @@ public class PropertyMappingExpression : IPropertyMappingExpression public void UseAlias(string alias) { Alias = alias; - } + } + + public void IgnoreIfNull() + { + IsIgnoredWhenNull = true; + } + + public void Ignore() + { + IsIgnored = true; + } + + public void Immutable() + { + IsImmutable = true; + } } } \ No newline at end of file From 93442fef464e8538c0745053508d1eaa365b5012 Mon Sep 17 00:00:00 2001 From: "tadeusz.wojcik" Date: Wed, 13 Oct 2010 00:37:49 +0200 Subject: [PATCH 6/8] added CreateCompundIndex method --- NoRM/Collections/CreateIndexExpression.cs | 75 ++++++++++++++++++++++ NoRM/Collections/MongoCollectionGeneric.cs | 14 +++- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 NoRM/Collections/CreateIndexExpression.cs diff --git a/NoRM/Collections/CreateIndexExpression.cs b/NoRM/Collections/CreateIndexExpression.cs new file mode 100644 index 00000000..66e7ecbf --- /dev/null +++ b/NoRM/Collections/CreateIndexExpression.cs @@ -0,0 +1,75 @@ +using System; +using System.Linq.Expressions; +using Norm.BSON; +using Norm.Configuration; +using Norm.Protocol.Messages; + +namespace Norm.Collections +{ + class CreateIndexExpression : ICreateIndexExpression + { + public Expando Expando + { + get; + set; + } + + public string CompoundName + { + get; + set; + } + + public CreateIndexExpression() + { + Expando = new Expando(); + + } + public void Index(Expression> func, IndexOption indexDirection) + { + var propName = this.RecurseExpression(func.Body); + Expando[propName] = indexDirection; + CompoundName += propName + "_" + (int)indexDirection; + + } + private String RecurseExpression(Expression body) + { + var me = body as MemberExpression; + if (me != null) + { + return this.RecurseMemberExpression(me); + } + + var ue = body as UnaryExpression; + if (ue != null) + { + return this.RecurseExpression(ue.Operand); + } + + throw new MongoException("Unknown expression type, expected a MemberExpression or UnaryExpression."); + } + private String RecurseMemberExpression(MemberExpression mex) + { + var retval = ""; + var parentEx = mex.Expression as MemberExpression; + if (parentEx != null) + { + //we need to recurse because we're not at the root yet. + retval += this.RecurseMemberExpression(parentEx) + "."; + } + retval += MongoConfiguration.GetPropertyAlias(mex.Expression.Type, mex.Member.Name); + return retval; + } + + } + + public interface ICreateIndexExpression + { + // Methods + void Index(Expression> func, IndexOption indexDirection); + + // Properties + string CompoundName { get; set; } + Expando Expando { get; set; } + } +} \ No newline at end of file diff --git a/NoRM/Collections/MongoCollectionGeneric.cs b/NoRM/Collections/MongoCollectionGeneric.cs index e8b75baf..a7519df8 100644 --- a/NoRM/Collections/MongoCollectionGeneric.cs +++ b/NoRM/Collections/MongoCollectionGeneric.cs @@ -552,7 +552,19 @@ public void CreateGeoIndex(Expression> index, IEnumerable> indexes, bool isUnique) + { + ICreateIndexExpression expression = new CreateIndexExpression(); + indexes(expression); + + this.CreateIndex(expression.Expando, expression.CompoundName, isUnique); + + } /// /// Gets the distinct values for the specified fieldSelectionExpando. From a5033f2caeb49ec4848b2ffa4ab84fccd9a57763 Mon Sep 17 00:00:00 2001 From: chris van de steeg Date: Wed, 13 Oct 2010 20:34:54 +0200 Subject: [PATCH 7/8] Hmm, missed something during the merge --- NoRM/Configuration/MongoTypeConfigurationGeneric.cs | 2 +- NoRM/NoRM.csproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NoRM/Configuration/MongoTypeConfigurationGeneric.cs b/NoRM/Configuration/MongoTypeConfigurationGeneric.cs index bd08deb3..c9daca51 100644 --- a/NoRM/Configuration/MongoTypeConfigurationGeneric.cs +++ b/NoRM/Configuration/MongoTypeConfigurationGeneric.cs @@ -67,7 +67,7 @@ public void IdIs(Expression> idProperty) public void IgnoreProperty(Expression> property) { var expression = CheckForPropertyExpression(property); - expression.Ignore = true; + expression.Ignore(); MongoConfiguration.FireTypeChangedEvent(typeof(T)); } diff --git a/NoRM/NoRM.csproj b/NoRM/NoRM.csproj index c5a64f16..20b418a3 100644 --- a/NoRM/NoRM.csproj +++ b/NoRM/NoRM.csproj @@ -112,6 +112,7 @@ + From 38d7543504c98fd250dc37ddfaf87db8aed3e4dd Mon Sep 17 00:00:00 2001 From: chris van de steeg Date: Wed, 13 Oct 2010 21:11:48 +0200 Subject: [PATCH 8/8] Typo in the list-fix that actually broke deserializing Lists instead of fixing them :) IList, IEnumerable and ICollection as properties works now though.... --- NoRM/BSON/BsonDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NoRM/BSON/BsonDeserializer.cs b/NoRM/BSON/BsonDeserializer.cs index d7b6c777..c3d3bcd1 100644 --- a/NoRM/BSON/BsonDeserializer.cs +++ b/NoRM/BSON/BsonDeserializer.cs @@ -357,7 +357,7 @@ private object ReadObject(Type type) if (!isNull) { if (_IEnumerableType.IsAssignableFrom(type) || storageType == BSONTypes.Array) - value = DeserializeList(type, property.Getter(instance)); + value = DeserializeList(propertyType, property.Getter(instance)); else value = DeserializeValue(propertyType, storageType); }