2009-05-13 15 views
18

Ich habe ein Problem beim Übergeben von Nullen an eine ExecuteCommand() -Methode mit linq. Mein Code ist ähnlich dem, der folgt:Linq ExecuteCommand versteht Nullwerte nicht

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

Aber, wenn ein Parameter null ist (Adresse, zum Beispiel), erhalte ich folgende Fehlermeldung: „Eine Abfrage-Parameter nicht vom Typ sein kann‚System.Object‘ . "

Der Fehler tritt nicht auf, wenn keine Parameter null sind. Ich weiß, dass das Design in meinem Beispiel etwas schlecht ist. Ich habe ein vereinfachtes Beispiel erstellt, um mich auf das Problem zu konzentrieren. Irgendwelche Vorschläge?

Antwort

17

Dies ist ein bekannter Fehler und Microsoft hat nicht die Absicht, es zu beheben ...

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

Die Arbeit um ist entweder:

  1. Tropfen in ADO.NET und führen die SQL-Befehl direkt
  2. Formatieren Sie die Zeichenfolge, die Sie selbst ausführen, und rufen Sie ExecuteCommand mit einem leeren Objektarray auf (neues Objekt [0])

Die zweite ist keine gute Idee, da es Sie SQL inject Angriffe öffnet, aber es ist ein schneller Hack.

+2

7 Jahre später. Nichts hat sich geändert. Arghhhh Microsoft! – dzendras

+1

Dies ist seltsam, wie [MSDN explizit sagt] (https://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executecommand (v = vs.110) .aspx), dass es behandelt Nullen –

0

haben Sie versucht, den Nullwerten einen Wert zuzuweisen? Bedeutung (pseudo):

Wenn Adresse null ist dann adressieren = "" oder Wenn Alter < 0 dann age = 0

dann fügen Sie

zu myList oder man konnte immer eine Ternary verwenden Betreiber:

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

dann fügen sie es zu myList

+0

Was passiert, wenn ich wie 10 Felder haben, oder so ähnlich. Muss ich sie alle validieren. Außerdem akzeptiert meine Datenbank Nullwerte, und ich möchte sie als null speichern. Ich sehe keinen anderen willkürlichen Wert, um Null als eine gute Praxis zu ersetzen –

+0

Ich validiere normalerweise nur diejenigen, die NULL sein könnten, also ja, Sie müssen sie möglicherweise alle tun. Ich stimme Ihnen zu, wenn Sie tatsächlich den NULL erhalten wollen, dann würden Sie meinen Ansatz nicht wollen. Außerdem würde ich es keinen willkürlichen Wert nennen, weil es wirklich nur der minimale Wert ist, den das Objekt haben kann, und nicht irgendein zufälliger bedeutungsloser Wert. – northpole

+0

Was ich sagen will ist. Auch wenn wir eine leere Zeichenkette (oder Int32.MinValue) verwenden und sie verwenden, gibt es andere Anwendungen, die das als null speichern (der Kunde hat sein Alter nicht angegeben, daher wird es als Null gespeichert, wenn man bedenkt, dass jedes Feld nullfähig ist) . Also, wenn ich meine DB jemand anderem erklären, werde ich sagen. Oh, wenn der Kunde sein Alter nicht angibt, ist es null oder Int32.MinValue, es hängt davon ab, welche Anwendung es gespeichert hat (oder das gleiche für seine Adresse, die leer oder null sein könnte). Wer auch diesen Wert liest, wird einen Teil haben, wenn (Alter == null || Alter == Int32.MinValue) :( –

-1

warum nicht auf NULL festlegbare Werte verwenden?

+2

Es funktioniert nicht, wenn sie null sind –

+0

ah natürlich - Zeichenfolge ist ein Referenztyp. . oops – tim

+0

ich denke, ur stecken mit Erweiterungsmethoden für Zeichenfolgen und ints kann nicht null sein Oops @ zurück static void Main (string [] args) { Test (null);} .. public static Stringtest (Stringtester) { Rückführtester.ToDefaultedString(); } } public static class Test { public static string ToDefaultedString (dieser String test2) { if (test2! = Null) { return test2.ToString(); } sonst { Rückgabe String.Empty; } } } – tim

-6

In .NET wird eine null/nothing Zeichenfolge nicht zu einer leeren Zeichenfolge, d. H. "" Ausgewertet. Wenn Sie "" wollen, dann muss das der Wert der Zeichenkette sein, oder wenn Sie null/nichts in SQL darstellen wollen, müssen Sie "NULL" manuell ausgeben, wenn Ihre .NET Zeichenkette tatsächlich null/nichts ist.

Der Befehl Ausführen führt eine von Ihnen bereitgestellte SQL-Abfrage als String aus. Es macht nichts besonderes in Bezug auf diese SQL-Zeichenfolge.

Damit der Execute-Befehl funktioniert, müssen Sie eine gültige SQL-Zeichenfolge übergeben, müssen Sie die Zeichenfolge manuell korrekt erstellen.

+0

Warum wurde dies markiert. vor allem, da die Auflösung der akzeptierten Antwort entspricht? nicht schlagen und rennen! – andy

+2

Weil die Erklärung der Antwort keinen Sinn ergibt und nichts mit der eigentlichen Ursache des Problems zu tun hat. Suchen Sie in der Dokumentation von ExecuteCommand nach; es macht ein magisches Tric mit Abfrageparametern, die Parametrisierung genannt werden. – ErikHeemskerk

0

Das gleiche Problem für mich. So dumm von MS, das nicht zu beheben. Hier ist meine Lösung, obwohl ich nicht alle Parametertypen unterstützt habe, aber Sie haben die Idee. Ich steckte dies in die DataContext-Klasse, so dass es aussieht wie in 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

ich so etwas wie diese verwenden (beachten Sie, ich bin mit der SO „IDE“, so kann ich nicht garantieren, dies richtig kompilieren oder arbeiten, aber Sie bekommen die Idee)

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

Ich habe eine generische ParamArray-Funktion erstellt, um die parms zu übergeben, die ich normalerweise in ExecuteCommand übergeben würde. Dann haben sie die uninterpretted SQL parms und eine Liste von Objekten passieren zurück tatsächlich übergeben.

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

ich in der Regel diese Art der Sache verwenden, ist nicht ideal, aber es ist wird es getan, wenn Sie stecken

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

        } 
       } 
      } 

Dies stellt sicher, dass jeder Wert im Objekt einen Wert hat, bevor Sie die Parameter in ExecuteCommand verwenden. Wiederum nicht ideal, aber es funktioniert.

0

Ich mochte nicht mit string.format seit (wie die aktuelle ausgewählte Antwort auf diese Frage sagt) Sie öffnen sich für SQL-Injektion.

So löste ich das Problem durch Iterieren durch die Parameter, und wenn der Parameter null ist, füge ich NULL als Zeichenfolge zum Befehlstext hinzu, wenn es nicht null ist, füge ich einen Platzhalter hinzu, der ersetzt wird (ähnlich wie Zeichenfolge) .format) mit Werten von ExecuteQuery (was die SQL-Injection prüft).

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

Wird dieses Ergebnis in der Zeichenfolge "NULL" nicht anstelle des Werts NULL verwendet? –

0

Kevin is right.

ein Beispiel für seine Arbeit rund um # 1 in LinqPad. Sie benötigen diese (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();