Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use DynamicParameters with Template and have a return parameter in dapper?

Tags:

c#

.net

dapper

The system I am currently working on uses Stored Procedures for all data access. I'm looking into Dapper at the moment (so far it looks great) but I was wondering if I can use a DynamicParameters object created using a Template but make one of the parameters an output param. For example:

SP:

CREATE PROCEDURE InsertPerson
  @ID int Output,
  @Name varchar(100),
  @DOB DateTime2
AS
--INSERT STATEMENT

SET @ID = SCOPE_IDENTITY()

POCO:

internal class Person
{
  public int ID { get; set; }
  public string Name { get; set; }
  public DateTime DOB { get; set; }
}

Code:

var procParams = new DynamicParameters(person);
connection.Execute("InsertPerson", procParams, commandType: CommandType.StoredProcedure);

// This is where i'm having the issue, can it be done?
person.ID = procParams.Get<int>("ID");

Current I receive an error because the key was not found. Is there a way to get the ID output parameter without manually setting up all of the stored procs parameters?

like image 221
Fermin Avatar asked Nov 04 '11 14:11

Fermin


2 Answers

With a quick tweak, Add now replaces the value from a template, allowing:

public void TestProcWithOutParameter()
{
    connection.Execute(
        @"CREATE PROCEDURE #TestProcWithOutParameter
@ID int output,
@Foo varchar(100),
@Bar int
AS
SET @ID = @Bar + LEN(@Foo)");
    var obj = new
    { // this could be a Person instance etc
        ID = 0,
        Foo = "abc",
        Bar = 4
    };
    var args = new DynamicParameters(obj);
    args.Add("ID", 0, direction: ParameterDirection.Output);
    connection.Execute("#TestProcWithOutParameter", args,
                 commandType: CommandType.StoredProcedure);
    args.Get<int>("ID").IsEqualTo(7);
}

Is that close enough? You can also use ParameterDirection.ReturnValue, of either a pre-existing value or a new value. Note it does not update directly back into the original template; the value must be fetched from the DynamicParameters instance (as shown).

like image 181
Marc Gravell Avatar answered Oct 18 '22 06:10

Marc Gravell


When you use the constructor for DynamicParameters to specify a template object you will still need to specify that @ID is an output parameter. At first, via the template, it will be set to ParameterDirection.Input. Once you add it, it will be overridden to have the updated values, then you can get the value by the parameter name as follows:

procParams.Add("@ID", dbType: DbType.Int32, direction: ParameterDirection.Output);
// ... execute ...
person.ID = procParams.Get<int>("@ID");

I was able to get this working and used your classes and code, in addition to what I showed above.

EDIT: as discussed in the comments, the stored procedure doesn't accept more arguments than it has declared. Thus, an alternate approach is to ditch the stored procedure and resort to some inline SQL. When you use a query Dapper will ignore any parameters given to it that aren't specified in the SQL statement. This is a work-around to tackle this issue:

string sql = "INSERT INTO Person (Name, DOB) VALUES (@Name, @DOB) SELECT SCOPE_IDENTITY()";
decimal id = conn.Query<decimal>(sql, procParams).First();
person.ID = (int)id;

Note that SCOPE_IDENTITY() returns a decimal, not an int.

Another idea, which I think isn't ideal, is to modify the Dapper code and add a Remove method to the DynamicParameters class to remove undesired parameters. This doesn't save you much though since you'll still spend time specifying which parameters to remove all for the sake of making a stored procedure happy. If you decide to implement this remember that case matters when specifying the key to remove from the parameters dictionary.

like image 33
Ahmad Mageed Avatar answered Oct 18 '22 05:10

Ahmad Mageed