Подтвердить что ты не робот

Linq ExecuteCommand не понимает нули

У меня возникла проблема при передаче нулей в метод ExecuteCommand() с использованием linq. Мой код похож на следующий:

    public void InsertCostumer(string name, int age, string address)
    {
        List<object> myList = new List<object>();

        myList.Add(name);
        myList.Add(age);
        myList.Add(address);

        StringBuilder queryInsert = new StringBuilder();
        queryInsert.Append("insert into Customers(name, address) values ({0}, {1}, {2})");

        this.myDataContext.ExecuteCommand(queryInsert.ToString(), myList.ToArray());
    }

Но, когда параметр имеет значение null (адрес, например), я получаю следующую ошибку: "Параметр запроса не может быть типа" System.Object ".

Ошибка не возникает, если ни один параметр не равен нулю. Я знаю, что дизайн в моем примере немного беден, я просто создал упрощенный пример, чтобы сосредоточиться на проблеме. Любые предложения?

4b9b3361

Ответ 1

Это известная ошибка, и Microsoft не намерена ее исправлять...

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=305114&wa=wsignin1.0

Работа вокруг заключается в следующем:

  • Перейдите в ADO.NET и выполните команду SQL непосредственно
  • Отформатируйте строку, которую вы выполняете, и вызовите ExecuteCommand с пустым массивом объектов (новый объект [0])

Вторая - это не очень хорошая идея, так как она открывает вам SQL-атаки, но это быстрый хак.

Ответ 2

Вы пытались присвоить значение тем, которые являются нулевыми? Значение (псевдо):

Если адрес равен null, тогда address = "" или Если возраст равен < 0, тогда возраст = 0

затем добавьте его в myList

или вы всегда можете использовать оператор Ternary:

name = name.Length < 1 ? "" : name;
age = age < 1 ? Int32.MinValue : age;

затем добавьте его в myList

Ответ 3

Такая же проблема для меня. Так глупо MS, чтобы не исправить это. Здесь мое решение, хотя я не поддерживал все типы параметров, но вы получите эту идею. Я придерживался этого в классе DataContext, поэтому он выглядит так, как будто он встроен в Linq:).

    public int ExecuteCommandEx(string sCommand, params object[] parameters)
    {
        object[] newParams = new object[parameters.Length];

        for (int i = 0; i < parameters.Length; i++)
        {
            if (parameters[i] == null)
                newParams[i] = "NULL";
            else if (parameters[i] is System.Guid || parameters[i] is System.String || parameters[i] is System.DateTime)
                newParams[i] = string.Format("'{0}'", parameters[i]);
            else if (parameters[i] is System.Int32 || parameters[i] is System.Int16)
                newParams[i] = string.Format("{0}", parameters[i]);
            else
            {
                string sNotSupportedMsg = string.Format("Type of param {0} not currently supported.", parameters[i].GetType());
                System.Diagnostics.Debug.Assert(false, sNotSupportedMsg);
            }
        }

        return ExecuteCommand(string.Format(sCommand, newParams));
    }

Ответ 4

Я использую что-то вроде этого (заметьте, что я использую SO "IDE", поэтому я не могу, гарантирую, что это будет компилироваться или работать правильно, но вы получите идею)

    public void InsertCostumer(string name, int age, string address)
    {
        List<object> myList = new List<object>();

        myList.Add(name);
        myList.Add(age);
        myList.Add(address);

        StringBuilder queryInsert = new StringBuilder();
        queryInsert.Append("insert into Customers(name, age, address) values (");
        int i = 0;
        foreach (var param in myList.ToArray())
        {
            if (param == null)
            {
                queryInsert.Append("null, ");
                myList.RemoveAt(i);
            }
            else
            {
                queryInsert.Append("{" + i + "}, ");
                i++;
            }
        }

        queryInsert.Remove(queryInsert.Length - 2, 2);
        queryInsert.Append(")");

        this.myDataContext.ExecuteCommand(queryInsert.ToString(), myList.ToArray());
    }

Ответ 5

Я сделал универсальную функцию ParamArray для передачи в частях, которые я обычно передавал в ExecuteCommand. Затем передайте обратно неинтерпретированные SQL-пармы и список объектов, которые действительно переданы.

Public Sub CommitRecords(ByVal InItems As List(Of Item)
    Dim db As New DataContext(ConnectionString)
    Try
        For Each oItem In InItems 
            With oItem
                Dim strParms As String = ""
                Dim collParms = BuildExecuteCommandParms(strParms, .MapValue1, .MapValue2, .MapValue3, .MapValue4, .MapValue5, .MapValue6)

                db.ExecuteCommand("Insert Into ItemTable (Value1, Value2, Value3, Value4, Value5, Value6)" & vbCrLf & _
                                    "Values (" & strParms & ")", _
                                    collParms.ToArray)
            End With
        Next

    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try
End Sub

Public Function BuildExecuteCommandParms(ByRef strParms As String, ByVal ParamArray InParms As Object()) As List(Of Object)
    Dim i As Integer = 0
    Dim collOutParms As New List(Of Object)
    For Each oParm In InParms
        If i <> 0 Then strParms &= ", "
        If oParm Is Nothing Then
            strParms &= "NULL"
        Else
            strParms &= "{" & i & "}"
            collOutParms.Add(oParm)
        End If
        i += 1
    Next
    Return collOutParms
End Function

Ответ 6

Я обычно использую такие вещи, а не идеалы, но это делается, если вы застряли

            if (myObject != null)
            {
                foreach (var p in ct.GetType().GetProperties())
                {
                    if (p.GetValue(myObject , null) == null)
                    {
                        if (p.PropertyType == typeof(string))
                        {
                            p.SetValue(myObject , "Empty", null);
                        }
                        if (p.PropertyType == typeof(int))
                        {
                            p.SetValue(myObject , 0, null);
                        }
                        if (p.PropertyType == typeof(int?))
                        {
                            p.SetValue(myObject , 0, null);
                        }

                    }
                }
            }

Это гарантирует, что каждое значение в объекте имеет значение перед использованием параметров в ExecuteCommand. Опять же, не идеальный, но он работает.

Ответ 7

Мне не понравилось использование string.format, так как (как говорит текущий выбранный ответ на этот вопрос) вы открываете SQL-инъекцию.

Итак, я решил проблему, выполнив итерацию по параметрам, и если параметр равен null, я добавляю NULL в качестве строки в текст команды, если это не null, я добавляю замещающий элемент, который будет заменен (аналогично строке. формат) со значениями ExecuteQuery (который выполняет проверки SQL-запросов).

    private static T ExecuteSingle<T>(string connectionString, string sprocName, params object[] sprocParameters)
        where T : class
    {
        var commandText = sprocName;
        if (sprocParameters.Length > 0)
        {
            // http://stackoverflow.com/questions/859985/linq-executecommand-doesnt-understand-nulls
            int counter = 0;
            var nulledPlaceholders = sprocParameters
                .Select(x => x == null ? "NULL" : "{" + counter ++ + "}");

            commandText += " " + string.Join(",", nulledPlaceholders);
            sprocParameters = sprocParameters.Where(x => x != null).ToArray();
        }
        var connection = new SqlConnection(connectionString);
        var dc = new DataContext(connection);
        return dc.ExecuteQuery<T>(commandText, sprocParameters).SingleOrDefault();
    }

Ответ 8

internal static class DataContextExtensions
{
    public static int ExecuteCommandEx(this DataContext context, string command, params object[] parameters)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        if (parameters != null && parameters.Length > 0)
            parameters = parameters.Select(p => p ?? "NULL").ToArray();
        return context.ExecuteCommand(command, parameters);
    }
}

Ответ 9

Кевин прав.

пример его работы вокруг №1 в LinqPad. Вам нужен этот (Object) s DBNull.Value

string s = null; 

//ExecuteCommand("insert into T(C1) values({0})", s); //Exception

SqlCommand cmd= new SqlCommand(){
    CommandText = "insert into T(C1) values(@P0)",
    Connection = new SqlConnection(this.Connection.ConnectionString),
};
//cmd.Parameters.AddWithValue("@P0", s); //SqlException
cmd.Parameters.AddWithValue("@P0", (Object)s??DBNull.Value);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();

Ts.OrderByDescending(t=>t.ID).Take(1).Dump();

Ответ 10

почему бы не использовать значения с нулевым значением?

public void InsertCostumer(string? name, int? age, string? address)
{
    List<object> myList = new List<object>();

    myList.Add(name.GetValueOrDefault());
    myList.Add(age.GetValueOrDefault());
    myList.Add(address.GetValueOrDefault());

    StringBuilder queryInsert = new StringBuilder();
    queryInsert.Append("insert into Customers(name, address) values ({0}, {1}, {2})");

    this.myDataContext.ExecuteCommand(queryInsert.ToString(), myList.ToArray());
}

Ответ 11

В .NET строка null/nothing не оценивает пустую строку, т.е. ". Если вы хотите" ", то это должно быть значение строки, или если вы хотите представлять null/nothing в SQL, вы должны вручную выписать" NULL", если ваша строка .NET на самом деле равна null/nothing.

Вся команда выполнения выполняет SQL-запрос, предоставляемый вами как String. он не делает ничего особенного с точки зрения этой строки SQL.

Итак, чтобы команда Execute работала, вам нужно передать правильную строку SQL, вы должны вручную построить строку правильно.