Support main procedure result via ProcedureResultAttribute
This commit is contained in:
@@ -14648,13 +14648,17 @@ namespace DynamORM
|
|||||||
payload[item.Key] = item.Value == DBNull.Value ? null : item.Value;
|
payload[item.Key] = item.Value == DBNull.Value ? null : item.Value;
|
||||||
|
|
||||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType);
|
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType);
|
||||||
|
object instance = existing;
|
||||||
|
|
||||||
if (mapper != null)
|
if (mapper != null)
|
||||||
return mapper.Map(payload.ToDynamic(), existing ?? mapper.Creator());
|
instance = mapper.Map(payload.ToDynamic(), existing ?? mapper.Creator());
|
||||||
|
else if (instance == null)
|
||||||
|
instance = payload.ToDynamic();
|
||||||
|
|
||||||
if (existing != null)
|
if (instance != null && resultType.IsInstanceOfType(instance))
|
||||||
return existing;
|
BindMainResultMembers(instance, mainResult);
|
||||||
|
|
||||||
return payload.ToDynamic();
|
return instance;
|
||||||
}
|
}
|
||||||
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
|
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
|
||||||
{
|
{
|
||||||
@@ -14680,10 +14684,39 @@ namespace DynamORM
|
|||||||
|
|
||||||
return properties.Concat(fields)
|
return properties.Concat(fields)
|
||||||
.Where(x => x.Attribute != null)
|
.Where(x => x.Attribute != null)
|
||||||
|
.Where(x => !IsMainResultBinding(x.Attribute))
|
||||||
.OrderBy(x => x.Attribute.ResultIndex)
|
.OrderBy(x => x.Attribute.ResultIndex)
|
||||||
.ThenBy(x => x.SortOrder)
|
.ThenBy(x => x.SortOrder)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
private static IList<ResultMemberBinding> GetMainResultBindings(Type resultType)
|
||||||
|
{
|
||||||
|
var properties = resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(x => x.CanWrite && x.GetIndexParameters().Length == 0)
|
||||||
|
.Select(x => new ResultMemberBinding
|
||||||
|
{
|
||||||
|
Member = x,
|
||||||
|
MemberType = x.PropertyType,
|
||||||
|
SortOrder = x.MetadataToken,
|
||||||
|
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 => IsMainResultBinding(x.Attribute))
|
||||||
|
.OrderBy(x => x.SortOrder)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
private static void BindResultMembers(object instance, IDataReader reader, IList<ResultMemberBinding> bindings)
|
private static void BindResultMembers(object instance, IDataReader reader, IList<ResultMemberBinding> bindings)
|
||||||
{
|
{
|
||||||
ValidateBindings(instance.GetType(), bindings);
|
ValidateBindings(instance.GetType(), bindings);
|
||||||
@@ -14718,6 +14751,21 @@ namespace DynamORM
|
|||||||
if (duplicates != null)
|
if (duplicates != null)
|
||||||
throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for result index {1}.", resultType.FullName, duplicates.Key));
|
throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for result index {1}.", resultType.FullName, duplicates.Key));
|
||||||
}
|
}
|
||||||
|
private static void BindMainResultMembers(object instance, object mainResult)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IList<ResultMemberBinding> bindings = GetMainResultBindings(instance.GetType());
|
||||||
|
if (bindings.Count == 0)
|
||||||
|
return;
|
||||||
|
if (bindings.Count > 1)
|
||||||
|
throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for the main procedure result.", instance.GetType().FullName));
|
||||||
|
|
||||||
|
ResultMemberBinding binding = bindings[0];
|
||||||
|
object value = ConvertScalarValue(binding.MemberType, mainResult == DBNull.Value ? null : mainResult);
|
||||||
|
binding.SetValue(instance, value);
|
||||||
|
}
|
||||||
private static object ReadResultValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
private static object ReadResultValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
||||||
{
|
{
|
||||||
Type elementType;
|
Type elementType;
|
||||||
@@ -14742,7 +14790,6 @@ 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;
|
||||||
@@ -14759,17 +14806,7 @@ namespace DynamORM
|
|||||||
if (!haveRow || value == null)
|
if (!haveRow || value == null)
|
||||||
return propertyType.GetDefaultValue();
|
return propertyType.GetDefaultValue();
|
||||||
|
|
||||||
if (targetType == typeof(Guid))
|
return ConvertScalarValue(propertyType, value);
|
||||||
{
|
|
||||||
Guid g;
|
|
||||||
if (Guid.TryParse(value.ToString(), out g))
|
|
||||||
return propertyType == typeof(Guid) ? (object)g : new Guid?(g);
|
|
||||||
return propertyType.GetDefaultValue();
|
|
||||||
}
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -14879,6 +14916,29 @@ namespace DynamORM
|
|||||||
return 0;
|
return 0;
|
||||||
return reader.GetOrdinal(attr.ColumnName);
|
return reader.GetOrdinal(attr.ColumnName);
|
||||||
}
|
}
|
||||||
|
private static bool IsMainResultBinding(ProcedureResultAttribute attribute)
|
||||||
|
{
|
||||||
|
return attribute != null && attribute.ResultIndex < 0;
|
||||||
|
}
|
||||||
|
private static object ConvertScalarValue(Type targetType, object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return targetType.GetDefaultValue();
|
||||||
|
|
||||||
|
Type underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType;
|
||||||
|
|
||||||
|
if (underlyingType == typeof(Guid))
|
||||||
|
{
|
||||||
|
Guid g;
|
||||||
|
if (Guid.TryParse(value.ToString(), out g))
|
||||||
|
return targetType == typeof(Guid) ? (object)g : new Guid?(g);
|
||||||
|
return targetType.GetDefaultValue();
|
||||||
|
}
|
||||||
|
if (underlyingType.IsEnum)
|
||||||
|
return Enum.ToObject(underlyingType, value);
|
||||||
|
|
||||||
|
return underlyingType == targetType ? targetType.CastObject(value) : underlyingType.CastObject(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// <summary>Framework detection and specific implementations.</summary>
|
/// <summary>Framework detection and specific implementations.</summary>
|
||||||
public static class FrameworkTools
|
public static class FrameworkTools
|
||||||
@@ -17927,6 +17987,14 @@ namespace DynamORM
|
|||||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||||
public class ProcedureResultAttribute : Attribute
|
public class ProcedureResultAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>Main procedure result marker.</summary>
|
||||||
|
public const int MainResultIndex = -1;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
||||||
|
public ProcedureResultAttribute()
|
||||||
|
: this(MainResultIndex)
|
||||||
|
{
|
||||||
|
}
|
||||||
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
||||||
public ProcedureResultAttribute(int resultIndex)
|
public ProcedureResultAttribute(int resultIndex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,6 +46,21 @@ namespace DynamORM.Tests.Helpers
|
|||||||
public int Status { get; set; }
|
public int Status { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ProcedureParameterAttributeMainResult
|
||||||
|
{
|
||||||
|
[ProcedureResult]
|
||||||
|
public int MainResult { get; set; }
|
||||||
|
|
||||||
|
[DynamORM.Mapper.Column("status")]
|
||||||
|
public int Status { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProcedureParameterAttributeMainResultField
|
||||||
|
{
|
||||||
|
[ProcedureResult(-1)]
|
||||||
|
public int MainResult;
|
||||||
|
}
|
||||||
|
|
||||||
public class ProcedureMultiResult : IProcedureResultReader
|
public class ProcedureMultiResult : IProcedureResultReader
|
||||||
{
|
{
|
||||||
[DynamORM.Mapper.Column("sp_Multi")]
|
[DynamORM.Mapper.Column("sp_Multi")]
|
||||||
|
|||||||
@@ -143,6 +143,36 @@ namespace DynamORM.Tests.Procedure
|
|||||||
Assert.AreEqual(3, result.Status);
|
Assert.AreEqual(3, result.Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeclaredResultPayloadBindingSupportsProcedureResultMainResultProperty()
|
||||||
|
{
|
||||||
|
var result = DynamicProcedureResultBinder.BindPayload(
|
||||||
|
typeof(ProcedureParameterAttributeMainResult),
|
||||||
|
"sp_Test",
|
||||||
|
27,
|
||||||
|
new System.Collections.Generic.Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "status", 6 }
|
||||||
|
}) as ProcedureParameterAttributeMainResult;
|
||||||
|
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.AreEqual(27, result.MainResult);
|
||||||
|
Assert.AreEqual(6, result.Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeclaredResultPayloadBindingSupportsProcedureResultMainResultField()
|
||||||
|
{
|
||||||
|
var result = DynamicProcedureResultBinder.BindPayload(
|
||||||
|
typeof(ProcedureParameterAttributeMainResultField),
|
||||||
|
"sp_Test",
|
||||||
|
33,
|
||||||
|
null) as ProcedureParameterAttributeMainResultField;
|
||||||
|
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.AreEqual(33, result.MainResult);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDeclaredResultReaderCanConsumeMultipleResultSets()
|
public void TestDeclaredResultReaderCanConsumeMultipleResultSets()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -130,13 +130,17 @@ namespace DynamORM.Helpers
|
|||||||
payload[item.Key] = item.Value == DBNull.Value ? null : item.Value;
|
payload[item.Key] = item.Value == DBNull.Value ? null : item.Value;
|
||||||
|
|
||||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType);
|
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType);
|
||||||
|
object instance = existing;
|
||||||
|
|
||||||
if (mapper != null)
|
if (mapper != null)
|
||||||
return mapper.Map(payload.ToDynamic(), existing ?? mapper.Creator());
|
instance = mapper.Map(payload.ToDynamic(), existing ?? mapper.Creator());
|
||||||
|
else if (instance == null)
|
||||||
|
instance = payload.ToDynamic();
|
||||||
|
|
||||||
if (existing != null)
|
if (instance != null && resultType.IsInstanceOfType(instance))
|
||||||
return existing;
|
BindMainResultMembers(instance, mainResult);
|
||||||
|
|
||||||
return payload.ToDynamic();
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
|
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
|
||||||
@@ -163,11 +167,41 @@ namespace DynamORM.Helpers
|
|||||||
|
|
||||||
return properties.Concat(fields)
|
return properties.Concat(fields)
|
||||||
.Where(x => x.Attribute != null)
|
.Where(x => x.Attribute != null)
|
||||||
|
.Where(x => !IsMainResultBinding(x.Attribute))
|
||||||
.OrderBy(x => x.Attribute.ResultIndex)
|
.OrderBy(x => x.Attribute.ResultIndex)
|
||||||
.ThenBy(x => x.SortOrder)
|
.ThenBy(x => x.SortOrder)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IList<ResultMemberBinding> GetMainResultBindings(Type resultType)
|
||||||
|
{
|
||||||
|
var properties = resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(x => x.CanWrite && x.GetIndexParameters().Length == 0)
|
||||||
|
.Select(x => new ResultMemberBinding
|
||||||
|
{
|
||||||
|
Member = x,
|
||||||
|
MemberType = x.PropertyType,
|
||||||
|
SortOrder = x.MetadataToken,
|
||||||
|
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 => IsMainResultBinding(x.Attribute))
|
||||||
|
.OrderBy(x => x.SortOrder)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private static void BindResultMembers(object instance, IDataReader reader, IList<ResultMemberBinding> bindings)
|
private static void BindResultMembers(object instance, IDataReader reader, IList<ResultMemberBinding> bindings)
|
||||||
{
|
{
|
||||||
ValidateBindings(instance.GetType(), bindings);
|
ValidateBindings(instance.GetType(), bindings);
|
||||||
@@ -205,6 +239,22 @@ namespace DynamORM.Helpers
|
|||||||
throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for result index {1}.", resultType.FullName, duplicates.Key));
|
throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for result index {1}.", resultType.FullName, duplicates.Key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void BindMainResultMembers(object instance, object mainResult)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IList<ResultMemberBinding> bindings = GetMainResultBindings(instance.GetType());
|
||||||
|
if (bindings.Count == 0)
|
||||||
|
return;
|
||||||
|
if (bindings.Count > 1)
|
||||||
|
throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for the main procedure result.", instance.GetType().FullName));
|
||||||
|
|
||||||
|
ResultMemberBinding binding = bindings[0];
|
||||||
|
object value = ConvertScalarValue(binding.MemberType, mainResult == DBNull.Value ? null : mainResult);
|
||||||
|
binding.SetValue(instance, value);
|
||||||
|
}
|
||||||
|
|
||||||
private static object ReadResultValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
private static object ReadResultValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
|
||||||
{
|
{
|
||||||
Type elementType;
|
Type elementType;
|
||||||
@@ -231,7 +281,6 @@ 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;
|
||||||
@@ -249,18 +298,7 @@ namespace DynamORM.Helpers
|
|||||||
if (!haveRow || value == null)
|
if (!haveRow || value == null)
|
||||||
return propertyType.GetDefaultValue();
|
return propertyType.GetDefaultValue();
|
||||||
|
|
||||||
if (targetType == typeof(Guid))
|
return ConvertScalarValue(propertyType, value);
|
||||||
{
|
|
||||||
Guid g;
|
|
||||||
if (Guid.TryParse(value.ToString(), out g))
|
|
||||||
return propertyType == typeof(Guid) ? (object)g : new Guid?(g);
|
|
||||||
return propertyType.GetDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -384,5 +422,31 @@ namespace DynamORM.Helpers
|
|||||||
return 0;
|
return 0;
|
||||||
return reader.GetOrdinal(attr.ColumnName);
|
return reader.GetOrdinal(attr.ColumnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsMainResultBinding(ProcedureResultAttribute attribute)
|
||||||
|
{
|
||||||
|
return attribute != null && attribute.ResultIndex < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ConvertScalarValue(Type targetType, object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return targetType.GetDefaultValue();
|
||||||
|
|
||||||
|
Type underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType;
|
||||||
|
|
||||||
|
if (underlyingType == typeof(Guid))
|
||||||
|
{
|
||||||
|
Guid g;
|
||||||
|
if (Guid.TryParse(value.ToString(), out g))
|
||||||
|
return targetType == typeof(Guid) ? (object)g : new Guid?(g);
|
||||||
|
return targetType.GetDefaultValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlyingType.IsEnum)
|
||||||
|
return Enum.ToObject(underlyingType, value);
|
||||||
|
|
||||||
|
return underlyingType == targetType ? targetType.CastObject(value) : underlyingType.CastObject(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,15 @@ namespace DynamORM.Mapper
|
|||||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||||
public class ProcedureResultAttribute : Attribute
|
public class ProcedureResultAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>Main procedure result marker.</summary>
|
||||||
|
public const int MainResultIndex = -1;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
||||||
|
public ProcedureResultAttribute()
|
||||||
|
: this(MainResultIndex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="ProcedureResultAttribute"/> class.</summary>
|
||||||
public ProcedureResultAttribute(int resultIndex)
|
public ProcedureResultAttribute(int resultIndex)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user