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.Tests/MongoConfigurationTests.cs b/NoRM.Tests/MongoConfigurationTests.cs index b31bab8f..8ebd20f5 100644 --- a/NoRM.Tests/MongoConfigurationTests.cs +++ b/NoRM.Tests/MongoConfigurationTests.cs @@ -226,6 +226,40 @@ public void Are_Queries_Fully_Linqified() } } + [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/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.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/BsonDeserializer.cs b/NoRM/BSON/BsonDeserializer.cs index 26ac2d9c..c3d3bcd1 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(propertyType, 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/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..e1701614 100644 --- a/NoRM/BSON/ReflectionHelper.cs +++ b/NoRM/BSON/ReflectionHelper.cs @@ -244,7 +244,8 @@ private static IDictionary LoadMagicProperties(IEnumerabl 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/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 830d9f93..1af077b4 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 @@ -547,6 +554,18 @@ 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. /// @@ -879,19 +898,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]()); } } diff --git a/NoRM/Configuration/IMongoConfigurationMap.cs b/NoRM/Configuration/IMongoConfigurationMap.cs index a98e80ac..c5f67045 100644 --- a/NoRM/Configuration/IMongoConfigurationMap.cs +++ b/NoRM/Configuration/IMongoConfigurationMap.cs @@ -54,6 +54,13 @@ public interface IMongoConfigurationMap : IHideObjectMembers /// 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); } } \ No newline at end of file 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/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..92ce2267 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,30 @@ 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 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..78289e09 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 ) { @@ -137,6 +137,41 @@ public string GetPropertyAlias(Type type, string 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/MongoTypeConfigurationGeneric.cs b/NoRM/Configuration/MongoTypeConfigurationGeneric.cs index acff7c72..c9daca51 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(); + MongoConfiguration.FireTypeChangedEvent(typeof(T)); } /// diff --git a/NoRM/Configuration/PropertyMappingExpression.cs b/NoRM/Configuration/PropertyMappingExpression.cs index e4199570..66f2a1be 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. @@ -34,5 +39,20 @@ 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 diff --git a/NoRM/NoRM.csproj b/NoRM/NoRM.csproj index b1406fc2..20b418a3 100644 --- a/NoRM/NoRM.csproj +++ b/NoRM/NoRM.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -112,6 +112,7 @@ +