/* * 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 TestExecMethodRejectsWrongArgumentsType() { var procedures = new DynamicProcedureInvoker(null); Assert.Throws(() => { var ignored = procedures.Exec(new ProcedureParameterColumnFallbackObject()); }); } [Test] public void TestDynamicDatabaseTypedProcedureRejectsWrongArgumentsType() { Assert.Throws(() => { var ignored = Database.Procedure(new ProcedureParameterColumnFallbackObject()); }); } [Test] public void TestDeclaredResultPayloadBindingMapsMainAndOutValues() { var result = DynamicProcedureResultBinder.BindPayload( typeof(ProcedureParameterResult), "sp_Test", 15, new System.Collections.Generic.Dictionary { { "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 { { "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 { { "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))); } } }