Support main procedure result via ProcedureResultAttribute

This commit is contained in:
2026-02-27 16:21:42 +01:00
parent 6ebda34a04
commit 9ce10273f1
5 changed files with 219 additions and 33 deletions

View File

@@ -14648,13 +14648,17 @@ namespace DynamORM
payload[item.Key] = item.Value == DBNull.Value ? null : item.Value;
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType);
object instance = existing;
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)
return existing;
if (instance != null && resultType.IsInstanceOfType(instance))
BindMainResultMembers(instance, mainResult);
return payload.ToDynamic();
return instance;
}
private static IList<ResultMemberBinding> GetResultMemberBindings(Type resultType)
{
@@ -14680,10 +14684,39 @@ namespace DynamORM
return properties.Concat(fields)
.Where(x => x.Attribute != null)
.Where(x => !IsMainResultBinding(x.Attribute))
.OrderBy(x => x.Attribute.ResultIndex)
.ThenBy(x => x.SortOrder)
.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)
{
ValidateBindings(instance.GetType(), bindings);
@@ -14718,6 +14751,21 @@ namespace DynamORM
if (duplicates != null)
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)
{
Type elementType;
@@ -14742,7 +14790,6 @@ namespace DynamORM
}
private static object ReadSimpleValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader)
{
Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
object value = null;
int ordinal = -1;
bool haveRow = false;
@@ -14759,17 +14806,7 @@ namespace DynamORM
if (!haveRow || value == null)
return propertyType.GetDefaultValue();
if (targetType == typeof(Guid))
{
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);
return ConvertScalarValue(propertyType, value);
}
private static object ReadSimpleList(Type propertyType, Type elementType, ProcedureResultAttribute attr, IDataReader reader)
{
@@ -14879,6 +14916,29 @@ namespace DynamORM
return 0;
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>
public static class FrameworkTools
@@ -17927,6 +17987,14 @@ namespace DynamORM
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
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>
public ProcedureResultAttribute(int resultIndex)
{