add AddRange, updates to ToDataTable, GetColumnAttributes, sqltypemap
This commit is contained in:
parent
d12750e1c4
commit
8a8c205855
@ -11,7 +11,7 @@
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Description>A set of common utilities and extension methods for .NET.</Description>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Version>21.4.20.1</Version>
|
||||
<Version>21.4.20.2</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -6,6 +6,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BinaryDad.Extensions
|
||||
{
|
||||
@ -24,6 +25,20 @@ namespace BinaryDad.Extensions
|
||||
items.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the elements of the specified collection to the end of the <see cref="ICollection{T}"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="items"></param>
|
||||
public static void AddRange<T>(this ICollection<T> source, IEnumerable<T> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
source.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a distinct list of elements using the first-matched item on a specific property
|
||||
/// </summary>
|
||||
@ -59,31 +74,82 @@ namespace BinaryDad.Extensions
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param> or <see cref="PropertyAliasAttribute"/>, if bound to a property.
|
||||
/// <param name="columnNameModifier">If a non-null string is returned, the column name is overridden</param>
|
||||
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
|
||||
/// <param name="tableName"></param>
|
||||
/// <returns></returns>
|
||||
public static DataTable ToDataTable<T>(this IEnumerable<T> collection, bool useColumnAttributeName = false, string tableName = null) where T : class
|
||||
public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, bool useColumnAttributeName = false, string tableName = null)
|
||||
{
|
||||
if (collection == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#region Get type of first record
|
||||
|
||||
// NOTE:
|
||||
// We assume all values are the same, so use the type from the first record.
|
||||
// This allows us to use the actual instance type instead of the generic version (useful for anonymous type collections)
|
||||
|
||||
Type type = null;
|
||||
var genericArguments = collection.GetType().GetGenericArguments();
|
||||
|
||||
if (genericArguments.Any())
|
||||
{
|
||||
// handle anonymous types, where there are multiple generic arguments (the first is an integer)
|
||||
type = genericArguments.FirstOrDefault(g => g.IsClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
var enumerator = collection.GetEnumerator();
|
||||
|
||||
enumerator.MoveNext();
|
||||
|
||||
type = enumerator.Current.GetType();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
using (var table = new DataTable(tableName))
|
||||
{
|
||||
#region Build Table Schema
|
||||
|
||||
var propertyInfo = typeof(T)
|
||||
var propertyInfo = type
|
||||
.GetProperties()
|
||||
.Select(p => new
|
||||
.Where(p => !p.HasCustomAttribute<NotMappedAttribute>())
|
||||
.Select(p =>
|
||||
{
|
||||
Property = p,
|
||||
string columnName = p.Name;
|
||||
string columnTypeName = null;
|
||||
|
||||
// set column name to be either the property name
|
||||
// or, if specified, based on the attribute
|
||||
ColumnName = useColumnAttributeName
|
||||
? p.GetDataColumnNames().FirstOrDefault()
|
||||
: p.Name,
|
||||
if (useColumnAttributeName)
|
||||
{
|
||||
var columnAttributes = p.GetColumnAttributes();
|
||||
|
||||
// include the column if [NotMapped] is NOT attached
|
||||
IncludeColumn = !p.HasCustomAttribute<NotMappedAttribute>()
|
||||
columnName = p.GetDataColumnNames(columnAttributes).FirstOrDefault();
|
||||
columnTypeName = columnAttributes.FirstOrDefault()?.TypeName;
|
||||
}
|
||||
|
||||
if (columnNameModifier != null)
|
||||
{
|
||||
var modifiedColumnName = columnNameModifier.Invoke(p);
|
||||
|
||||
if (modifiedColumnName.IsNotEmpty())
|
||||
{
|
||||
columnName = modifiedColumnName;
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
Property = p,
|
||||
ColumnTypeName = columnTypeName,
|
||||
Converter = p.GetAttributeTypeConverter(),
|
||||
ColumnName = columnName
|
||||
};
|
||||
})
|
||||
.Where(p => p.IncludeColumn)
|
||||
.ToList();
|
||||
|
||||
foreach (var info in propertyInfo)
|
||||
@ -95,6 +161,11 @@ namespace BinaryDad.Extensions
|
||||
columnType = columnType.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
if (info.ColumnTypeName.IsNotEmpty())
|
||||
{
|
||||
columnType = SqlTypeMap.GetType(info.ColumnTypeName);
|
||||
}
|
||||
|
||||
table.Columns.Add(info.ColumnName, columnType);
|
||||
}
|
||||
|
||||
@ -109,6 +180,12 @@ namespace BinaryDad.Extensions
|
||||
foreach (var info in propertyInfo)
|
||||
{
|
||||
var value = info.Property.GetValue(item, null);
|
||||
var columnType = row.Table.Columns[info.ColumnName].DataType;
|
||||
|
||||
if (info.Converter != null && info.Converter.CanConvertTo(columnType))
|
||||
{
|
||||
value = info.Converter.ConvertTo(value, columnType);
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
|
@ -83,6 +83,19 @@ namespace BinaryDad.Extensions
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<string> GetDataColumnNames(this PropertyInfo property)
|
||||
{
|
||||
var attributes = property.GetColumnAttributes();
|
||||
|
||||
return GetDataColumnNames(property, attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of available property binding aliases using <see cref="ColumnAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <param name="attributes"></param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<string> GetDataColumnNames(this PropertyInfo property, ICollection<ColumnAttribute> attributes)
|
||||
{
|
||||
#region Null checks
|
||||
|
||||
@ -95,14 +108,9 @@ namespace BinaryDad.Extensions
|
||||
|
||||
var dataRowFieldNames = new List<string>();
|
||||
|
||||
var attributes = property
|
||||
.GetCustomAttributes(true);
|
||||
|
||||
var columnAttribute = attributes.FirstOfType<ColumnAttribute>();
|
||||
|
||||
if (columnAttribute != null)
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
dataRowFieldNames.Add(columnAttribute.Name);
|
||||
dataRowFieldNames.Add(attribute.Name);
|
||||
}
|
||||
|
||||
// add the property's name at the end, so it's the last in the lookup
|
||||
@ -169,6 +177,14 @@ namespace BinaryDad.Extensions
|
||||
.FirstOrDefault(f => columns.Contains(f));
|
||||
}
|
||||
|
||||
internal static ICollection<ColumnAttribute> GetColumnAttributes(this PropertyInfo property)
|
||||
{
|
||||
return property
|
||||
.GetCustomAttributes<ColumnAttribute>()
|
||||
.OrderBy(c => !c.IsDefaultAttribute())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an instance of the <see cref="TypeConverter"/> associated with the property's <see cref="TypeConverterAttribute"/>
|
||||
/// </summary>
|
||||
|
44
BinaryDad.Extensions/Extensions/SqlTypeMap.cs
Normal file
44
BinaryDad.Extensions/Extensions/SqlTypeMap.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
|
||||
namespace BinaryDad.Extensions
|
||||
{
|
||||
public static class SqlTypeMap
|
||||
{
|
||||
public static Dictionary<SqlDbType, Type> typeMap = new Dictionary<SqlDbType, Type>
|
||||
{
|
||||
[SqlDbType.NVarChar] = typeof(string),
|
||||
[SqlDbType.VarChar] = typeof(string),
|
||||
[SqlDbType.Char] = typeof(char[]),
|
||||
[SqlDbType.NChar] = typeof(char[]),
|
||||
[SqlDbType.TinyInt] = typeof(byte),
|
||||
[SqlDbType.SmallInt] = typeof(short),
|
||||
[SqlDbType.Int] = typeof(int),
|
||||
[SqlDbType.BigInt] = typeof(long),
|
||||
[SqlDbType.Bit] = typeof(bool),
|
||||
[SqlDbType.DateTime] = typeof(DateTime),
|
||||
[SqlDbType.DateTime2] = typeof(DateTime),
|
||||
[SqlDbType.SmallDateTime] = typeof(DateTime),
|
||||
[SqlDbType.Time] = typeof(TimeSpan),
|
||||
[SqlDbType.DateTimeOffset] = typeof(DateTimeOffset),
|
||||
[SqlDbType.Decimal] = typeof(decimal),
|
||||
[SqlDbType.Money] = typeof(decimal),
|
||||
[SqlDbType.SmallMoney] = typeof(decimal),
|
||||
[SqlDbType.Float] = typeof(double),
|
||||
[SqlDbType.Real] = typeof(float)
|
||||
};
|
||||
|
||||
public static Type GetType(string sqlDbTypeName)
|
||||
{
|
||||
return GetType(sqlDbTypeName.ToEnum<SqlDbType>());
|
||||
}
|
||||
|
||||
public static Type GetType(SqlDbType sqlDbType)
|
||||
{
|
||||
// I'm aware this is backward
|
||||
return typeMap.FirstOrDefault(t => t.Key == sqlDbType).Value;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user