Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a List of objects to SQL Server stored procedure and retrieving an output value

I'm trying to pass list of objects and some other numbers and strings parameters to a SQL Server stored procedure using ado.net and execute it, and retrieve a value from an output parameter.

To capture the list of objects from the SQL Server stored procedure, I used a user-defined table type as shown here:

enter image description here

And this is the stored procedure:

ALTER PROCEDURE [dbo].[UpdateMailjetDetails] 
    @listCode VARCHAR(1000),
    @listName VARCHAR(1000),
    @mailjetListId BIGINT,
    @mailjetListStatus INT,
    @autoAsync BIT,
    @contacts Contact READONLY,
    @companyId INT,
    @tblContactCompanyStatus INT,   
    @espListContactStatus INT,
    @outputMessage VARCHAR(1000) OUT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION [Tran1]
    BEGIN TRY
         -- logic
         --
         --

         SET @outputMessage = 'success';    
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION [Tran1];  

        SELECT  
            'Error - Rollbacked -' AS CustomMessage,
            ERROR_NUMBER() AS ErrorNumber,
            ERROR_SEVERITY() AS ErrorSeverity,
            ERROR_STATE() AS ErrorState,
            ERROR_PROCEDURE() AS ErrorProcedure,
            ERROR_LINE() AS ErrorLine,
            ERROR_MESSAGE() AS ErrorMessage;  

        SET @outputMessage = 'error - ' + ERROR_MESSAGE();
    END CATCH   
END

This is the C# code which calls the stored procedure.

public string SaveAndPassToMailjetWindowsService(string listCode, string listName, long mailjetListId, MailjetListStatus mailjetListStatus, bool autoSync, List<Contact> contacts, int companyId, TblContactCompanyStatus tblContactCompanyStatus, EspListContactStatus espListContactStatus)
{
    try
    {
        string result;
        var conString = GetMailCoreConnectionString();

        using (var conn = new SqlConnection(conString))
        {
            var command = new SqlCommand("UpdateMailjetDetails", conn) 
                          { CommandType = CommandType.StoredProcedure };

            command.Parameters.Add(new SqlParameter("@listCode", listCode));
            command.Parameters.Add(new SqlParameter("@listName", listName));
            command.Parameters.Add(new SqlParameter("@mailjetListId", mailjetListId));
            command.Parameters.Add(new SqlParameter("@mailjetListStatus", (int) mailjetListStatus));
            command.Parameters.Add(new SqlParameter("@autoAsync", autoSync));

            var contactsParam =
                        new SqlParameter("@contacts", SqlDbType.Structured)
                        {
                            TypeName = "dbo.Contact",
                            Value = GetSqlDataRecordsContactsList(contacts)
                        };
            command.Parameters.Add(new SqlParameter("@contacts", contactsParam));

            command.Parameters.Add(new SqlParameter("@companyId", companyId));
            command.Parameters.Add(new SqlParameter("@tblContactCompanyStatus", (int) tblContactCompanyStatus));
            command.Parameters.Add(new SqlParameter("@espListContactStatus", (int) espListContactStatus));

            var outputParameter = new SqlParameter
                   {
                        ParameterName = "@outputMessage",
                        SqlDbType = SqlDbType.VarChar,
                        Direction = ParameterDirection.Output
                   };
            command.Parameters.Add(outputParameter);

            conn.Open();
            command.ExecuteNonQuery();      // throws exception
            result = outPutParameter.Value.ToString();
            conn.Close();
        }

        return result;
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        throw;
    }
}

And below is the C# method which converts the contacts list to a data table.

private List<SqlDataRecord> GetSqlDataRecordsContactsList(List<Contact> contacts)
{
    try
    {
        List<SqlDataRecord> datatable = new List<SqlDataRecord>();
        SqlMetaData[] sqlMetaData = new SqlMetaData[5];
        sqlMetaData[0] = new SqlMetaData("Email", SqlDbType.NVarChar, 512);
        sqlMetaData[1] = new SqlMetaData("Name", SqlDbType.NVarChar, 512);
        sqlMetaData[2] = new SqlMetaData("TblContactId", SqlDbType.BigInt);
        sqlMetaData[3] = new SqlMetaData("CompanyId", SqlDbType.Int);
        sqlMetaData[4] = new SqlMetaData("TblContactCompanyId", SqlDbType.BigInt);

        foreach (var contact in contacts)
        {
            SqlDataRecord row = new SqlDataRecord(sqlMetaData);
            row.SetValues(contact.Email, contact.Name, contact.TblContactId ?? (object) DBNull.Value,
                        contact.CompanyId ?? (object) DBNull.Value,
                        contact.TblContactCompanyId ?? (object) DBNull.Value);
            datatable.Add(row);
        }

        return datatable;
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        throw;
    }
}

Now, when I run the code it does all the logic in stored procedure properly and completes all the changes I'm trying to do. I can see that data has been added via the stored procedure properly, but SaveAndPassToMailjetWindowsService methods

command.ExecuteNonQuery();      

throws an exception in the C# side (even though the stored procedure executed its logic properly).

No mapping exists from object type System.Data.SqlClient.SqlParameter to a known managed provider native type.

Any guidance to fix this is much appreciated. Thanks.

like image 234
BUDDHIKA Avatar asked Jul 26 '18 18:07

BUDDHIKA


People also ask

How do you pass a list of objects to a stored procedure in SQL Server?

In order to use table-valued parameters we need to complete three steps: Create a User-Defined Table Type. Create the stored procedure where we are going to use the table-valued parameter and use the recently created User-Defined Table Type. Configure ADO.NET to send the parameter as a special parameter.

How can I return multiple values from a stored procedure in SQL Server?

In order to fetch the multiple returned values from the Stored Procedure, you need to make use of a variable with data type and size same as the Output parameter and pass it as Output parameter using OUTPUT keyword. You can also make use of the Split function to split the comma separated (delimited) values into rows.

Can a stored procedure return an output value to its caller?

If you specify the output keyword for a parameter in the procedure definition, the procedure can return the current value of the parameter to the calling program when the procedure exits.


1 Answers

Here you are adding the parameter as a value for a parameter:

var contactsParam =
                    new SqlParameter("@contacts", SqlDbType.Structured)
                    {
                        TypeName = "dbo.Contact",
                        Value = GetSqlDataRecordsContactsList(contacts)
                    };

command.Parameters.Add(new SqlParameter("@contacts", contactsParam)); //Problem is here

It should just be:

 command.Parameters.Add(contactsParam);
like image 195
SBFrancies Avatar answered Oct 22 '22 16:10

SBFrancies