162 lines
5.4 KiB
C#
162 lines
5.4 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data.SqlClient;
|
||
using System.Collections.ObjectModel;
|
||
using FastMember;
|
||
using System.Linq;
|
||
|
||
namespace Knoks.Framework.DataAccess
|
||
{
|
||
public class DataResult
|
||
{
|
||
public Dictionary<short, Table> Тables { get; set; } = new Dictionary<short, Table>();
|
||
public Dictionary<string, object> OutputParams { get; set; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||
public int? ReturnValue { get; set; }
|
||
}
|
||
|
||
public class Row
|
||
{
|
||
public ReadOnlyDictionary<string, int> ColumnNames { get; private set; }
|
||
public ReadOnlyCollection<object> ColumnValues { get; private set; }
|
||
|
||
public Row(ReadOnlyDictionary<string, int> columnNames, ReadOnlyCollection<object> columnValues)
|
||
{
|
||
ColumnNames = columnNames;
|
||
ColumnValues = columnValues;
|
||
}
|
||
|
||
public object this[int columnIndex]
|
||
{
|
||
get { return ColumnValues[columnIndex]; }
|
||
}
|
||
|
||
public object this[string columnName]
|
||
{
|
||
get { return ColumnValues[ColumnNames[columnName]]; }
|
||
}
|
||
|
||
public T To<T>() where T : class, new()
|
||
{
|
||
return To<T>(ColumnNames, ColumnValues);
|
||
}
|
||
public T ToMapped<T>(Func<string, string> mapper = null) where T : class, new()
|
||
{
|
||
var accessor = TypeAccessor.Create(typeof(T));
|
||
var meta = Table.Parse<T>(accessor, ColumnNames, mapper);
|
||
return Process<T>(accessor, meta);
|
||
}
|
||
internal T Process<T>(TypeAccessor accessor, Dictionary<Member, int> meta) where T : class, new()
|
||
{
|
||
var res = new T();
|
||
foreach (var c in meta)
|
||
{
|
||
try
|
||
{
|
||
accessor[res, c.Key.Name] = ColumnValues[c.Value];
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
try
|
||
{
|
||
accessor[res, c.Key.Name] = Convert.ChangeType(c.Value, c.Key.Type);
|
||
}
|
||
catch (Exception exx)
|
||
{
|
||
throw new ArgumentException(c.Key.Name + ": " + ex.Message, exx);
|
||
}
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
public static T To<T>(IDictionary<string, int> ColumnNames, IList<object> ColumnValues) where T : class, new()
|
||
{
|
||
var res = new T();
|
||
|
||
var accessor = TypeAccessor.Create(res.GetType());
|
||
|
||
foreach (var cn in accessor.GetMembers())
|
||
{
|
||
int columnIndex;
|
||
try
|
||
{
|
||
if (ColumnNames.TryGetValue(cn.Name, out columnIndex))
|
||
accessor[res, cn.Name] = ColumnValues[columnIndex];
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new ArgumentException(cn.Name + ": " + ex.Message, ex);
|
||
}
|
||
|
||
}
|
||
|
||
return res;
|
||
}
|
||
}
|
||
|
||
public class Table
|
||
{
|
||
private Dictionary<string, int> _columnNames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||
public ReadOnlyDictionary<string, int> ColumnNames { get; }
|
||
|
||
private List<Row> _rows = new List<Row>();
|
||
public ReadOnlyCollection<Row> Rows { get; }
|
||
|
||
public Table(SqlDataReader reader)
|
||
{
|
||
ColumnNames = new ReadOnlyDictionary<string, int>(_columnNames);
|
||
Rows = new ReadOnlyCollection<Row>(_rows);
|
||
|
||
int fieldCount = reader.FieldCount;
|
||
|
||
for (int i = 0; i < fieldCount; i++)
|
||
_columnNames.Add(reader.GetName(i), i);
|
||
|
||
while (reader.Read())
|
||
{
|
||
var rowValues = new object[fieldCount];
|
||
|
||
for (var i = 0; i < rowValues.Length; i++)
|
||
{
|
||
var val = reader[i];
|
||
rowValues[i] = val == DBNull.Value ? null : val;
|
||
}
|
||
|
||
_rows.Add(new Row(ColumnNames, new ReadOnlyCollection<object>(rowValues)));
|
||
}
|
||
}
|
||
|
||
public Row this[int rowIndex]
|
||
{
|
||
get { return _rows[rowIndex]; }
|
||
}
|
||
|
||
public IEnumerable<T> ToList<T>() where T : class, new()
|
||
{
|
||
return _rows.Select(r => r.To<T>());
|
||
}
|
||
public IEnumerable<T> ToListMapped<T>(Func<string,string> mapper = null) where T : class, new()
|
||
{
|
||
var accessor = TypeAccessor.Create(typeof(T));
|
||
var meta = Parse<T>(accessor, ColumnNames, mapper);
|
||
return _rows.Select(r => r.Process<T>(accessor, meta));
|
||
}
|
||
internal static Dictionary<Member, int> Parse<T>(TypeAccessor accessor, IDictionary<string, int> ColumnNames, Func<string, string> mapper = null) where T : class, new()
|
||
{
|
||
return accessor.GetMembers().Select(cn => {
|
||
int columnIndex;
|
||
string name = mapper == null ? cn.Name : mapper(cn.Name);
|
||
if (!ColumnNames.TryGetValue(name, out columnIndex))
|
||
{
|
||
columnIndex = -1;
|
||
}
|
||
return new { cn, columnIndex };
|
||
}).Where(it => it.columnIndex != -1).ToDictionary(it => it.cn, it => it.columnIndex);
|
||
}
|
||
|
||
public IEnumerable<T> ToPrimitiveList<T>()
|
||
{
|
||
return _rows.Select(r => (T)r.ColumnValues.First());
|
||
}
|
||
}
|
||
}
|