/// <summary> /// Mark property as not updatable. Not updatable properties /// will not be updated after inserted. Not updatable properties /// will not be checked against database to define if value of property /// has been changed. /// </summary> /// <exception cref="ArgumentException"> /// When not appropriate or already marked property is marked. /// </exception> /// <returns>Current ExtendedPropertyHelper.</returns> public ExtendedPropertyHelper <T> NotUpdatable() { var sourceType = typeof(T); if (!Property.PropertyType.GetUnderlyingType().IsBuiltinType()) { throw new ArgumentException(string.Format( "Property '{0}' for '{1}' is inappropriate property to set as not updatable.\n" + "Only properties underlying type of which is built in types can be set as not updatable.", Property.Name, typeof(T).Name)); } var markedNotUpdatable = MappingStorage.Instance.NotUpdatableProperties .Where(m => m.SourceType.Equals(typeof(T))) .FirstOrDefault(); if (markedNotUpdatable == null) { markedNotUpdatable = new PropertiesWithSource() { SourceType = typeof(T) } } ; var alreadyAdded = markedNotUpdatable.Properties .Any(m => Property.Name.Equals(m.Name)); if (alreadyAdded) { return(this); } markedNotUpdatable.Properties.Add(Property); if (!MappingStorage.Instance.NotUpdatableProperties.Contains(markedNotUpdatable)) { MappingStorage.Instance.NotUpdatableProperties.Add(markedNotUpdatable); } return(this); } }
/// <summary> /// Mark properties as unique. /// </summary> /// <exception cref="ArgumentNullException"> /// When lambda expression is null. /// </exception> /// <exception cref="ArgumentException"> /// When lambda expression deos not select any property. /// When lambda expression selects not appropriate properties. /// When lambda expression selects already selected combination /// of properties to set as unique. /// </exception> /// <typeparam name="TProperty">Type of property.</typeparam> /// <param name="propertyLambda">Lambda expression to mark properties as unique.</param> public void HasUnique <TProperty>( Expression <Func <TEntity, TProperty> > propertyLambda) { if (propertyLambda == null) { throw new ArgumentNullException("propertyLambda"); } var markedProperties = propertyLambda.GetPropertyInfoList(); if (markedProperties == null || markedProperties.Count == 0) { throw new ArgumentException(string.Format( "Expression '{0}' for '{1}' marks no property to set as unique", propertyLambda.ToString(), typeof(TEntity).Name)); } // Selects properties which are not appropriate to set as unique var violatedProperties = markedProperties .Where(m => m.PropertyType.IsCollectionType() || (!m.PropertyType.IsBuiltinType() && (!m.PropertyType.IsEnum && !m.PropertyType.IsNullableEnum()))); if (violatedProperties != null && violatedProperties.Count() > 0) { throw new ArgumentException(string.Format( "Expression '{0}' for '{1}' selects inappropriate properties to set unique.\n" + "Only built in value types or enums can be set as unique.", propertyLambda.ToString(), typeof(TEntity).Name)); } var markedAsUnique = new PropertiesWithSource() { SourceType = typeof(TEntity), Properties = markedProperties }; var duplicates = MappingStorage.Instance.UniqueProperties .Where(m => m.SourceType.Equals(markedAsUnique.SourceType) && m.Properties .Select(p => p.Name) .OrderBy(p => p) .SequenceEqual(markedAsUnique .Properties .Select(p => p.Name) .OrderBy(p => p))); if (duplicates.Any()) { return; } if (!MappingStorage.Instance.UniqueProperties.Contains(markedAsUnique)) { MappingStorage.Instance.UniqueProperties.Add(markedAsUnique); } }
/// <summary> /// Mark properties state of which has to be defined before entity itself /// in order to be able to correctly define state of entity itself. These properties /// are those uniqueness of which can be determined easily according to their /// property values. Properties from which state of entity is dependant from /// and which are in one-to-one relationship with this entity should be marked. /// Parents of entity or entities which are in many-to-one relationship with this entity /// should not be marked, they are automatically ordered. /// </summary> /// <exception cref="ArgumentNullException"> /// When lambda expression is null. /// </exception> /// <exception cref="ArgumentException"> /// When lambda expression selects no property at all. /// When lambda expression selects inappropriate properties to define state of. /// When lambda expression selects already selected property to define state of. /// </exception> /// <typeparam name="TProperty">Type of property.</typeparam> /// <param name="propertyLambda">Lambda expression to get properties /// state of which must be defined.</param> public void HasStateDefiner <TProperty>( Expression <Func <TEntity, TProperty> > propertyLambda) { if (propertyLambda == null) { throw new ArgumentNullException("propertyLambda"); } var markedProperties = propertyLambda.GetPropertyInfoList(); if (markedProperties == null || markedProperties.Count == 0) { throw new ArgumentException(string.Format( "Expression '{0}' for '{1}' marks no property to define state of", propertyLambda.ToString(), typeof(TEntity).Name)); } // Selects properties which are not appropriate to set as to define sate of var violatedProperties = markedProperties .Where(m => m.PropertyType.IsValueType || (m.PropertyType.IsBuiltinType() && m.PropertyType.IsCollectionType() && m.PropertyType.IsGenericType && (m.PropertyType .GenericTypeArguments .FirstOrDefault() .IsBuiltinType() || !m.PropertyType .GenericTypeArguments .FirstOrDefault() .IsClass))); if (violatedProperties != null && violatedProperties.Count() > 0) { throw new ArgumentException(string.Format( "Expression '{0}' for '{1}' selects inappropriate properties to define state of.\n" + "Only properties of user defined class type or collections of those classes" + "can be set to define state of", propertyLambda.ToString(), typeof(TEntity).Name)); } var markedToDefineStateOf = MappingStorage.Instance.StateDefiners .Where(m => m.SourceType.Equals(typeof(TEntity))) .FirstOrDefault(); if (markedToDefineStateOf == null) { markedToDefineStateOf = new PropertiesWithSource() { SourceType = typeof(TEntity) } } ; var alreadyAdded = markedToDefineStateOf.Properties .Any(m => markedProperties .Select(p => p.Name) .Contains(m.Name)); if (alreadyAdded) { return; } markedToDefineStateOf.Properties.AddRange(markedProperties); if (!MappingStorage.Instance.StateDefiners.Contains(markedToDefineStateOf)) { MappingStorage.Instance.StateDefiners.Add(markedToDefineStateOf); } }
/// <summary> /// Compare entity and entity from source and detect which properties /// have been changed. Prepare changed proeprties for update. /// </summary> /// <exception cref="ArgumentNullException"> /// When entity or entityFromSource is null. /// </exception> /// <param name="entity">Current entity to compare.</param> /// <param name="entityFromSource">Entity from source to compare.</param> /// <returns>True if any of propeties has been changed/False otherwise.</returns> public bool DetectPropertyChanges( TEntity entity, TEntity entityFromSource) { if (entity == null) { throw new ArgumentNullException(nameof(entity)); } if (entityFromSource == null) { throw new ArgumentNullException(nameof(entityFromSource)); } bool anyPropertyHasChanged = false; IEnumerable <string> primaryKeyNames = GetPrimaryKeys(); PropertiesWithSource notToCompareConfiguration = MappingStorage .Instance .PropertiesNotToCompare .Where(m => m.SourceType.Equals(typeof(TEntity))) .FirstOrDefault(); PropertiesWithSource notUpdatableConfiguration = MappingStorage .Instance .NotUpdatableProperties .Where(m => m.SourceType.Equals(typeof(TEntity))) .FirstOrDefault(); IEnumerable <string> propertiesNotToCompare; IEnumerable <string> notUpdatableProperties; if (notToCompareConfiguration != null) { propertiesNotToCompare = notToCompareConfiguration .Properties .Select(m => m.Name); } else { propertiesNotToCompare = new List <string>(); } if (notUpdatableConfiguration != null) { notUpdatableProperties = notUpdatableConfiguration .Properties .Select(m => m.Name); } else { notUpdatableProperties = new List <string>(); } // Get properties to compare List <string> propertiesToCompare = GetSimpleEntityProperties() .Where(m => !primaryKeyNames.Contains(m.Name) && !propertiesNotToCompare.Contains(m.Name) && !notUpdatableProperties.Contains(m.Name)) .Select(m => m.Name) .ToList(); DbEntityEntry <TEntity> entry = Context.Entry(entity); foreach (string propertyName in propertiesToCompare) { var currentValue = entity.GetPropertyValue(propertyName); var sourceValue = entityFromSource.GetPropertyValue(propertyName); if (!Utilities.IsEqual( entity.GetPropertyValue(propertyName), entityFromSource.GetPropertyValue(propertyName))) { anyPropertyHasChanged = true; entry.Property(propertyName).IsModified = true; } } // If any properties has changed also update // properties which marked not to compare if (anyPropertyHasChanged) { foreach (string propertyName in propertiesNotToCompare.Except(notUpdatableProperties)) { Context.Entry(entity) .Property(propertyName).IsModified = true; } } return(anyPropertyHasChanged); }