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 ".
Ошибка не возникает, если ни один параметр не равен нулю. Я знаю, что дизайн в моем примере немного беден, я просто создал упрощенный пример, чтобы сосредоточиться на проблеме. Любые предложения?
Ответы
Ответ 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, вы должны вручную построить строку правильно.