Add typed procedure descriptors and Exec invoker

This commit is contained in:
2026-02-27 16:27:49 +01:00
parent 9ce10273f1
commit 416404f8d1
7 changed files with 410 additions and 23 deletions

View File

@@ -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
{ {

View File

@@ -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>
{
}
} }

View File

@@ -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()
{ {

View File

@@ -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;

View 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
};
}
}
}

View 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; }
}
}

View 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
{
}
}