/// <summary> /// Execute a query to copy and update an object. /// </summary> /// <typeparam name="TSource">Type of the object to 'copy and update'</typeparam> /// <param name="query">Query to execute</param> /// <param name="getPropertyOrFieldNameFromArgument"> /// Returns the field/property name corresponding to a specified argument name. /// If not specified, pascal case convention is used. /// Only useful if you use a different naming convention for your members ('m_' prefix for example) /// </param> /// <returns>New object, with updated values</returns> public static TSource Create <TSource>( this CopyUpdateQuery <TSource> query, Func <string, string> getPropertyOrFieldNameFromArgument = null) where TSource : class { getPropertyOrFieldNameFromArgument = getPropertyOrFieldNameFromArgument ?? PascalCase.Convert; var typeToBuild = typeof(TSource); // Check if unique constructor is available var typeInfo = typeToBuild.GetTypeInfo(); var ctorInfos = typeInfo.DeclaredConstructors.Where(ctor => !ctor.IsStatic).ToList(); if (1 != ctorInfos.Count()) { throw new InvalidOperationException( string.Format( "Type {0} must only contain one constructor", typeToBuild)); } // Get constructor parameters var ctorInfo = ctorInfos.First(); var ctorParams = ctorInfo.GetParameters(); var newValues = query.PropertyOrFieldValues.ToDictionary(keyValue => keyValue.Key, keyValue => keyValue.Value); // Get arguments values var arguments = new object[ctorParams.Length]; for (int index = 0; index < ctorParams.Length; ++index) { var arg = ctorParams[index]; var propertyOrFieldName = getPropertyOrFieldNameFromArgument(arg.Name); // Update value ? object newValue; if (newValues.TryGetValue(propertyOrFieldName, out newValue)) { arguments[index] = newValue; continue; } // Get property/field value var valueAccessor = AccessorProvider(typeToBuild, propertyOrFieldName); arguments[index] = valueAccessor(query.Source); } var constructor = ConstructorProvider(ctorInfo); return((TSource)constructor(arguments)); }
/// <summary> /// Creates a query to copy and update an object. /// </summary> /// <typeparam name="TSource">Type of the object to 'copy and update'</typeparam> /// <typeparam name="TPropertyOrField">Type of the field/property to update</typeparam> /// <param name="query">Current query to update</param> /// <param name="propertyOrFieldSelector">Selector on the field/property to update</param> /// <param name="value">New value for the field/property</param> /// <returns>Query used to create the new desired object</returns> public static CopyUpdateQuery <TSource> With <TSource, TPropertyOrField>( this CopyUpdateQuery <TSource> query, Expression <Func <TSource, TPropertyOrField> > propertyOrFieldSelector, TPropertyOrField value) where TSource : class { // Get field/property name accessed by the selector var propertyOrFieldName = GetReturnedPropertyOrFieldName(propertyOrFieldSelector); // Create query return(new CopyUpdateQuery <TSource>( query.Source, query.PropertyOrFieldValues.Concat(KeyValuePair.Create(propertyOrFieldName, (object)value)))); }