2009-05-13 16 views
18

Tengo un problema al pasar nulos a un método ExecuteCommand() usando linq. Mi código es similar a la que sigue:Linq ExecuteCommand no entiende nulos

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()); 
    } 

Pero, cuando un parámetro es nulo (dirección, por ejemplo), me sale el siguiente error: "Un parámetro de consulta no puede ser de tipo 'System.Object' "

El error no ocurre si no hay parámetros nulos. Sé que el diseño en mi ejemplo es un poco pobre, acabo de crear un ejemplo simplificado para enfocarme en el problema. ¿Alguna sugerencia?

Respuesta

17

Se trata de un error conocido y Microsoft no tiene intención de solucionarlo ...

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

La solución es o bien:

  1. gota en ADO.NET y ejecutar la Comando SQL directamente
  2. Formatee la cadena que está ejecutando usted mismo y llame a ExecuteCommand con una matriz de objetos vacía (nuevo objeto [0])

La segunda no es una buena idea, ya que te abre a SQL inyectar ataques, pero es un truco rápido.

+2

7 años después. Nada ha cambiado. ¡Arghhhh Microsoft! – dzendras

+1

Esto es extraño, como [MSDN dice explícitamente] (https://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executecommand (v = vs.110) .aspx) que maneja null –

0

¿ha intentado asignar un valor a aquellos que son nulos? Significado (pseudo):

Si la dirección es nulo, address = "" o Si la edad es < 0, entonces la edad = 0

luego añadirlo a Mi lista

o usted podría utilizar siempre un ternario operador:

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

después añadirlo al miLista

+0

¿Qué pasa si tengo como 10 campos, o algo por el estilo. Debo validarlos a todos. Además, mi base de datos acepta valores nulos, y quiero guardarlos como nulos. No veo el uso de algún otro valor arbitrario para reemplazar el nulo como una buena práctica –

+0

Por lo general solo valido los que podrían ser NULL, por lo que sí, es posible que deba hacerlos todos. Estoy de acuerdo contigo, si de hecho quieres preservar el NULL, entonces no querrás mi enfoque. Además, no lo llamaría un valor arbitrario porque realmente es solo el valor mínimo que puede tener el objeto y no un valor aleatorio sin sentido. – northpole

+0

Lo que estoy tratando de decir es. Incluso si tomamos una cadena vacía (o Int32.MinValue) y la usamos, hay otras aplicaciones que guardan esto como nulo (el cliente no dijo su edad, por lo que se guardará como nulo, considerando que cada campo es nulo) . Entonces, cuando explique mi DB a otra persona, diré. Ah, si el cliente no proporciona su edad, será nulo o Int32.MinValue, depende de qué aplicación lo guardó (o lo mismo para su dirección, que podría estar vacía o nula). Además, quien lea este valor tendrá una parte si (edad == nulo || edad == Int32.MinValue) :( –

-1

por qué no utilizar los valores anulables?

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()); 
} 
+2

No funciona cuando son nulos –

+0

ah, por supuesto, la cadena es un tipo de referencia. . Vaya – tim

+0

supongo ur pegado con los métodos de extensión de cadenas y enteros no puede ser nulo Vaya @ prev void main (string [] args) {estáticas prueba (null); }.. public static prueba de cuerda (probador de cuerda) { comprobador de retorno.ToDefaultedString(); }} public static class Prueba { pública cadena ToDefaultedString estática (esta Cadena de test2) { si (test2! = Null) { test2.ToString retorno(); } else { return string.Empty; } } } – tim

-6

En .NET, una cadena nula/nada no evalúa a una cadena vacía, es decir "". Si quiere "", entonces ese tiene que ser el valor de la cadena, o si quiere representar nulo/nada en SQL, tiene que escribir manualmente "NULL" si su cadena .NET es en realidad nula/nada.

Todo el comando execute lo hace, es ejecutar una consulta SQL, proporcionada por usted, como una cadena. no hace nada especial en términos de esa cadena de SQL.

Por lo tanto, para que el comando Ejecutar funcione, debe pasar una cadena de SQL válida, debe construir la cadena manualmente.

+0

por qué se marcó esto. sobre todo porque la resolución coincide con la respuesta aceptada ?? no golpees y corras! – andy

+2

Porque la explicación de la respuesta no tiene sentido y no tiene nada que ver con la causa real del problema. Busque la documentación de ExecuteCommand; hace un tric mágico con parámetros de consulta llamado parametrización. – ErikHeemskerk

0

Mismo problema para mí. Tan estúpido de MS no para arreglar eso. Aquí está mi solución, aunque no admití todos los tipos de parámetros pero ya entiendo la idea. Puse esto en la clase DataContext para que parezca que está integrado en 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

utilizo algo como esto (tenga en cuenta que estoy usando el SO "IDE" así que no puedo, garantizar esto compilará o trabajar correctamente, pero usted consigue la idea)

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

Hice una función ParamArray genérica para pasar los parms que normalmente pasaría a ExecuteCommand. A continuación, haga que sea pasar de nuevo a los parms SQL uninterpretted y una lista de objetos que se le pasan en.

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

normalmente utilizo este tipo de cosas, no es ideal pero es lo consigue hecho si estás atascado

  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); 
         } 

        } 
       } 
      } 

Esto asegura que cada valor en el objeto tenga un valor antes de usar los parámetros en ExecuteCommand. De nuevo, no es ideal, pero funciona.

0

No me gustó usar string.format ya que (como dice la respuesta seleccionada actual a esta pregunta) te estás abriendo a la inyección de SQL.

Así que resolví el problema iterando a través de los parámetros y si el parámetro es nulo, agrego NULL como una cadena al texto del comando, si no es nulo, agrego un marcador de posición que será reemplazado (similar a la cadena .format) con valores por ExecuteQuery (que hace las comprobaciones de inyección de 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(); 
    } 
0
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

¿Esto no dará como resultado el uso de la cadena "NULL" en lugar del valor NULL? –

0

Kevin is right.

un ejemplo de su trabajo alrededor del n. ° 1 en LinqPad. Necesitará esta (Objeto) 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(); 
Cuestiones relacionadas