Skip to content

Instantly share code, notes, and snippets.

@Konctantin
Created January 16, 2019 07:11
Show Gist options
  • Select an option

  • Save Konctantin/e027e53c1ecbf2f9b26c48c9077456fa to your computer and use it in GitHub Desktop.

Select an option

Save Konctantin/e027e53c1ecbf2f9b26c48c9077456fa to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
namespace System.Data.MapperExtension
{
/// <summary>
/// Make field name for mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlFieldNameAttribute : Attribute
{
/// <summary>
/// Field name.
/// </summary>
public string Name { get; }
/// <summary>
/// Create new instance for <see cref="SqlFieldNameAttribute"/>.
/// </summary>
/// <param name="name">Field name.</param>
public SqlFieldNameAttribute(string name)
{
Name = name;
}
}
/// <summary>
/// Helper class for reading data from <see cref="DbDataReader"/> to object type of <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">Type for mapping from <see cref="DbDataReader"/>.</typeparam>
public class DbFieldCache<T> : IDisposable where T : class, new()
{
private const BindingFlags BFlag = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
private delegate void Setter(T fieldType, object value);
class FieldCacheInfo
{
internal int Index;
internal TypeCode TypeCode;
internal Setter Setter;
}
class StaticFieldCacheInfo
{
internal string Name;
internal TypeCode TypeCode;
internal Setter Setter;
}
private List<FieldCacheInfo> Fields { get; } = new List<FieldCacheInfo>();
private static readonly List<StaticFieldCacheInfo> staticPropertyList = new List<StaticFieldCacheInfo>();
private DbDataReader Reader { get; }
private static bool IsSimpleType(TypeCode typeCode)
{
switch (typeCode)
{
case TypeCode.Empty:
case TypeCode.Object:
case TypeCode.DBNull:
return false;
default:
return true;
}
}
private static Setter GetSetter(PropertyInfo fieldInfo)
{
var paramExpression = Expression.Parameter(typeof(T));
var propertyExpression = Expression.Property(paramExpression, fieldInfo);
var valueExpression = Expression.Parameter(typeof(object));
var convertExpression = Expression.Convert(valueExpression, fieldInfo.PropertyType);
var assignExpression = Expression.Assign(propertyExpression, convertExpression);
return Expression.Lambda<Setter>(assignExpression, paramExpression, valueExpression).Compile();
}
static DbFieldCache()
{
staticPropertyList.Clear();
foreach (var property in typeof(T).GetProperties(BFlag))
{
if (property.SetMethod == null)
continue;
var name = property.Name;
// if property has attribute with fieldname.
var attributes = property.GetCustomAttributes(typeof(SqlFieldNameAttribute), false);
if (attributes.Length > 0)
name = (attributes[0] as SqlFieldNameAttribute).Name;
var type = property.PropertyType;
// if it's nullable type
if (Nullable.GetUnderlyingType(type) is Type utype)
{
type = utype;
}
// if it's enum, extract base type.
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
}
var typeCode = Type.GetTypeCode(type);
if (IsSimpleType(typeCode))
{
var setter = GetSetter(property);
var fieldInfo = new StaticFieldCacheInfo { Name = name, TypeCode = typeCode, Setter = setter };
staticPropertyList.Add(fieldInfo);
}
}
}
/// <summary>
/// Create new instance for <see cref="MapperExtension.DbFieldCache"/>.
/// </summary>
/// <param name="reader"></param>
public DbFieldCache(DbDataReader reader)
{
Reader = reader;
foreach (var property in staticPropertyList)
{
for (int i = 0; i < reader.FieldCount; ++i)
{
if (property.Name.Equals(reader.GetName(i), StringComparison.InvariantCultureIgnoreCase))
{
var fieldCache = new FieldCacheInfo
{
Index = i,
TypeCode = property.TypeCode,
Setter = property.Setter
};
Fields.Add(fieldCache);
break;
}
}
}
}
/// <summary>
/// Read object from current record.
/// </summary>
/// <returns></returns>
public T Read()
{
var entry = new T();
foreach (var field in Fields)
{
if (!Reader.IsDBNull(field.Index))
{
var value = Convert.ChangeType(Reader[field.Index], field.TypeCode, CultureInfo.InvariantCulture);
field.Setter(entry, value);
}
}
return entry;
}
/// <summary>
/// Read all records from recordset.
/// </summary>
/// <returns></returns>
public IEnumerable<T> ReadAll()
{
while (Reader.Read())
{
var val = Read();
yield return val;
}
}
/// <summary>
/// <inheriteddoc/>
/// </summary>
public void Dispose()
{
Fields?.Clear();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment