Linq ExecuteCommand не понимает нулей - PullRequest
22 голосов
/ 13 мая 2009

У меня проблема при передаче нулей в метод 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 ».

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

Ответы [ 11 ]

19 голосов
/ 31 июля 2009

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

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

Обходной путь должен либо:

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

Второй вариант - не самая лучшая идея, поскольку он открывает для вас атаки с использованием SQL-инъекций, но это быстрый взлом.

1 голос
/ 20 апреля 2016

Кевин прав .

пример его работы вокруг # 1 в LinqPad. Нужно это (Объект) 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();
0 голосов
/ 27 июня 2015
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);
    }
}
0 голосов
/ 30 декабря 2014

Мне не понравилось использование 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)
        {
            // /837180/linq-executecommand-ne-ponimaet-nulei
            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();
    }
0 голосов
/ 18 февраля 2014

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

            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. Опять не идеально, но работает.

0 голосов
/ 13 июня 2013

Я сделал универсальную функцию 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
0 голосов
/ 05 марта 2013

Я использую что-то вроде этого (обратите внимание, что я использую 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());
    }
0 голосов
/ 16 мая 2011

Та же проблема для меня. Так глупо 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));
    }
0 голосов
/ 13 июля 2009

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

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

Таким образом, чтобы команда «Выполнить» работала, вам нужно передать допустимую строку SQL, вам нужно вручную правильно ее создать.

0 голосов
/ 13 июля 2009

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

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());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...