add AddRange, updates to ToDataTable, GetColumnAttributes, sqltypemap
This commit is contained in:
parent
d12750e1c4
commit
8a8c205855
@ -11,7 +11,7 @@
|
|||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Description>A set of common utilities and extension methods for .NET.</Description>
|
<Description>A set of common utilities and extension methods for .NET.</Description>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
<Version>21.4.20.1</Version>
|
<Version>21.4.20.2</Version>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace BinaryDad.Extensions
|
namespace BinaryDad.Extensions
|
||||||
{
|
{
|
||||||
@ -24,6 +25,20 @@ namespace BinaryDad.Extensions
|
|||||||
items.Add(item);
|
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>
|
/// <summary>
|
||||||
/// Returns a distinct list of elements using the first-matched item on a specific property
|
/// Returns a distinct list of elements using the first-matched item on a specific property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -59,31 +74,82 @@ namespace BinaryDad.Extensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="collection"></param>
|
/// <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>
|
/// <param name="tableName"></param>
|
||||||
/// <returns></returns>
|
/// <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))
|
using (var table = new DataTable(tableName))
|
||||||
{
|
{
|
||||||
#region Build Table Schema
|
#region Build Table Schema
|
||||||
|
|
||||||
var propertyInfo = typeof(T)
|
var propertyInfo = type
|
||||||
.GetProperties()
|
.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
|
// set column name to be either the property name
|
||||||
// or, if specified, based on the attribute
|
// or, if specified, based on the attribute
|
||||||
ColumnName = useColumnAttributeName
|
if (useColumnAttributeName)
|
||||||
? p.GetDataColumnNames().FirstOrDefault()
|
{
|
||||||
: p.Name,
|
var columnAttributes = p.GetColumnAttributes();
|
||||||
|
|
||||||
// include the column if [NotMapped] is NOT attached
|
columnName = p.GetDataColumnNames(columnAttributes).FirstOrDefault();
|
||||||
IncludeColumn = !p.HasCustomAttribute<NotMappedAttribute>()
|
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();
|
.ToList();
|
||||||
|
|
||||||
foreach (var info in propertyInfo)
|
foreach (var info in propertyInfo)
|
||||||
@ -95,6 +161,11 @@ namespace BinaryDad.Extensions
|
|||||||
columnType = columnType.GetGenericArguments()[0];
|
columnType = columnType.GetGenericArguments()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info.ColumnTypeName.IsNotEmpty())
|
||||||
|
{
|
||||||
|
columnType = SqlTypeMap.GetType(info.ColumnTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
table.Columns.Add(info.ColumnName, columnType);
|
table.Columns.Add(info.ColumnName, columnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +180,12 @@ namespace BinaryDad.Extensions
|
|||||||
foreach (var info in propertyInfo)
|
foreach (var info in propertyInfo)
|
||||||
{
|
{
|
||||||
var value = info.Property.GetValue(item, null);
|
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)
|
if (value != null)
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,19 @@ namespace BinaryDad.Extensions
|
|||||||
/// <param name="property"></param>
|
/// <param name="property"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal static IEnumerable<string> GetDataColumnNames(this PropertyInfo property)
|
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
|
#region Null checks
|
||||||
|
|
||||||
@ -95,14 +108,9 @@ namespace BinaryDad.Extensions
|
|||||||
|
|
||||||
var dataRowFieldNames = new List<string>();
|
var dataRowFieldNames = new List<string>();
|
||||||
|
|
||||||
var attributes = property
|
foreach (var attribute in attributes)
|
||||||
.GetCustomAttributes(true);
|
|
||||||
|
|
||||||
var columnAttribute = attributes.FirstOfType<ColumnAttribute>();
|
|
||||||
|
|
||||||
if (columnAttribute != null)
|
|
||||||
{
|
{
|
||||||
dataRowFieldNames.Add(columnAttribute.Name);
|
dataRowFieldNames.Add(attribute.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the property's name at the end, so it's the last in the lookup
|
// 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));
|
.FirstOrDefault(f => columns.Contains(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static ICollection<ColumnAttribute> GetColumnAttributes(this PropertyInfo property)
|
||||||
|
{
|
||||||
|
return property
|
||||||
|
.GetCustomAttributes<ColumnAttribute>()
|
||||||
|
.OrderBy(c => !c.IsDefaultAttribute())
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves an instance of the <see cref="TypeConverter"/> associated with the property's <see cref="TypeConverterAttribute"/>
|
/// Retrieves an instance of the <see cref="TypeConverter"/> associated with the property's <see cref="TypeConverterAttribute"/>
|
||||||
/// </summary>
|
/// </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