Improve declarative procedure result-set binding
This commit is contained in:
@@ -5542,7 +5542,7 @@ namespace DynamORM
|
|||||||
|
|
||||||
object mainResult = null;
|
object mainResult = null;
|
||||||
|
|
||||||
if (types.Count == 0 && DynamicProcedureResultBinder.HasDeclaredResultBinding(declaredResultType))
|
if (types.Count == 0 && DynamicProcedureResultBinder.CanReadResults(declaredResultType))
|
||||||
{
|
{
|
||||||
using (IDataReader rdr = cmd.ExecuteReader())
|
using (IDataReader rdr = cmd.ExecuteReader())
|
||||||
using (IDataReader cache = rdr.CachedReader())
|
using (IDataReader cache = rdr.CachedReader())
|
||||||
@@ -14564,10 +14564,25 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
internal static class DynamicProcedureResultBinder
|
internal static class DynamicProcedureResultBinder
|
||||||
{
|
{
|
||||||
private sealed class ResultPropertyBinding
|
private sealed class ResultMemberBinding
|
||||||
{
|
{
|
||||||
public ProcedureResultAttribute Attribute { get; set; }
|
public ProcedureResultAttribute Attribute { get; set; }
|
||||||
public PropertyInfo Property { get; set; }
|
public MemberInfo Member { get; set; }
|
||||||
|
public Type MemberType { get; set; }
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
|
public void SetValue(object instance, object value)
|
||||||
|
{
|
||||||
|
PropertyInfo property = Member as PropertyInfo;
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
property.SetValue(instance, value, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FieldInfo field = Member as FieldInfo;
|
||||||
|
if (field != null)
|
||||||
|
field.SetValue(instance, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
internal static bool IsProcedureContract(object item)
|
internal static bool IsProcedureContract(object item)
|
||||||
{
|
{
|
||||||
@@ -14583,10 +14598,14 @@ namespace DynamORM
|
|||||||
|
|
||||||
return iface == null ? null : iface.GetGenericArguments()[0];
|
return iface == null ? null : iface.GetGenericArguments()[0];
|
||||||
}
|
}
|
||||||
internal static bool HasDeclaredResultBinding(Type resultType)
|
internal static bool CanReadResults(Type resultType)
|
||||||
{
|
{
|
||||||
return resultType != null &&
|
return resultType != null &&
|
||||||
(typeof(IProcedureResultReader).IsAssignableFrom(resultType) || GetResultPropertyBindings(resultType).Count > 0);
|
(typeof(IProcedureResultReader).IsAssignableFrom(resultType) || GetResultMemberBindings(resultType).Count > 0);
|
||||||
|
}
|
||||||
|
internal static bool HasDeclaredResultBinding(Type resultType)
|
||||||
|
{
|
||||||
|
return CanReadResults(resultType);
|
||||||
}
|
}
|
||||||
internal static object CreateDeclaredResult(Type resultType)
|
internal static object CreateDeclaredResult(Type resultType)
|
||||||
{
|
{
|
||||||
@@ -14601,14 +14620,14 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
internal static object ReadDeclaredResult(Type resultType, IDataReader reader)
|
internal static object ReadDeclaredResult(Type resultType, IDataReader reader)
|
||||||
{
|
{
|
||||||
if (!HasDeclaredResultBinding(resultType))
|
if (!CanReadResults(resultType))
|
||||||
throw new InvalidOperationException(string.Format("Type '{0}' does not declare a supported procedure result binding.", resultType == null ? "<null>" : resultType.FullName));
|
throw new InvalidOperationException(string.Format("Type '{0}' does not declare a supported procedure result binding.", resultType == null ? "<null>" : resultType.FullName));
|
||||||
|
|
||||||
object instance = CreateDeclaredResult(resultType);
|
object instance = CreateDeclaredResult(resultType);
|
||||||
|
|
||||||
IList<ResultPropertyBinding> bindings = GetResultPropertyBindings(resultType);
|
IList<ResultMemberBinding> bindings = GetResultMemberBindings(resultType);
|
||||||
if (bindings.Count > 0)
|
if (bindings.Count > 0)
|
||||||
BindResultProperties(instance, reader, bindings);
|
BindResultMembers(instance, reader, bindings);
|
||||||
else
|
else
|
||||||
((IProcedureResultReader)instance).ReadResults(reader);
|
((IProcedureResultReader)instance).ReadResults(reader);
|
||||||
|
|
||||||
@@ -14637,21 +14656,35 @@ namespace DynamORM
|
|||||||
|
|
||||||
return payload.ToDynamic();
|
return payload.ToDynamic();
|
||||||
}
|
}
|
||||||
private static IList<ResultPropertyBinding> GetResultPropertyBindings(Type resultType)
|
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
|
||||||
{
|
{
|
||||||
return resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
var properties = resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
.Where(x => x.CanWrite && x.GetIndexParameters().Length == 0)
|
.Where(x => x.CanWrite && x.GetIndexParameters().Length == 0)
|
||||||
.Select(x => new ResultPropertyBinding
|
.Select(x => new ResultMemberBinding
|
||||||
{
|
{
|
||||||
Property = x,
|
Member = x,
|
||||||
|
MemberType = x.PropertyType,
|
||||||
|
SortOrder = x.MetadataToken,
|
||||||
Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast<ProcedureResultAttribute>().FirstOrDefault()
|
Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast<ProcedureResultAttribute>().FirstOrDefault()
|
||||||
})
|
});
|
||||||
|
|
||||||
|
var fields = resultType.GetFields(BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(x => !x.IsInitOnly && !x.IsLiteral)
|
||||||
|
.Select(x => new ResultMemberBinding
|
||||||
|
{
|
||||||
|
Member = x,
|
||||||
|
MemberType = x.FieldType,
|
||||||
|
SortOrder = x.MetadataToken,
|
||||||
|
Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast<ProcedureResultAttribute>().FirstOrDefault()
|
||||||
|
});
|
||||||
|
|
||||||
|
return properties.Concat(fields)
|
||||||
.Where(x => x.Attribute != null)
|
.Where(x => x.Attribute != null)
|
||||||
.OrderBy(x => x.Attribute.ResultIndex)
|
.OrderBy(x => x.Attribute.ResultIndex)
|
||||||
.ThenBy(x => x.Property.MetadataToken)
|
.ThenBy(x => x.SortOrder)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
private static void BindResultProperties(object instance, IDataReader reader, IList<ResultPropertyBinding> bindings)
|
private static void BindResultMembers(object instance, IDataReader reader, IList<ResultMemberBinding> bindings)
|
||||||
{
|
{
|
||||||
ValidateBindings(instance.GetType(), bindings);
|
ValidateBindings(instance.GetType(), bindings);
|
||||||
|
|
||||||
@@ -14660,7 +14693,7 @@ namespace DynamORM
|
|||||||
|
|
||||||
for (int i = 0; i < bindings.Count; i++)
|
for (int i = 0; i < bindings.Count; i++)
|
||||||
{
|
{
|
||||||
ResultPropertyBinding binding = bindings[i];
|
ResultMemberBinding binding = bindings[i];
|
||||||
while (hasCurrent && currentIndex < binding.Attribute.ResultIndex)
|
while (hasCurrent && currentIndex < binding.Attribute.ResultIndex)
|
||||||
{
|
{
|
||||||
hasCurrent = reader.NextResult();
|
hasCurrent = reader.NextResult();
|
||||||
@@ -14669,8 +14702,8 @@ namespace DynamORM
|
|||||||
if (!hasCurrent || currentIndex != binding.Attribute.ResultIndex)
|
if (!hasCurrent || currentIndex != binding.Attribute.ResultIndex)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
object value = ReadResultValue(binding.Property.PropertyType, binding.Attribute, reader);
|
object value = ReadResultValue(binding.MemberType, binding.Attribute, reader);
|
||||||
binding.Property.SetValue(instance, value, null);
|
binding.SetValue(instance, value);
|
||||||
|
|
||||||
if (i + 1 < bindings.Count)
|
if (i + 1 < bindings.Count)
|
||||||
{
|
{
|
||||||
@@ -14679,7 +14712,7 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static void ValidateBindings(Type resultType, IList<ResultPropertyBinding> bindings)
|
private static void ValidateBindings(Type resultType, IList<ResultMemberBinding> bindings)
|
||||||
{
|
{
|
||||||
var duplicates = bindings.GroupBy(x => x.Attribute.ResultIndex).FirstOrDefault(x => x.Count() > 1);
|
var duplicates = bindings.GroupBy(x => x.Attribute.ResultIndex).FirstOrDefault(x => x.Count() > 1);
|
||||||
if (duplicates != null)
|
if (duplicates != null)
|
||||||
@@ -14709,6 +14742,7 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
private static object ReadSimpleValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
private static object ReadSimpleValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
||||||
{
|
{
|
||||||
|
Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||||
object value = null;
|
object value = null;
|
||||||
int ordinal = -1;
|
int ordinal = -1;
|
||||||
bool haveRow = false;
|
bool haveRow = false;
|
||||||
@@ -14725,17 +14759,21 @@ namespace DynamORM
|
|||||||
if (!haveRow || value == null)
|
if (!haveRow || value == null)
|
||||||
return propertyType.GetDefaultValue();
|
return propertyType.GetDefaultValue();
|
||||||
|
|
||||||
if (propertyType == typeof(Guid))
|
if (targetType == typeof(Guid))
|
||||||
{
|
{
|
||||||
Guid g;
|
Guid g;
|
||||||
if (Guid.TryParse(value.ToString(), out g))
|
if (Guid.TryParse(value.ToString(), out g))
|
||||||
return g;
|
return propertyType == typeof(Guid) ? (object)g : new Guid?(g);
|
||||||
return propertyType.GetDefaultValue();
|
return propertyType.GetDefaultValue();
|
||||||
}
|
}
|
||||||
return propertyType.CastObject(value);
|
if (targetType.IsEnum)
|
||||||
|
return Enum.ToObject(targetType, value);
|
||||||
|
|
||||||
|
return targetType == propertyType ? propertyType.CastObject(value) : targetType.CastObject(value);
|
||||||
}
|
}
|
||||||
private static object ReadSimpleList(Type propertyType, Type elementType, ProcedureResultAttribute attr, IDataReader reader)
|
private static object ReadSimpleList(Type propertyType, Type elementType, ProcedureResultAttribute attr, IDataReader reader)
|
||||||
{
|
{
|
||||||
|
Type targetElementType = Nullable.GetUnderlyingType(elementType) ?? elementType;
|
||||||
Type listType = typeof(List<>).MakeGenericType(elementType);
|
Type listType = typeof(List<>).MakeGenericType(elementType);
|
||||||
var list = (System.Collections.IList)Activator.CreateInstance(listType);
|
var list = (System.Collections.IList)Activator.CreateInstance(listType);
|
||||||
int ordinal = -1;
|
int ordinal = -1;
|
||||||
@@ -14754,13 +14792,18 @@ namespace DynamORM
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
object value = reader[ordinal];
|
object value = reader[ordinal];
|
||||||
if (elementType == typeof(Guid))
|
if (targetElementType == typeof(Guid))
|
||||||
{
|
{
|
||||||
Guid g;
|
Guid g;
|
||||||
list.Add(Guid.TryParse(value.ToString(), out g) ? (object)g : elementType.GetDefaultValue());
|
if (Guid.TryParse(value.ToString(), out g))
|
||||||
|
list.Add(elementType == typeof(Guid) ? (object)g : new Guid?(g));
|
||||||
|
else
|
||||||
|
list.Add(elementType.GetDefaultValue());
|
||||||
}
|
}
|
||||||
|
else if (targetElementType.IsEnum)
|
||||||
|
list.Add(Enum.ToObject(targetElementType, value));
|
||||||
else
|
else
|
||||||
list.Add(elementType.CastObject(value));
|
list.Add(targetElementType == elementType ? elementType.CastObject(value) : targetElementType.CastObject(value));
|
||||||
}
|
}
|
||||||
if (propertyType.IsArray)
|
if (propertyType.IsArray)
|
||||||
{
|
{
|
||||||
@@ -14834,12 +14877,7 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
if (attr == null || string.IsNullOrEmpty(attr.ColumnName))
|
if (attr == null || string.IsNullOrEmpty(attr.ColumnName))
|
||||||
return 0;
|
return 0;
|
||||||
|
return reader.GetOrdinal(attr.ColumnName);
|
||||||
int ordinal = reader.GetOrdinal(attr.ColumnName);
|
|
||||||
if (ordinal < 0)
|
|
||||||
throw new IndexOutOfRangeException(attr.ColumnName);
|
|
||||||
|
|
||||||
return ordinal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>Framework detection and specific implementations.</summary>
|
/// <summary>Framework detection and specific implementations.</summary>
|
||||||
@@ -17886,7 +17924,7 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>Declares mapping of a typed procedure result property to a specific result set.</summary>
|
/// <summary>Declares mapping of a typed procedure result property to a specific result set.</summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||||
public class ProcedureResultAttribute : Attribute
|
public class ProcedureResultAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
||||||
|
|||||||
@@ -101,6 +101,24 @@ namespace DynamORM.Tests.Helpers
|
|||||||
public int Status { get; set; }
|
public int Status { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ProcedureAttributedFieldResult
|
||||||
|
{
|
||||||
|
[ProcedureResult(0)]
|
||||||
|
public string FirstCode;
|
||||||
|
|
||||||
|
[ProcedureResult(1)]
|
||||||
|
public System.Collections.Generic.List<string> Codes;
|
||||||
|
|
||||||
|
[ProcedureResult(2, ColumnName = "State")]
|
||||||
|
public System.Collections.Generic.IEnumerable<int> States;
|
||||||
|
|
||||||
|
[ProcedureResult(3)]
|
||||||
|
public Users User;
|
||||||
|
|
||||||
|
[ProcedureResult(4, Name = "users_table")]
|
||||||
|
public DataTable UsersTable;
|
||||||
|
}
|
||||||
|
|
||||||
public class ProcedureMultiResultArgs : IProcedureParameters<ProcedureMultiResult>
|
public class ProcedureMultiResultArgs : IProcedureParameters<ProcedureMultiResult>
|
||||||
{
|
{
|
||||||
[ProcedureParameter("status", Direction = ParameterDirection.Output, Order = 1, DbType = DbType.Int32)]
|
[ProcedureParameter("status", Direction = ParameterDirection.Output, Order = 1, DbType = DbType.Int32)]
|
||||||
|
|||||||
@@ -206,6 +206,36 @@ namespace DynamORM.Tests.Procedure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeclaredResultCanReadAttributedFieldResultSets()
|
||||||
|
{
|
||||||
|
using (var reader = new FakeMultiResultDataReader(
|
||||||
|
Tuple.Create(new[] { "Code" }, new[] { typeof(string) }, new[] { new object[] { "FIELD-FIRST" }, new object[] { "FIELD-SECOND" } }),
|
||||||
|
Tuple.Create(new[] { "Code" }, new[] { typeof(string) }, new[] { new object[] { "C" }, new object[] { "D" } }),
|
||||||
|
Tuple.Create(new[] { "State" }, new[] { typeof(int) }, new[] { new object[] { 30 }, new object[] { 40 } }),
|
||||||
|
Tuple.Create(
|
||||||
|
new[] { "id", "code", "first" },
|
||||||
|
new[] { typeof(long), typeof(string), typeof(string) },
|
||||||
|
new[] { new object[] { 4L, "U4", "Four" } }),
|
||||||
|
Tuple.Create(
|
||||||
|
new[] { "id", "code", "first" },
|
||||||
|
new[] { typeof(long), typeof(string), typeof(string) },
|
||||||
|
new[] { new object[] { 5L, "U5", "Five" }, new object[] { 6L, "U6", "Six" } })))
|
||||||
|
{
|
||||||
|
var result = DynamicProcedureResultBinder.ReadDeclaredResult(typeof(ProcedureAttributedFieldResult), reader) as ProcedureAttributedFieldResult;
|
||||||
|
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.AreEqual("FIELD-FIRST", result.FirstCode);
|
||||||
|
CollectionAssert.AreEqual(new[] { "C", "D" }, result.Codes);
|
||||||
|
CollectionAssert.AreEqual(new[] { 30, 40 }, result.States);
|
||||||
|
Assert.NotNull(result.User);
|
||||||
|
Assert.AreEqual(4L, result.User.Id);
|
||||||
|
Assert.NotNull(result.UsersTable);
|
||||||
|
Assert.AreEqual("users_table", result.UsersTable.TableName);
|
||||||
|
Assert.AreEqual(2, result.UsersTable.Rows.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDeclaredResultPayloadCanAugmentReaderResult()
|
public void TestDeclaredResultPayloadCanAugmentReaderResult()
|
||||||
{
|
{
|
||||||
@@ -233,9 +263,10 @@ namespace DynamORM.Tests.Procedure
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDeclaredResultBindingDetectionSupportsAttributedResults()
|
public void TestDeclaredResultBindingDetectionSupportsAttributedResults()
|
||||||
{
|
{
|
||||||
Assert.IsTrue(DynamicProcedureResultBinder.HasDeclaredResultBinding(typeof(ProcedureMultiResult)));
|
Assert.IsTrue(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureMultiResult)));
|
||||||
Assert.IsTrue(DynamicProcedureResultBinder.HasDeclaredResultBinding(typeof(ProcedureAttributedResult)));
|
Assert.IsTrue(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureAttributedResult)));
|
||||||
Assert.IsFalse(DynamicProcedureResultBinder.HasDeclaredResultBinding(typeof(ProcedureParameterResult)));
|
Assert.IsTrue(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureAttributedFieldResult)));
|
||||||
|
Assert.IsFalse(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureParameterResult)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ namespace DynamORM
|
|||||||
|
|
||||||
object mainResult = null;
|
object mainResult = null;
|
||||||
|
|
||||||
if (types.Count == 0 && DynamicProcedureResultBinder.HasDeclaredResultBinding(declaredResultType))
|
if (types.Count == 0 && DynamicProcedureResultBinder.CanReadResults(declaredResultType))
|
||||||
{
|
{
|
||||||
using (IDataReader rdr = cmd.ExecuteReader())
|
using (IDataReader rdr = cmd.ExecuteReader())
|
||||||
using (IDataReader cache = rdr.CachedReader())
|
using (IDataReader cache = rdr.CachedReader())
|
||||||
|
|||||||
@@ -38,10 +38,26 @@ namespace DynamORM.Helpers
|
|||||||
{
|
{
|
||||||
internal static class DynamicProcedureResultBinder
|
internal static class DynamicProcedureResultBinder
|
||||||
{
|
{
|
||||||
private sealed class ResultPropertyBinding
|
private sealed class ResultMemberBinding
|
||||||
{
|
{
|
||||||
public ProcedureResultAttribute Attribute { get; set; }
|
public ProcedureResultAttribute Attribute { get; set; }
|
||||||
public PropertyInfo Property { get; set; }
|
public MemberInfo Member { get; set; }
|
||||||
|
public Type MemberType { get; set; }
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
|
public void SetValue(object instance, object value)
|
||||||
|
{
|
||||||
|
PropertyInfo property = Member as PropertyInfo;
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
property.SetValue(instance, value, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfo field = Member as FieldInfo;
|
||||||
|
if (field != null)
|
||||||
|
field.SetValue(instance, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsProcedureContract(object item)
|
internal static bool IsProcedureContract(object item)
|
||||||
@@ -60,10 +76,15 @@ namespace DynamORM.Helpers
|
|||||||
return iface == null ? null : iface.GetGenericArguments()[0];
|
return iface == null ? null : iface.GetGenericArguments()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool HasDeclaredResultBinding(Type resultType)
|
internal static bool CanReadResults(Type resultType)
|
||||||
{
|
{
|
||||||
return resultType != null &&
|
return resultType != null &&
|
||||||
(typeof(IProcedureResultReader).IsAssignableFrom(resultType) || GetResultPropertyBindings(resultType).Count > 0);
|
(typeof(IProcedureResultReader).IsAssignableFrom(resultType) || GetResultMemberBindings(resultType).Count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasDeclaredResultBinding(Type resultType)
|
||||||
|
{
|
||||||
|
return CanReadResults(resultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static object CreateDeclaredResult(Type resultType)
|
internal static object CreateDeclaredResult(Type resultType)
|
||||||
@@ -80,14 +101,14 @@ namespace DynamORM.Helpers
|
|||||||
|
|
||||||
internal static object ReadDeclaredResult(Type resultType, IDataReader reader)
|
internal static object ReadDeclaredResult(Type resultType, IDataReader reader)
|
||||||
{
|
{
|
||||||
if (!HasDeclaredResultBinding(resultType))
|
if (!CanReadResults(resultType))
|
||||||
throw new InvalidOperationException(string.Format("Type '{0}' does not declare a supported procedure result binding.", resultType == null ? "<null>" : resultType.FullName));
|
throw new InvalidOperationException(string.Format("Type '{0}' does not declare a supported procedure result binding.", resultType == null ? "<null>" : resultType.FullName));
|
||||||
|
|
||||||
object instance = CreateDeclaredResult(resultType);
|
object instance = CreateDeclaredResult(resultType);
|
||||||
|
|
||||||
IList<ResultPropertyBinding> bindings = GetResultPropertyBindings(resultType);
|
IList<ResultMemberBinding> bindings = GetResultMemberBindings(resultType);
|
||||||
if (bindings.Count > 0)
|
if (bindings.Count > 0)
|
||||||
BindResultProperties(instance, reader, bindings);
|
BindResultMembers(instance, reader, bindings);
|
||||||
else
|
else
|
||||||
((IProcedureResultReader)instance).ReadResults(reader);
|
((IProcedureResultReader)instance).ReadResults(reader);
|
||||||
|
|
||||||
@@ -118,22 +139,36 @@ namespace DynamORM.Helpers
|
|||||||
return payload.ToDynamic();
|
return payload.ToDynamic();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IList<ResultPropertyBinding> GetResultPropertyBindings(Type resultType)
|
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
|
||||||
{
|
{
|
||||||
return resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
var properties = resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
.Where(x => x.CanWrite && x.GetIndexParameters().Length == 0)
|
.Where(x => x.CanWrite && x.GetIndexParameters().Length == 0)
|
||||||
.Select(x => new ResultPropertyBinding
|
.Select(x => new ResultMemberBinding
|
||||||
{
|
{
|
||||||
Property = x,
|
Member = x,
|
||||||
|
MemberType = x.PropertyType,
|
||||||
|
SortOrder = x.MetadataToken,
|
||||||
Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast<ProcedureResultAttribute>().FirstOrDefault()
|
Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast<ProcedureResultAttribute>().FirstOrDefault()
|
||||||
})
|
});
|
||||||
|
|
||||||
|
var fields = resultType.GetFields(BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(x => !x.IsInitOnly && !x.IsLiteral)
|
||||||
|
.Select(x => new ResultMemberBinding
|
||||||
|
{
|
||||||
|
Member = x,
|
||||||
|
MemberType = x.FieldType,
|
||||||
|
SortOrder = x.MetadataToken,
|
||||||
|
Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast<ProcedureResultAttribute>().FirstOrDefault()
|
||||||
|
});
|
||||||
|
|
||||||
|
return properties.Concat(fields)
|
||||||
.Where(x => x.Attribute != null)
|
.Where(x => x.Attribute != null)
|
||||||
.OrderBy(x => x.Attribute.ResultIndex)
|
.OrderBy(x => x.Attribute.ResultIndex)
|
||||||
.ThenBy(x => x.Property.MetadataToken)
|
.ThenBy(x => x.SortOrder)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BindResultProperties(object instance, IDataReader reader, IList<ResultPropertyBinding> bindings)
|
private static void BindResultMembers(object instance, IDataReader reader, IList<ResultMemberBinding> bindings)
|
||||||
{
|
{
|
||||||
ValidateBindings(instance.GetType(), bindings);
|
ValidateBindings(instance.GetType(), bindings);
|
||||||
|
|
||||||
@@ -142,7 +177,7 @@ namespace DynamORM.Helpers
|
|||||||
|
|
||||||
for (int i = 0; i < bindings.Count; i++)
|
for (int i = 0; i < bindings.Count; i++)
|
||||||
{
|
{
|
||||||
ResultPropertyBinding binding = bindings[i];
|
ResultMemberBinding binding = bindings[i];
|
||||||
while (hasCurrent && currentIndex < binding.Attribute.ResultIndex)
|
while (hasCurrent && currentIndex < binding.Attribute.ResultIndex)
|
||||||
{
|
{
|
||||||
hasCurrent = reader.NextResult();
|
hasCurrent = reader.NextResult();
|
||||||
@@ -152,8 +187,8 @@ namespace DynamORM.Helpers
|
|||||||
if (!hasCurrent || currentIndex != binding.Attribute.ResultIndex)
|
if (!hasCurrent || currentIndex != binding.Attribute.ResultIndex)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
object value = ReadResultValue(binding.Property.PropertyType, binding.Attribute, reader);
|
object value = ReadResultValue(binding.MemberType, binding.Attribute, reader);
|
||||||
binding.Property.SetValue(instance, value, null);
|
binding.SetValue(instance, value);
|
||||||
|
|
||||||
if (i + 1 < bindings.Count)
|
if (i + 1 < bindings.Count)
|
||||||
{
|
{
|
||||||
@@ -163,7 +198,7 @@ namespace DynamORM.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateBindings(Type resultType, IList<ResultPropertyBinding> bindings)
|
private static void ValidateBindings(Type resultType, IList<ResultMemberBinding> bindings)
|
||||||
{
|
{
|
||||||
var duplicates = bindings.GroupBy(x => x.Attribute.ResultIndex).FirstOrDefault(x => x.Count() > 1);
|
var duplicates = bindings.GroupBy(x => x.Attribute.ResultIndex).FirstOrDefault(x => x.Count() > 1);
|
||||||
if (duplicates != null)
|
if (duplicates != null)
|
||||||
@@ -196,6 +231,7 @@ namespace DynamORM.Helpers
|
|||||||
|
|
||||||
private static object ReadSimpleValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
private static object ReadSimpleValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
||||||
{
|
{
|
||||||
|
Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||||
object value = null;
|
object value = null;
|
||||||
int ordinal = -1;
|
int ordinal = -1;
|
||||||
bool haveRow = false;
|
bool haveRow = false;
|
||||||
@@ -213,19 +249,23 @@ namespace DynamORM.Helpers
|
|||||||
if (!haveRow || value == null)
|
if (!haveRow || value == null)
|
||||||
return propertyType.GetDefaultValue();
|
return propertyType.GetDefaultValue();
|
||||||
|
|
||||||
if (propertyType == typeof(Guid))
|
if (targetType == typeof(Guid))
|
||||||
{
|
{
|
||||||
Guid g;
|
Guid g;
|
||||||
if (Guid.TryParse(value.ToString(), out g))
|
if (Guid.TryParse(value.ToString(), out g))
|
||||||
return g;
|
return propertyType == typeof(Guid) ? (object)g : new Guid?(g);
|
||||||
return propertyType.GetDefaultValue();
|
return propertyType.GetDefaultValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
return propertyType.CastObject(value);
|
if (targetType.IsEnum)
|
||||||
|
return Enum.ToObject(targetType, value);
|
||||||
|
|
||||||
|
return targetType == propertyType ? propertyType.CastObject(value) : targetType.CastObject(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object ReadSimpleList(Type propertyType, Type elementType, ProcedureResultAttribute attr, IDataReader reader)
|
private static object ReadSimpleList(Type propertyType, Type elementType, ProcedureResultAttribute attr, IDataReader reader)
|
||||||
{
|
{
|
||||||
|
Type targetElementType = Nullable.GetUnderlyingType(elementType) ?? elementType;
|
||||||
Type listType = typeof(List<>).MakeGenericType(elementType);
|
Type listType = typeof(List<>).MakeGenericType(elementType);
|
||||||
var list = (System.Collections.IList)Activator.CreateInstance(listType);
|
var list = (System.Collections.IList)Activator.CreateInstance(listType);
|
||||||
int ordinal = -1;
|
int ordinal = -1;
|
||||||
@@ -246,13 +286,18 @@ namespace DynamORM.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
object value = reader[ordinal];
|
object value = reader[ordinal];
|
||||||
if (elementType == typeof(Guid))
|
if (targetElementType == typeof(Guid))
|
||||||
{
|
{
|
||||||
Guid g;
|
Guid g;
|
||||||
list.Add(Guid.TryParse(value.ToString(), out g) ? (object)g : elementType.GetDefaultValue());
|
if (Guid.TryParse(value.ToString(), out g))
|
||||||
|
list.Add(elementType == typeof(Guid) ? (object)g : new Guid?(g));
|
||||||
|
else
|
||||||
|
list.Add(elementType.GetDefaultValue());
|
||||||
}
|
}
|
||||||
|
else if (targetElementType.IsEnum)
|
||||||
|
list.Add(Enum.ToObject(targetElementType, value));
|
||||||
else
|
else
|
||||||
list.Add(elementType.CastObject(value));
|
list.Add(targetElementType == elementType ? elementType.CastObject(value) : targetElementType.CastObject(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propertyType.IsArray)
|
if (propertyType.IsArray)
|
||||||
@@ -337,12 +382,7 @@ namespace DynamORM.Helpers
|
|||||||
{
|
{
|
||||||
if (attr == null || string.IsNullOrEmpty(attr.ColumnName))
|
if (attr == null || string.IsNullOrEmpty(attr.ColumnName))
|
||||||
return 0;
|
return 0;
|
||||||
|
return reader.GetOrdinal(attr.ColumnName);
|
||||||
int ordinal = reader.GetOrdinal(attr.ColumnName);
|
|
||||||
if (ordinal < 0)
|
|
||||||
throw new IndexOutOfRangeException(attr.ColumnName);
|
|
||||||
|
|
||||||
return ordinal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ using System;
|
|||||||
namespace DynamORM.Mapper
|
namespace DynamORM.Mapper
|
||||||
{
|
{
|
||||||
/// <summary>Declares mapping of a typed procedure result property to a specific result set.</summary>
|
/// <summary>Declares mapping of a typed procedure result property to a specific result set.</summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||||
public class ProcedureResultAttribute : Attribute
|
public class ProcedureResultAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user