remove recursive set value on ToList and To
This commit is contained in:
parent
07ecc55433
commit
5e4c1ead0c
@ -117,12 +117,7 @@ namespace BinaryDad.Extensions
|
|||||||
|
|
||||||
var instance = Activator.CreateInstance(type);
|
var instance = Activator.CreateInstance(type);
|
||||||
|
|
||||||
/* 01/09/13 - SJK: Class properties can be decorated with our custom DataRowField attribute. This allows us to
|
properties.ForEach(p => SetValueFromRow(p, row, instance));
|
||||||
* declare a mapping between an object and a database column that are named differently. If the attribute is
|
|
||||||
* present on a property, we use its name for the customProperties collection. Otherwise, we use the name
|
|
||||||
* of the property as we assume the name on the database column is the same.
|
|
||||||
*/
|
|
||||||
properties.ForEach(p => RecursiveSetValue(p, row, instance));
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@ -281,66 +276,73 @@ namespace BinaryDad.Extensions
|
|||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
||||||
private static void RecursiveSetValue(PropertyInfo property, DataRow row, object instance, int maxDepth = 5, int currentDepth = 0)
|
private static string QuoteValue(string value) => string.Concat("\"", value.Replace("\"", "\"\""), "\"");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the value of a property using the matched column from the data row. Supports using <see cref="ColumnAttribute"/> and <see cref="NotMappedAttribute"/> for column mapping.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="property"></param>
|
||||||
|
/// <param name="row"></param>
|
||||||
|
/// <param name="instance"></param>
|
||||||
|
private static void SetValueFromRow(PropertyInfo property, DataRow row, object instance)
|
||||||
{
|
{
|
||||||
#region Max Depth
|
if (property == null)
|
||||||
|
|
||||||
//Ensure we prevent too complex objects. This could be a sign of poor design or a condition prompting infinite recursion.
|
|
||||||
if (currentDepth > maxDepth)
|
|
||||||
{
|
{
|
||||||
throw new MaxRecursionException($"The currentDepth {currentDepth} may not exceed the maxDepth {maxDepth} for recursion. Check the complexity of the object and its implementation of the DataRowPopulate attribute.");
|
throw new ArgumentNullException(nameof(property));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
// ignore the property if specified
|
||||||
|
if (property.HasCustomAttribute<NotMappedAttribute>())
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// ignore the property if specified
|
return;
|
||||||
if (property.HasCustomAttribute<NotMappedAttribute>())
|
}
|
||||||
|
|
||||||
|
if (row == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the column name from the row
|
||||||
|
var matchedColumnName = property.GetDataColumnName(row.Table.Columns);
|
||||||
|
|
||||||
|
//Set the value if we matched a column. If we don't have a match, there's simply no way to set a value.
|
||||||
|
if (matchedColumnName != null)
|
||||||
|
{
|
||||||
|
// raw value from the row, matching on column name
|
||||||
|
var tableValue = row[matchedColumnName];
|
||||||
|
|
||||||
|
// RJP - we'll come back to this
|
||||||
|
tableValue = ApplyTypeConversion(property, tableValue);
|
||||||
|
|
||||||
|
// if DBNull, set as null
|
||||||
|
var value = tableValue == DBNull.Value ? null : tableValue;
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
{
|
{
|
||||||
return;
|
try
|
||||||
}
|
|
||||||
|
|
||||||
// if we found DataRowPopulate attributes, we go a level deeper
|
|
||||||
if (property.HasCustomAttribute<DataRowPopulateAttribute>())
|
|
||||||
{
|
|
||||||
currentDepth++;
|
|
||||||
|
|
||||||
// get all properties on this type and continue if we have any
|
|
||||||
var subProperties = property.PropertyType.GetProperties();
|
|
||||||
|
|
||||||
if (subProperties.AnyAndNotNull())
|
|
||||||
{
|
{
|
||||||
// since we have properties, we attempt to create an item (e.g. Address) based on the type of the current p
|
property.SetValue(instance, value.To(property.PropertyType), null);
|
||||||
var subItem = Activator.CreateInstance(Type.GetType(property.PropertyType.AssemblyQualifiedName));
|
|
||||||
|
|
||||||
// attempt to set each value on subItem (e.g. populating Street1, Street2 on Address).
|
|
||||||
subProperties.ForEach(p2 => RecursiveSetValue(p2, row, subItem, maxDepth, currentDepth));
|
|
||||||
|
|
||||||
// now that subItem (instance of Address) has been populated, we in turn need to populate the current p (Address) on item.
|
|
||||||
property.SetValue(instance, subItem, null);
|
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
property.SetValue(row, instance);
|
// encapsulate more detail about why the conversion failed
|
||||||
|
throw new DataPropertyConversionException(instance, property, value, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
property.SetValue(row, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (MaxRecursionException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new Exception($"Property {property.Name} cannot be set from row.", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string QuoteValue(string value) => string.Concat("\"", value.Replace("\"", "\"\""), "\"");
|
private static object ApplyTypeConversion(PropertyInfo property, object sourceValue)
|
||||||
|
{
|
||||||
|
var converter = property.GetAttributeTypeConverter();
|
||||||
|
|
||||||
|
if (converter != null && converter.CanConvertFrom(sourceValue.GetType()))
|
||||||
|
{
|
||||||
|
sourceValue = converter.ConvertFrom(sourceValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceValue;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -168,6 +169,30 @@ namespace BinaryDad.Extensions
|
|||||||
.FirstOrDefault(f => columns.Contains(f));
|
.FirstOrDefault(f => columns.Contains(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an instance of the <see cref="TypeConverter"/> associated with the property's <see cref="TypeConverterAttribute"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="property"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static TypeConverter GetAttributeTypeConverter(this PropertyInfo property)
|
||||||
|
{
|
||||||
|
var converter = property.GetCustomAttribute<TypeConverterAttribute>();
|
||||||
|
|
||||||
|
if (converter != null)
|
||||||
|
{
|
||||||
|
var converterType = Type.GetType(converter.ConverterTypeName);
|
||||||
|
|
||||||
|
if (converterType == typeof(EnumConverter))
|
||||||
|
{
|
||||||
|
return (TypeConverter)Activator.CreateInstance(converterType, property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (TypeConverter)Activator.CreateInstance(converterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user