Add typed procedure descriptors and Exec invoker
This commit is contained in:
@@ -5361,21 +5361,43 @@ namespace DynamORM
|
|||||||
// Get generic types
|
// Get generic types
|
||||||
IList<Type> types = binder.GetGenericTypeArguments() ?? new List<Type>();
|
IList<Type> types = binder.GetGenericTypeArguments() ?? new List<Type>();
|
||||||
Type declaredResultType = null;
|
Type declaredResultType = null;
|
||||||
|
string procedureName = binder.Name;
|
||||||
|
string resultName = binder.Name;
|
||||||
|
Type execArgumentsType = null;
|
||||||
|
|
||||||
|
if (binder.Name == "Exec")
|
||||||
|
{
|
||||||
|
if (types.Count != 1)
|
||||||
|
throw new InvalidOperationException("Exec<TProcedure>(args) requires exactly one generic procedure descriptor type.");
|
||||||
|
|
||||||
|
DynamicProcedureDescriptor descriptor = DynamicProcedureDescriptor.Resolve(types[0]);
|
||||||
|
procedureName = descriptor.ProcedureName;
|
||||||
|
resultName = descriptor.ResultName;
|
||||||
|
execArgumentsType = descriptor.ArgumentsType;
|
||||||
|
declaredResultType = descriptor.ResultType;
|
||||||
|
types = new List<Type>();
|
||||||
|
|
||||||
|
if (args.Length > 1)
|
||||||
|
throw new InvalidOperationException("Exec<TProcedure>(args) accepts at most one arguments contract instance.");
|
||||||
|
|
||||||
|
if (args.Length == 1 && args[0] != null && !execArgumentsType.IsAssignableFrom(args[0].GetType()))
|
||||||
|
throw new InvalidOperationException(string.Format("Exec<{0}>(args) expects argument of type '{1}', received '{2}'.", descriptor.ProcedureType.FullName, execArgumentsType.FullName, args[0].GetType().FullName));
|
||||||
|
}
|
||||||
Dictionary<string, int> retParams = null;
|
Dictionary<string, int> retParams = null;
|
||||||
|
|
||||||
using (IDbConnection con = _db.Open())
|
using (IDbConnection con = _db.Open())
|
||||||
using (IDbCommand cmd = con.CreateCommand())
|
using (IDbCommand cmd = con.CreateCommand())
|
||||||
{
|
{
|
||||||
if (_prefixes == null || _prefixes.Count == 0)
|
if (_prefixes == null || _prefixes.Count == 0)
|
||||||
cmd.SetCommand(CommandType.StoredProcedure, binder.Name);
|
cmd.SetCommand(CommandType.StoredProcedure, procedureName);
|
||||||
else
|
else
|
||||||
cmd.SetCommand(CommandType.StoredProcedure, string.Format("{0}.{1}", string.Join(".", _prefixes), binder.Name));
|
cmd.SetCommand(CommandType.StoredProcedure, string.Format("{0}.{1}", string.Join(".", _prefixes), procedureName));
|
||||||
|
|
||||||
#region Prepare arguments
|
#region Prepare arguments
|
||||||
|
|
||||||
int alen = args.Length;
|
int alen = args.Length;
|
||||||
bool retIsAdded = false;
|
bool retIsAdded = false;
|
||||||
|
if (declaredResultType == null)
|
||||||
declaredResultType = alen == 1 ? DynamicProcedureResultBinder.GetDeclaredResultType(args[0]) : null;
|
declaredResultType = alen == 1 ? DynamicProcedureResultBinder.GetDeclaredResultType(args[0]) : null;
|
||||||
|
|
||||||
if (alen > 0)
|
if (alen > 0)
|
||||||
@@ -5561,7 +5583,7 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
using (IDataReader rdr = cmd.ExecuteReader())
|
using (IDataReader rdr = cmd.ExecuteReader())
|
||||||
using (IDataReader cache = rdr.CachedReader())
|
using (IDataReader cache = rdr.CachedReader())
|
||||||
mainResult = cache.ToDataTable(binder.Name);
|
mainResult = cache.ToDataTable(resultName);
|
||||||
}
|
}
|
||||||
else if (types[0].IsGenericEnumerable())
|
else if (types[0].IsGenericEnumerable())
|
||||||
{
|
{
|
||||||
@@ -5688,9 +5710,9 @@ namespace DynamORM
|
|||||||
if (mainResult != null)
|
if (mainResult != null)
|
||||||
{
|
{
|
||||||
if (mainResult == DBNull.Value)
|
if (mainResult == DBNull.Value)
|
||||||
res.Add(binder.Name, null);
|
res.Add(resultName, null);
|
||||||
else
|
else
|
||||||
res.Add(binder.Name, mainResult);
|
res.Add(resultName, mainResult);
|
||||||
}
|
}
|
||||||
foreach (KeyValuePair<string, int> pos in retParams)
|
foreach (KeyValuePair<string, int> pos in retParams)
|
||||||
res.Add(pos.Key, ((IDbDataParameter)cmd.Parameters[pos.Value]).Value);
|
res.Add(pos.Key, ((IDbDataParameter)cmd.Parameters[pos.Value]).Value);
|
||||||
@@ -5705,14 +5727,14 @@ namespace DynamORM
|
|||||||
result = res.ToDynamic();
|
result = res.ToDynamic();
|
||||||
}
|
}
|
||||||
else if (declaredResultType != null)
|
else if (declaredResultType != null)
|
||||||
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, binder.Name, mainResult, res.Where(x => x.Key != binder.Name).ToDictionary(x => x.Key, x => x.Value), mainResult != null && declaredResultType.IsInstanceOfType(mainResult) ? mainResult : null);
|
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, resultName, mainResult, res.Where(x => x.Key != resultName).ToDictionary(x => x.Key, x => x.Value), mainResult != null && declaredResultType.IsInstanceOfType(mainResult) ? mainResult : null);
|
||||||
else
|
else
|
||||||
result = res.ToDynamic();
|
result = res.ToDynamic();
|
||||||
}
|
}
|
||||||
else if (declaredResultType != null && mainResult != null && declaredResultType.IsInstanceOfType(mainResult))
|
else if (declaredResultType != null && mainResult != null && declaredResultType.IsInstanceOfType(mainResult))
|
||||||
result = mainResult;
|
result = mainResult;
|
||||||
else if (declaredResultType != null)
|
else if (declaredResultType != null)
|
||||||
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, binder.Name, mainResult, null);
|
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, resultName, mainResult, null);
|
||||||
else
|
else
|
||||||
result = mainResult;
|
result = mainResult;
|
||||||
|
|
||||||
@@ -14440,6 +14462,67 @@ namespace DynamORM
|
|||||||
return resultTable;
|
return resultTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal sealed class DynamicProcedureDescriptor
|
||||||
|
{
|
||||||
|
public Type ProcedureType { get; set; }
|
||||||
|
public Type ArgumentsType { get; set; }
|
||||||
|
public Type ResultType { get; set; }
|
||||||
|
public string ProcedureName { get; set; }
|
||||||
|
public string ResultName { get; set; }
|
||||||
|
|
||||||
|
internal static DynamicProcedureDescriptor Resolve(Type procedureType)
|
||||||
|
{
|
||||||
|
if (procedureType == null)
|
||||||
|
throw new ArgumentNullException("procedureType");
|
||||||
|
|
||||||
|
Type current = procedureType;
|
||||||
|
Type argumentsType = null;
|
||||||
|
Type resultType = null;
|
||||||
|
|
||||||
|
while (current != null && current != typeof(object))
|
||||||
|
{
|
||||||
|
if (current.IsGenericType)
|
||||||
|
{
|
||||||
|
Type genericDefinition = current.GetGenericTypeDefinition();
|
||||||
|
Type[] genericArguments = current.GetGenericArguments();
|
||||||
|
|
||||||
|
if (genericDefinition == typeof(Procedure<>) && genericArguments.Length == 1)
|
||||||
|
{
|
||||||
|
argumentsType = genericArguments[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (genericDefinition == typeof(Procedure<,>) && genericArguments.Length == 2)
|
||||||
|
{
|
||||||
|
argumentsType = genericArguments[0];
|
||||||
|
resultType = genericArguments[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current.BaseType;
|
||||||
|
}
|
||||||
|
if (argumentsType == null)
|
||||||
|
throw new InvalidOperationException(string.Format("Type '{0}' is not a typed procedure descriptor.", procedureType.FullName));
|
||||||
|
|
||||||
|
if (!typeof(IProcedureParameters).IsAssignableFrom(argumentsType))
|
||||||
|
throw new InvalidOperationException(string.Format("Procedure descriptor '{0}' declares argument type '{1}' that does not implement IProcedureParameters.", procedureType.FullName, argumentsType.FullName));
|
||||||
|
|
||||||
|
ProcedureAttribute attr = procedureType.GetCustomAttributes(typeof(ProcedureAttribute), true)
|
||||||
|
.Cast<ProcedureAttribute>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
string name = attr != null && !string.IsNullOrEmpty(attr.Name) ? attr.Name : procedureType.Name;
|
||||||
|
string owner = attr != null ? attr.Owner : null;
|
||||||
|
|
||||||
|
return new DynamicProcedureDescriptor
|
||||||
|
{
|
||||||
|
ProcedureType = procedureType,
|
||||||
|
ArgumentsType = argumentsType,
|
||||||
|
ResultType = resultType,
|
||||||
|
ProcedureName = string.IsNullOrEmpty(owner) ? name : string.Format("{0}.{1}", owner, name),
|
||||||
|
ResultName = name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
internal static class DynamicProcedureParameterBinder
|
internal static class DynamicProcedureParameterBinder
|
||||||
{
|
{
|
||||||
internal sealed class BindingResult
|
internal sealed class BindingResult
|
||||||
@@ -17935,6 +18018,19 @@ namespace DynamORM
|
|||||||
public class IgnoreAttribute : Attribute
|
public class IgnoreAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
/// <summary>Allows to add stored procedure metadata to class.</summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class ProcedureAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>Gets or sets procedure owner name.</summary>
|
||||||
|
public string Owner { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets procedure name.</summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether metadata overrides other defaults.</summary>
|
||||||
|
public bool Override { get; set; }
|
||||||
|
}
|
||||||
/// <summary>Declares metadata for object-based stored procedure parameters.</summary>
|
/// <summary>Declares metadata for object-based stored procedure parameters.</summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class ProcedureParameterAttribute : ColumnAttribute
|
public class ProcedureParameterAttribute : ColumnAttribute
|
||||||
@@ -18456,6 +18552,19 @@ namespace DynamORM
|
|||||||
_database = null;
|
_database = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>Base class for typed stored procedure descriptors.</summary>
|
||||||
|
/// <typeparam name="TArgs">Procedure arguments contract.</typeparam>
|
||||||
|
public abstract class Procedure<TArgs>
|
||||||
|
where TArgs : IProcedureParameters
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/// <summary>Base class for typed stored procedure descriptors with explicit result model.</summary>
|
||||||
|
/// <typeparam name="TArgs">Procedure arguments contract.</typeparam>
|
||||||
|
/// <typeparam name="TResult">Procedure result model.</typeparam>
|
||||||
|
public abstract class Procedure<TArgs, TResult> : Procedure<TArgs>
|
||||||
|
where TArgs : IProcedureParameters
|
||||||
|
{
|
||||||
|
}
|
||||||
/// <summary>Marks an object as an explicit stored procedure parameter contract.</summary>
|
/// <summary>Marks an object as an explicit stored procedure parameter contract.</summary>
|
||||||
public interface IProcedureParameters
|
public interface IProcedureParameters
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -139,4 +139,18 @@ namespace DynamORM.Tests.Helpers
|
|||||||
[ProcedureParameter("status", Direction = ParameterDirection.Output, Order = 1, DbType = DbType.Int32)]
|
[ProcedureParameter("status", Direction = ParameterDirection.Output, Order = 1, DbType = DbType.Int32)]
|
||||||
public int Status { get; set; }
|
public int Status { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Procedure(Name = "sp_exec_test", Owner = "dbo")]
|
||||||
|
public class ExecProcedureDescriptor : Procedure<ProcedureParameterObject>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExecProcedureDefaultDescriptor : Procedure<ProcedureParameterObject>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Procedure(Name = "sp_exec_result")]
|
||||||
|
public class ExecProcedureDescriptorWithExplicitResult : Procedure<ProcedureParameterColumnFallbackObject, ProcedureAttributedResult>
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Dynamic;
|
||||||
using DynamORM.Helpers;
|
using DynamORM.Helpers;
|
||||||
using DynamORM.Tests.Helpers;
|
using DynamORM.Tests.Helpers;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@@ -122,6 +123,55 @@ namespace DynamORM.Tests.Procedure
|
|||||||
Assert.IsNull(DynamicProcedureResultBinder.GetDeclaredResultType(new object()));
|
Assert.IsNull(DynamicProcedureResultBinder.GetDeclaredResultType(new object()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestProcedureDescriptorResolvesAttributeNameAndArguments()
|
||||||
|
{
|
||||||
|
var descriptor = DynamicProcedureDescriptor.Resolve(typeof(ExecProcedureDescriptor));
|
||||||
|
|
||||||
|
Assert.AreEqual(typeof(ExecProcedureDescriptor), descriptor.ProcedureType);
|
||||||
|
Assert.AreEqual(typeof(ProcedureParameterObject), descriptor.ArgumentsType);
|
||||||
|
Assert.IsNull(descriptor.ResultType);
|
||||||
|
Assert.AreEqual("dbo.sp_exec_test", descriptor.ProcedureName);
|
||||||
|
Assert.AreEqual("sp_exec_test", descriptor.ResultName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestProcedureDescriptorResolvesDefaultNameAndExplicitResult()
|
||||||
|
{
|
||||||
|
var defaultDescriptor = DynamicProcedureDescriptor.Resolve(typeof(ExecProcedureDefaultDescriptor));
|
||||||
|
var explicitDescriptor = DynamicProcedureDescriptor.Resolve(typeof(ExecProcedureDescriptorWithExplicitResult));
|
||||||
|
|
||||||
|
Assert.AreEqual("ExecProcedureDefaultDescriptor", defaultDescriptor.ProcedureName);
|
||||||
|
Assert.AreEqual(typeof(ProcedureParameterObject), defaultDescriptor.ArgumentsType);
|
||||||
|
Assert.IsNull(defaultDescriptor.ResultType);
|
||||||
|
|
||||||
|
Assert.AreEqual("sp_exec_result", explicitDescriptor.ProcedureName);
|
||||||
|
Assert.AreEqual(typeof(ProcedureParameterColumnFallbackObject), explicitDescriptor.ArgumentsType);
|
||||||
|
Assert.AreEqual(typeof(ProcedureAttributedResult), explicitDescriptor.ResultType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExecRejectsWrongArgumentsType()
|
||||||
|
{
|
||||||
|
dynamic procedures = new DynamicProcedureInvoker(null);
|
||||||
|
|
||||||
|
Assert.Throws<System.InvalidOperationException>(() =>
|
||||||
|
{
|
||||||
|
var ignored = procedures.Exec<ExecProcedureDescriptor>(new ProcedureParameterColumnFallbackObject());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExecRejectsMultipleArguments()
|
||||||
|
{
|
||||||
|
dynamic procedures = new DynamicProcedureInvoker(null);
|
||||||
|
|
||||||
|
Assert.Throws<System.InvalidOperationException>(() =>
|
||||||
|
{
|
||||||
|
var ignored = procedures.Exec<ExecProcedureDescriptor>(new ProcedureParameterObject(), new ProcedureParameterObject());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDeclaredResultPayloadBindingMapsMainAndOutValues()
|
public void TestDeclaredResultPayloadBindingMapsMainAndOutValues()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -94,6 +94,28 @@ namespace DynamORM
|
|||||||
// Get generic types
|
// Get generic types
|
||||||
IList<Type> types = binder.GetGenericTypeArguments() ?? new List<Type>();
|
IList<Type> types = binder.GetGenericTypeArguments() ?? new List<Type>();
|
||||||
Type declaredResultType = null;
|
Type declaredResultType = null;
|
||||||
|
string procedureName = binder.Name;
|
||||||
|
string resultName = binder.Name;
|
||||||
|
Type execArgumentsType = null;
|
||||||
|
|
||||||
|
if (binder.Name == "Exec")
|
||||||
|
{
|
||||||
|
if (types.Count != 1)
|
||||||
|
throw new InvalidOperationException("Exec<TProcedure>(args) requires exactly one generic procedure descriptor type.");
|
||||||
|
|
||||||
|
DynamicProcedureDescriptor descriptor = DynamicProcedureDescriptor.Resolve(types[0]);
|
||||||
|
procedureName = descriptor.ProcedureName;
|
||||||
|
resultName = descriptor.ResultName;
|
||||||
|
execArgumentsType = descriptor.ArgumentsType;
|
||||||
|
declaredResultType = descriptor.ResultType;
|
||||||
|
types = new List<Type>();
|
||||||
|
|
||||||
|
if (args.Length > 1)
|
||||||
|
throw new InvalidOperationException("Exec<TProcedure>(args) accepts at most one arguments contract instance.");
|
||||||
|
|
||||||
|
if (args.Length == 1 && args[0] != null && !execArgumentsType.IsAssignableFrom(args[0].GetType()))
|
||||||
|
throw new InvalidOperationException(string.Format("Exec<{0}>(args) expects argument of type '{1}', received '{2}'.", descriptor.ProcedureType.FullName, execArgumentsType.FullName, args[0].GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
Dictionary<string, int> retParams = null;
|
Dictionary<string, int> retParams = null;
|
||||||
|
|
||||||
@@ -101,14 +123,15 @@ namespace DynamORM
|
|||||||
using (IDbCommand cmd = con.CreateCommand())
|
using (IDbCommand cmd = con.CreateCommand())
|
||||||
{
|
{
|
||||||
if (_prefixes == null || _prefixes.Count == 0)
|
if (_prefixes == null || _prefixes.Count == 0)
|
||||||
cmd.SetCommand(CommandType.StoredProcedure, binder.Name);
|
cmd.SetCommand(CommandType.StoredProcedure, procedureName);
|
||||||
else
|
else
|
||||||
cmd.SetCommand(CommandType.StoredProcedure, string.Format("{0}.{1}", string.Join(".", _prefixes), binder.Name));
|
cmd.SetCommand(CommandType.StoredProcedure, string.Format("{0}.{1}", string.Join(".", _prefixes), procedureName));
|
||||||
|
|
||||||
#region Prepare arguments
|
#region Prepare arguments
|
||||||
|
|
||||||
int alen = args.Length;
|
int alen = args.Length;
|
||||||
bool retIsAdded = false;
|
bool retIsAdded = false;
|
||||||
|
if (declaredResultType == null)
|
||||||
declaredResultType = alen == 1 ? DynamicProcedureResultBinder.GetDeclaredResultType(args[0]) : null;
|
declaredResultType = alen == 1 ? DynamicProcedureResultBinder.GetDeclaredResultType(args[0]) : null;
|
||||||
|
|
||||||
if (alen > 0)
|
if (alen > 0)
|
||||||
@@ -301,7 +324,7 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
using (IDataReader rdr = cmd.ExecuteReader())
|
using (IDataReader rdr = cmd.ExecuteReader())
|
||||||
using (IDataReader cache = rdr.CachedReader())
|
using (IDataReader cache = rdr.CachedReader())
|
||||||
mainResult = cache.ToDataTable(binder.Name);
|
mainResult = cache.ToDataTable(resultName);
|
||||||
}
|
}
|
||||||
else if (types[0].IsGenericEnumerable())
|
else if (types[0].IsGenericEnumerable())
|
||||||
{
|
{
|
||||||
@@ -433,9 +456,9 @@ namespace DynamORM
|
|||||||
if (mainResult != null)
|
if (mainResult != null)
|
||||||
{
|
{
|
||||||
if (mainResult == DBNull.Value)
|
if (mainResult == DBNull.Value)
|
||||||
res.Add(binder.Name, null);
|
res.Add(resultName, null);
|
||||||
else
|
else
|
||||||
res.Add(binder.Name, mainResult);
|
res.Add(resultName, mainResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (KeyValuePair<string, int> pos in retParams)
|
foreach (KeyValuePair<string, int> pos in retParams)
|
||||||
@@ -451,14 +474,14 @@ namespace DynamORM
|
|||||||
result = res.ToDynamic();
|
result = res.ToDynamic();
|
||||||
}
|
}
|
||||||
else if (declaredResultType != null)
|
else if (declaredResultType != null)
|
||||||
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, binder.Name, mainResult, res.Where(x => x.Key != binder.Name).ToDictionary(x => x.Key, x => x.Value), mainResult != null && declaredResultType.IsInstanceOfType(mainResult) ? mainResult : null);
|
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, resultName, mainResult, res.Where(x => x.Key != resultName).ToDictionary(x => x.Key, x => x.Value), mainResult != null && declaredResultType.IsInstanceOfType(mainResult) ? mainResult : null);
|
||||||
else
|
else
|
||||||
result = res.ToDynamic();
|
result = res.ToDynamic();
|
||||||
}
|
}
|
||||||
else if (declaredResultType != null && mainResult != null && declaredResultType.IsInstanceOfType(mainResult))
|
else if (declaredResultType != null && mainResult != null && declaredResultType.IsInstanceOfType(mainResult))
|
||||||
result = mainResult;
|
result = mainResult;
|
||||||
else if (declaredResultType != null)
|
else if (declaredResultType != null)
|
||||||
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, binder.Name, mainResult, null);
|
result = DynamicProcedureResultBinder.BindPayload(declaredResultType, resultName, mainResult, null);
|
||||||
else
|
else
|
||||||
result = mainResult;
|
result = mainResult;
|
||||||
|
|
||||||
|
|||||||
100
DynamORM/Helpers/DynamicProcedureDescriptor.cs
Normal file
100
DynamORM/Helpers/DynamicProcedureDescriptor.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
using DynamORM.Objects;
|
||||||
|
|
||||||
|
namespace DynamORM.Helpers
|
||||||
|
{
|
||||||
|
internal sealed class DynamicProcedureDescriptor
|
||||||
|
{
|
||||||
|
public Type ProcedureType { get; set; }
|
||||||
|
public Type ArgumentsType { get; set; }
|
||||||
|
public Type ResultType { get; set; }
|
||||||
|
public string ProcedureName { get; set; }
|
||||||
|
public string ResultName { get; set; }
|
||||||
|
|
||||||
|
internal static DynamicProcedureDescriptor Resolve(Type procedureType)
|
||||||
|
{
|
||||||
|
if (procedureType == null)
|
||||||
|
throw new ArgumentNullException("procedureType");
|
||||||
|
|
||||||
|
Type current = procedureType;
|
||||||
|
Type argumentsType = null;
|
||||||
|
Type resultType = null;
|
||||||
|
|
||||||
|
while (current != null && current != typeof(object))
|
||||||
|
{
|
||||||
|
if (current.IsGenericType)
|
||||||
|
{
|
||||||
|
Type genericDefinition = current.GetGenericTypeDefinition();
|
||||||
|
Type[] genericArguments = current.GetGenericArguments();
|
||||||
|
|
||||||
|
if (genericDefinition == typeof(Procedure<>) && genericArguments.Length == 1)
|
||||||
|
{
|
||||||
|
argumentsType = genericArguments[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genericDefinition == typeof(Procedure<,>) && genericArguments.Length == 2)
|
||||||
|
{
|
||||||
|
argumentsType = genericArguments[0];
|
||||||
|
resultType = genericArguments[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argumentsType == null)
|
||||||
|
throw new InvalidOperationException(string.Format("Type '{0}' is not a typed procedure descriptor.", procedureType.FullName));
|
||||||
|
|
||||||
|
if (!typeof(IProcedureParameters).IsAssignableFrom(argumentsType))
|
||||||
|
throw new InvalidOperationException(string.Format("Procedure descriptor '{0}' declares argument type '{1}' that does not implement IProcedureParameters.", procedureType.FullName, argumentsType.FullName));
|
||||||
|
|
||||||
|
ProcedureAttribute attr = procedureType.GetCustomAttributes(typeof(ProcedureAttribute), true)
|
||||||
|
.Cast<ProcedureAttribute>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
string name = attr != null && !string.IsNullOrEmpty(attr.Name) ? attr.Name : procedureType.Name;
|
||||||
|
string owner = attr != null ? attr.Owner : null;
|
||||||
|
|
||||||
|
return new DynamicProcedureDescriptor
|
||||||
|
{
|
||||||
|
ProcedureType = procedureType,
|
||||||
|
ArgumentsType = argumentsType,
|
||||||
|
ResultType = resultType,
|
||||||
|
ProcedureName = string.IsNullOrEmpty(owner) ? name : string.Format("{0}.{1}", owner, name),
|
||||||
|
ResultName = name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
DynamORM/Mapper/ProcedureAttribute.cs
Normal file
46
DynamORM/Mapper/ProcedureAttribute.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DynamORM.Mapper
|
||||||
|
{
|
||||||
|
/// <summary>Allows to add stored procedure metadata to class.</summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class ProcedureAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>Gets or sets procedure owner name.</summary>
|
||||||
|
public string Owner { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets procedure name.</summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether metadata overrides other defaults.</summary>
|
||||||
|
public bool Override { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
45
DynamORM/Objects/Procedure.cs
Normal file
45
DynamORM/Objects/Procedure.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace DynamORM.Objects
|
||||||
|
{
|
||||||
|
/// <summary>Base class for typed stored procedure descriptors.</summary>
|
||||||
|
/// <typeparam name="TArgs">Procedure arguments contract.</typeparam>
|
||||||
|
public abstract class Procedure<TArgs>
|
||||||
|
where TArgs : IProcedureParameters
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Base class for typed stored procedure descriptors with explicit result model.</summary>
|
||||||
|
/// <typeparam name="TArgs">Procedure arguments contract.</typeparam>
|
||||||
|
/// <typeparam name="TResult">Procedure result model.</typeparam>
|
||||||
|
public abstract class Procedure<TArgs, TResult> : Procedure<TArgs>
|
||||||
|
where TArgs : IProcedureParameters
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user