3

I saw many questions on stack overflow, non of them touched my own problem

procedure or function expects parameter which was not supplied

I created this SQL Server stored procedure:

CREATE proc [dbo].[spAddCustomer]
    @cuName varchar(50),
    @cuAddress varchar(50),
    @cuMobile varchar(50),
    @cuImage image,
    @cityId int,
    @exist int output
AS
BEGIN
    IF NOT EXISTS(SELECT Cu_Mobile FROM tblCustomers WHERE Cu_Mobile = @cuMobile)
    BEGIN
        INSERT INTO tblCustomers (Cu_Name, Cu_Address, Cu_Mobile, Cu_Image, City_ID)
        VALUES (@cuName, @cuAddress, @cuMobile, @cuImage, @cityId)

        SET @exist = 1
    END
    ELSE
        SET @exist = 0
END

In my Data Access Layer I have this method that is responsible for non-query commands:

public static int ExecuteNonQuery(string query, CommandType type, params SqlParameter[] arr)
{
    int outParam;

    SqlCommand cmd = new SqlCommand(query, cn);
    cmd.Parameters.AddRange(arr);
    cmd.CommandType = type;

    int i = cmd.ExecuteNonQuery();

    foreach (SqlParameter param in arr)
    {
        if (param.Direction == ParameterDirection.Output)
        {
            outParam = (int)cmd.Parameters[param.ToString()].Value;
            return outParam;
        }
    }

    return i;
}

The method responsible for creating parameters:

public static SqlParameter CreateParameter(string name, SqlDbType type, object value, ParameterDirection pd = ParameterDirection.Input)
{
    SqlParameter pr = new SqlParameter();
    pr.ParameterName = name;
    pr.Direction = pd;
    pr.SqlDbType = type;
    pr.SqlValue = value;

    return pr;
}

And this method in the Business Layer, pass the arguments from the Presentation Layer

public static int spAddCustomer(string cusName, string cusAddress, string cusMobile, byte[] arrImg, int cityId)
{
    DataAccessLayer.Open();

    int i = DataAccessLayer.ExecuteNonQuery("spAddCustomer", CommandType.StoredProcedure,
        DataAccessLayer.CreateParameter("@cuName", SqlDbType.VarChar, cusName),
        DataAccessLayer.CreateParameter("@cuAddress", SqlDbType.VarChar, cusAddress),
        DataAccessLayer.CreateParameter("@cuMobile", SqlDbType.VarChar, cusMobile),
        DataAccessLayer.CreateParameter("@cuImage", SqlDbType.Image, arrImg),
        DataAccessLayer.CreateParameter("@cityId", SqlDbType.Int, cityId),
        DataAccessLayer.CreateParameter("@exist", SqlDbType.Int, null, ParameterDirection.Output));

    DataAccessLayer.Close();

    return i;
}

When the user Click add a new record is inserted into the table (tblCustomers)

private void btnAU_Click(object sender, EventArgs e)
{
    byte[] imgArr;

    if (PbCustomer.Image == null)
           imgArr = null;
    else
    {
        MemoryStream ms = new MemoryStream();
        PbCustomer.Image.Save(ms, PbCustomer.Image.RawFormat);
        imgArr = ms.ToArray();
    }

    int cityId = int.Parse(cmbCities.SelectedValue.ToString());

    try
    {
        int exist = CustomerClass.spAddCustomer(txtName.Text, txtAddress.Text, txtMobile.Text, imgArr, cityId);

        if (exist == 1)
        {
            MessageBox.Show("A new customer has been saved");
            txtAddress.Text = txtMobile.Text = txtName.Text = "";
            PbCustomer.Image = null;
        }
        else
            MessageBox.Show("A customer with the same mobile number\nalready exists, add new number!");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

But when I click the Add button (passing null image), I get this error:

procedure or function 'spAddCustomer' expects parameter '@cuImage' which was not supplied

Despite the table tblCustomers accept null values

10
  • Have you stepped through the code in the debugger to see what values are actually being passed in? And can you post the code for CreateParameter? Commented Aug 18, 2016 at 17:44
  • 1
    Note that an empty TextBox has a value of String.Empty, not null! Do things get better is you convert "" to null? Commented Aug 18, 2016 at 17:52
  • 1
    better yet do something like this if the TextBox.Text is = to string.Empty DataAccessLayer.CreateParameter("@cuName", SqlDbType.VarChar).Value = string.IsNullOrEmpty(cusName) ? DBNULL.Value : cusName; Commented Aug 18, 2016 at 17:56
  • 2
    ntext, text, and image data types will be removed in a future version of SQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. Use nvarchar(max), varchar(max), and varbinary(max) instead. See details here Commented Aug 18, 2016 at 18:07
  • 1
    Thanks @marc_s, I know about text but I didn't know about image Commented Aug 18, 2016 at 18:10

2 Answers 2

24

I just found that I can set default values for the parameter in the stored procedure:

ALTER proc [dbo].[spAddCustomer]
@cuName varchar(50)=null,
@cuAddress varchar(50)=null,
@cuMobile varchar(50)= null,
@cuImage image= null,
@cityId int= null,
@exist int output

And this solved my problem! This is helpful specifically with null images from the PictureBox, since I have a helper method that checks for empty strings.

Sign up to request clarification or add additional context in comments.

Comments

8

You need to check your input for null and use DBNull.Value when you creating the parameters. If you pass just null as a parameter - ADO.Net will ignore that.

EDIT:

You can add that check into your custom method DataAccessLayer.CreateParameter()

2 Comments

DataAccessLayer.CreateParameter("@cuName", SqlDbType.VarChar, String.IsNullOrWhitespace(cusName) ? DBNull.Value : cusName),
I would rather fix DataAccessLayer.CreateParameter() method itself to check for null/whitespace parameter instead of using ternary operator ?: everytime.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.