353 lines
15 KiB
C#
353 lines
15 KiB
C#
/*
|
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
|
* All rights reserved.
|
|
*/
|
|
|
|
using System.Data;
|
|
using System.Dynamic;
|
|
using DynamORM.Helpers;
|
|
using DynamORM.Tests.Helpers;
|
|
using NUnit.Framework;
|
|
|
|
namespace DynamORM.Tests.Procedure
|
|
{
|
|
[TestFixture]
|
|
public class ProcedureParameterBinderTests : TestsBase
|
|
{
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
CreateTestDatabase();
|
|
CreateDynamicDatabase(
|
|
DynamicDatabaseOptions.SingleConnection |
|
|
DynamicDatabaseOptions.SingleTransaction |
|
|
DynamicDatabaseOptions.SupportStoredProcedures |
|
|
DynamicDatabaseOptions.SupportStoredProceduresResult |
|
|
DynamicDatabaseOptions.SupportSchema);
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
DestroyDynamicDatabase();
|
|
DestroyTestDatabase();
|
|
}
|
|
|
|
[Test]
|
|
public void TestCanBindProcedureParameterObject()
|
|
{
|
|
Assert.IsTrue(DynamicProcedureParameterBinder.CanBind(new ProcedureParameterObject()));
|
|
Assert.IsTrue(DynamicProcedureParameterBinder.CanBind(new ProcedureParameterColumnFallbackObject()));
|
|
Assert.IsFalse(DynamicProcedureParameterBinder.CanBind("x"));
|
|
Assert.IsFalse(DynamicProcedureParameterBinder.CanBind(5));
|
|
}
|
|
|
|
[Test]
|
|
public void TestBindUsesAttributeMetadataAndOrder()
|
|
{
|
|
using (IDbCommand cmd = new FakeDbCommand())
|
|
{
|
|
var result = DynamicProcedureParameterBinder.Bind(Database, cmd, new ProcedureParameterObject
|
|
{
|
|
Code = "ABC",
|
|
Description = "seed"
|
|
});
|
|
|
|
Assert.IsTrue(result.ReturnValueAdded);
|
|
Assert.NotNull(result.ReturnParameters);
|
|
Assert.AreEqual(3, result.ReturnParameters.Count);
|
|
Assert.AreEqual(4, cmd.Parameters.Count);
|
|
|
|
var p0 = (IDbDataParameter)cmd.Parameters[0];
|
|
var p1 = (IDbDataParameter)cmd.Parameters[1];
|
|
var p2 = (IDbDataParameter)cmd.Parameters[2];
|
|
var p3 = (IDbDataParameter)cmd.Parameters[3];
|
|
|
|
Assert.AreEqual(Database.GetParameterName("status"), p0.ParameterName);
|
|
Assert.AreEqual(ParameterDirection.ReturnValue, p0.Direction);
|
|
Assert.AreEqual(DbType.Int32, p0.DbType);
|
|
Assert.AreEqual(4, p0.Size);
|
|
|
|
Assert.AreEqual(Database.GetParameterName("code"), p1.ParameterName);
|
|
Assert.AreEqual(ParameterDirection.Input, p1.Direction);
|
|
Assert.AreEqual(DbType.String, p1.DbType);
|
|
Assert.AreEqual(32, p1.Size);
|
|
Assert.AreEqual("ABC", p1.Value);
|
|
|
|
Assert.AreEqual(Database.GetParameterName("result"), p2.ParameterName);
|
|
Assert.AreEqual(ParameterDirection.Output, p2.Direction);
|
|
Assert.AreEqual(DbType.Int32, p2.DbType);
|
|
Assert.AreEqual(DBNull.Value, p2.Value);
|
|
|
|
Assert.AreEqual(Database.GetParameterName("description"), p3.ParameterName);
|
|
Assert.AreEqual(ParameterDirection.InputOutput, p3.Direction);
|
|
Assert.AreEqual(DbType.String, p3.DbType);
|
|
Assert.AreEqual(256, p3.Size);
|
|
Assert.AreEqual("seed", p3.Value);
|
|
|
|
Assert.AreEqual(0, result.ReturnParameters["status"]);
|
|
Assert.AreEqual(2, result.ReturnParameters["result"]);
|
|
Assert.AreEqual(3, result.ReturnParameters["description"]);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void TestBindFallsBackToColumnAttributeMetadata()
|
|
{
|
|
using (IDbCommand cmd = new FakeDbCommand())
|
|
{
|
|
var result = DynamicProcedureParameterBinder.Bind(Database, cmd, new ProcedureParameterColumnFallbackObject
|
|
{
|
|
Code = "XYZ"
|
|
});
|
|
|
|
Assert.IsFalse(result.ReturnValueAdded);
|
|
Assert.IsNull(result.ReturnParameters);
|
|
Assert.AreEqual(1, cmd.Parameters.Count);
|
|
|
|
var p0 = (IDbDataParameter)cmd.Parameters[0];
|
|
Assert.AreEqual(Database.GetParameterName("code"), p0.ParameterName);
|
|
Assert.AreEqual(ParameterDirection.Input, p0.Direction);
|
|
Assert.AreEqual(DbType.String, p0.DbType);
|
|
Assert.AreEqual(64, p0.Size);
|
|
Assert.AreEqual("XYZ", p0.Value);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void TestDeclaredResultTypeComesFromContractInterface()
|
|
{
|
|
Assert.AreEqual(typeof(ProcedureParameterResult), DynamicProcedureResultBinder.GetDeclaredResultType(new ProcedureParameterObject()));
|
|
Assert.AreEqual(typeof(ProcedureMultiResult), DynamicProcedureResultBinder.GetDeclaredResultType(new ProcedureMultiResultArgs()));
|
|
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]
|
|
public void TestDeclaredResultPayloadBindingMapsMainAndOutValues()
|
|
{
|
|
var result = DynamicProcedureResultBinder.BindPayload(
|
|
typeof(ProcedureParameterResult),
|
|
"sp_Test",
|
|
15,
|
|
new System.Collections.Generic.Dictionary<string, object>
|
|
{
|
|
{ "result", 7 },
|
|
{ "description", "done" },
|
|
{ "status", 3 }
|
|
}) as ProcedureParameterResult;
|
|
|
|
Assert.NotNull(result);
|
|
Assert.AreEqual(15, result.MainResult);
|
|
Assert.AreEqual(7, result.Result);
|
|
Assert.AreEqual("done", result.Description);
|
|
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]
|
|
public void TestDeclaredResultReaderCanConsumeMultipleResultSets()
|
|
{
|
|
using (var reader = new FakeMultiResultDataReader(
|
|
Tuple.Create(
|
|
new[] { "Code" },
|
|
new[] { typeof(string) },
|
|
new[]
|
|
{
|
|
new object[] { "A" },
|
|
new object[] { "B" }
|
|
}),
|
|
Tuple.Create(
|
|
new[] { "State" },
|
|
new[] { typeof(int) },
|
|
new[]
|
|
{
|
|
new object[] { 10 },
|
|
new object[] { 20 }
|
|
})))
|
|
{
|
|
var result = DynamicProcedureResultBinder.ReadDeclaredResult(typeof(ProcedureMultiResult), reader) as ProcedureMultiResult;
|
|
|
|
Assert.NotNull(result);
|
|
CollectionAssert.AreEqual(new[] { "A", "B" }, result.Codes);
|
|
CollectionAssert.AreEqual(new[] { 10, 20 }, result.States);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void TestDeclaredResultCanReadAttributedResultSets()
|
|
{
|
|
using (var reader = new FakeMultiResultDataReader(
|
|
Tuple.Create(new[] { "Code" }, new[] { typeof(string) }, new[] { new object[] { "FIRST" }, new object[] { "SECOND" } }),
|
|
Tuple.Create(new[] { "Code" }, new[] { typeof(string) }, new[] { new object[] { "A" }, new object[] { "B" } }),
|
|
Tuple.Create(new[] { "State" }, new[] { typeof(int) }, new[] { new object[] { 10 }, new object[] { 20 } }),
|
|
Tuple.Create(
|
|
new[] { "id", "code", "first" },
|
|
new[] { typeof(long), typeof(string), typeof(string) },
|
|
new[] { new object[] { 1L, "U1", "One" } }),
|
|
Tuple.Create(
|
|
new[] { "id", "code", "first" },
|
|
new[] { typeof(long), typeof(string), typeof(string) },
|
|
new[] { new object[] { 2L, "U2", "Two" }, new object[] { 3L, "U3", "Three" } }),
|
|
Tuple.Create(new[] { "Code" }, new[] { typeof(string) }, new[] { new object[] { "X" }, new object[] { "Y" } })))
|
|
{
|
|
var result = DynamicProcedureResultBinder.ReadDeclaredResult(typeof(ProcedureAttributedResult), reader) as ProcedureAttributedResult;
|
|
|
|
Assert.NotNull(result);
|
|
Assert.AreEqual("FIRST", result.FirstCode);
|
|
CollectionAssert.AreEqual(new[] { "A", "B" }, result.Codes);
|
|
CollectionAssert.AreEqual(new[] { 10, 20 }, result.States);
|
|
Assert.NotNull(result.User);
|
|
Assert.AreEqual(1L, result.User.Id);
|
|
Assert.AreEqual("U1", result.User.Code);
|
|
Assert.NotNull(result.AllUsers);
|
|
Assert.AreEqual(2, result.AllUsers.Count);
|
|
Assert.NotNull(result.CodesTable);
|
|
Assert.AreEqual("codes_table", result.CodesTable.TableName);
|
|
Assert.AreEqual(2, result.CodesTable.Rows.Count);
|
|
}
|
|
}
|
|
|
|
[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]
|
|
public void TestDeclaredResultPayloadCanAugmentReaderResult()
|
|
{
|
|
var existing = new ProcedureMultiResult();
|
|
existing.Codes.Add("A");
|
|
existing.States.Add(10);
|
|
|
|
var result = DynamicProcedureResultBinder.BindPayload(
|
|
typeof(ProcedureMultiResult),
|
|
"sp_Multi",
|
|
99,
|
|
new System.Collections.Generic.Dictionary<string, object>
|
|
{
|
|
{ "status", 5 }
|
|
},
|
|
existing) as ProcedureMultiResult;
|
|
|
|
Assert.AreSame(existing, result);
|
|
Assert.AreEqual(99, result.MainResult);
|
|
Assert.AreEqual(5, result.Status);
|
|
CollectionAssert.AreEqual(new[] { "A" }, result.Codes);
|
|
CollectionAssert.AreEqual(new[] { 10 }, result.States);
|
|
}
|
|
|
|
[Test]
|
|
public void TestDeclaredResultBindingDetectionSupportsAttributedResults()
|
|
{
|
|
Assert.IsTrue(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureMultiResult)));
|
|
Assert.IsTrue(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureAttributedResult)));
|
|
Assert.IsTrue(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureAttributedFieldResult)));
|
|
Assert.IsFalse(DynamicProcedureResultBinder.CanReadResults(typeof(ProcedureParameterResult)));
|
|
}
|
|
}
|
|
}
|