none
Umwandlung von byte/char nach varbinary RRS feed

  • Frage

  • Hallo,

    ich versuche gerade meine SQL 2005 Datenbank mit char/byte zu füllen.

    ICh benutze Visual Studio 2008 und C# und folgende Cast Umwandlung:

     

    "CAST("+ cSpeicher + "AS VARBINARY(MAX))" + "','" 
    

     

    Dabei bekomme ich folgende Fehlermeldung:

    Die implizite Konvertierung vom varchar-Datentyp in varbinary(max) ist nicht zulässig. Verwenden Sie die CONVERT-Funktion, um diese Abfrage auszuführen.

    In der Convert Funktion finde ich aber den Datentyp Binary nicht.

    Hat jemand eine Idee?

    LG

    Freitag, 17. Dezember 2010 07:36

Antworten

  • Hallo,

    ein aussagefähiger Codeschnipsel wäre an dieser Stelle hilfreich;
    insbeonsdere welchen Inhalt die Variable cSpeicher transportiert.

    Wenn Du auf diese Weise einen .NET System.String in eine SQL Anweisung
    "umbastelst", handelst Du Dir nur Probleme ein.

    Zum einen sind .NET Strings Unicode, was einem nvarchar (mit oder ohne MAX)
    beim SQL Server entspricht. Du hast Deine Zeichenkette aber nicht mit N' gekennzeichnet,
    so dass die Zeichenfolge als ANSI varchar behandelt wird - und der Hinweis im varchar
    in der Fehlermeldung rührt daher. Dies kann/wird zu einem Datenverlust führen.

    Zudem - auch ohne SQL Injection zu bemühen -, fällt der Code auf die Nase,
    wenn in cSpeicher ein Anführungsstrich enthalten ist.

    Grundsätzlich solltest Du deshalb einen SqlParameter verwenden.
    Und jenach dem welchen Inhalt cSpeicher enthält eine Konvertierung
    über die Encoding Klasse via GetBytes vornehmen.

    Ist die Herkunft cSpeicher eine Datei kannst Du das Beispiel aus
    Bilder in einer MS SQL Tabelle ablegen an Deine Verhältnisse anpassen.

    Ist die Herkunft und/oder Verarbeitung eine andere, erläutere Dein Ansinnen näher,
    so kann man Dir auch dabei weiterhelfen.

    Gruß Elmar

     

    • Als Antwort markiert sleepy007 Montag, 20. Dezember 2010 10:51
    Freitag, 17. Dezember 2010 08:24
    Beantworter

Alle Antworten

  • jetzt sieht das ganze so aus:

     "CONVERT (varbinary(max), "+ cSpeicher + " )" + "','" +<br/>
    <br/>
    mit:<br/>
    byte[] cSpeicher = new byte[200];<br/>
    <br/>
    
    

    es kommt aber die gleiche Fehlermeldung:

    Die implizite Konvertierung vom varchar-Datentyp in varbinary(max) ist nicht zulässig. Verwenden Sie die CONVERT-Funktion, um diese Abfrage auszuführen.

    Freitag, 17. Dezember 2010 08:24
  • Hallo,

    ein aussagefähiger Codeschnipsel wäre an dieser Stelle hilfreich;
    insbeonsdere welchen Inhalt die Variable cSpeicher transportiert.

    Wenn Du auf diese Weise einen .NET System.String in eine SQL Anweisung
    "umbastelst", handelst Du Dir nur Probleme ein.

    Zum einen sind .NET Strings Unicode, was einem nvarchar (mit oder ohne MAX)
    beim SQL Server entspricht. Du hast Deine Zeichenkette aber nicht mit N' gekennzeichnet,
    so dass die Zeichenfolge als ANSI varchar behandelt wird - und der Hinweis im varchar
    in der Fehlermeldung rührt daher. Dies kann/wird zu einem Datenverlust führen.

    Zudem - auch ohne SQL Injection zu bemühen -, fällt der Code auf die Nase,
    wenn in cSpeicher ein Anführungsstrich enthalten ist.

    Grundsätzlich solltest Du deshalb einen SqlParameter verwenden.
    Und jenach dem welchen Inhalt cSpeicher enthält eine Konvertierung
    über die Encoding Klasse via GetBytes vornehmen.

    Ist die Herkunft cSpeicher eine Datei kannst Du das Beispiel aus
    Bilder in einer MS SQL Tabelle ablegen an Deine Verhältnisse anpassen.

    Ist die Herkunft und/oder Verarbeitung eine andere, erläutere Dein Ansinnen näher,
    so kann man Dir auch dabei weiterhelfen.

    Gruß Elmar

     

    • Als Antwort markiert sleepy007 Montag, 20. Dezember 2010 10:51
    Freitag, 17. Dezember 2010 08:24
    Beantworter
  • OK,

    meine SQl Tabelle sieht ungefähr so aus:

    USE [Sensoren]
    GO
    /****** Objekt: Table [dbo].[Auftraege]  Skriptdatum: 12/17/2010 09:30:41 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    SET ANSI_PADDING ON
    GO
    CREATE TABLE [dbo].[Auftraege](
      [id] [int] IDENTITY(1,1) NOT NULL,
      [Mitarbeiter] [nvarchar](50) NOT NULL,
      [Datum] [smalldatetime] NOT NULL,
      [Sensor] [nvarchar](50) NOT NULL,
      [Auftrag] [nvarchar](50) NOT NULL,
      [Seriennummer] [nvarchar](50) NOT NULL,
      [Bemerkungen] [nvarchar](50) NOT NULL,
      [Speicherinhalt] [varbinary](max) NOT NULL,
      [Kal_Neu_Rep] [nchar](20) NOT NULL
    ) ON [PRIMARY]
    
    GO
    SET ANSI_PADDING OFF<br/>
    
    

    meine Abfrage:

          byte[] cSpeicher = new byte[200];
    
          for (int i = 0; i < 200; i++)
          {
            cSpeicher[i] = (byte)(i + 1);
          }
    
    
           SqlConnection Connection = new SqlConnection
          (
          "Data Source=XXX\\XXX;" +
          "Initial Catalog=Sensoren;" +
          "Integrated Security=True"
          );
    
          DateTime dt = new DateTime();
          dt = DateTime.Now;
    
          string scrc = Convert.ToString(crc_tab.ToString());
    
                // Sensor in dbo.Sensor anlegen
          SqlCommand scInsertSensor = new SqlCommand(
            "INSERT INTO Auftraege" +
            "(Mitarbeiter, "+
            "Datum, "+
            "Sensor, "+
            "Auftrag, "+
            "Seriennummer, "+
            "Bemerkungen, "+
            "Speicherinhalt, "+
            " Kal_Neu_Rep)" +    
            " VALUES ('" +
            "Mitarbeiter" + "','" +
            dt + "','" +
            "Sensor" + "','" +
            "Auftrag" + "','" +
            "Seriennummer" + "','" +
            "Bemerkung" + "','" +
            "CONVERT (varbinary(max), "+ cSpeicher + " )" + "','" +
            "Neuauftrag" +
            "')",
            Connection);
    
          Connection.Open();
          scInsertSensor.ExecuteNonQuery();
          Connection.Close(); 
    

    Was ich brauche ist eine Möglichkeit Daten, welche auch nullen enthalten können, in der Datenbank abzulegen.

    Da bin ich dann auf das Datenformat varbinary gestoßen.

    Freitag, 17. Dezember 2010 08:37
  • Hallo,

    wie vorhin schon geschrieben, ist das über Parameter kein Problem.
    Und wenn Du dsa gleiche für das Datum machst, vermeidest Du auch gleich ein weiteres Problem,
    siehe http://www.insidesql.org/blogs/frankkalis/2010/08/19/der-ultimative-guide-fuer-die-datetime-datentypen

    Verpacke am besten das Speichern in eine Klasse wie die folgende:

    using System;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ElmarBoye.Samples.Data
    {
      public class SqlSensor
      {
        public static void InsertSensorTest()
        {
          byte[] cSpeicher = new byte[200];
    
          for (int i = 0; i < 200; i++)
          {
            cSpeicher[i] = (byte)(i + 1);
          }
    
          try
          {
            int id = InsertSensor(cSpeicher);
            Console.WriteLine("Speicher ID: {0}", id);
          }
          catch (Exception ex)
          {
            Console.WriteLine("Fehler beim Speichern:\n{0}", ex);
          }
        }
    
        public static int InsertSensor(byte[] speicher)
        {
          using (var connection = new SqlConnection(Properties.Settings.Default.TempDbConnectionString))
          {
            connection.Open();
    
            SqlCommand scInsertSensor = new SqlCommand(
              "INSERT INTO dbo.Auftraege ("
              + "Mitarbeiter, "
              + "Datum, "
              + "Sensor, "
              + "Auftrag, "
              + "Seriennummer, "
              + "Bemerkungen, "
              + "Speicherinhalt, "
              + "Kal_Neu_Rep"
              + ") VALUES (\r\n"
              + "N'Mitarbeiter',"
              + "@Datum, "  // Datum als Parameter
              // Unicode Literale N'...'
              + "N'Sensor', N'Auftrag', N'Seriennummer', N'Bemerkung', "
              + "@Speicherinhalt, "  // Speicherinhalt als Parameter
              + "N'Neuauftrag');\r\n"
              // Abruf der vergebenen ID
              + "SELECT CAST(SCOPE_IDENTITY() AS int) AS ID;",
            connection);
    
            scInsertSensor.Parameters.Add("@Datum", SqlDbType.SmallDateTime).Value = DateTime.Now;
            scInsertSensor.Parameters.Add("@SpeicherInhalt", SqlDbType.VarBinary, -1).Value = speicher;
    
            // Rückgabe der ID
            var id = scInsertSensor.ExecuteScalar();
            return (int)id;
          }
        }
      }
    }
    
    

     

    Dabei wird der varbinary(max) Parameter durch SqlDbType mit einer Länge von -1 festgelegt.
    Willst Du weitere Spalten variabel machen, so führe weitere SqlParameter nach gleichem Muster ein.
    Mehr dazu findest Du unter Befehle und Parameter (ADO.NET)

    Ich habe einige Korrekturen, wie die Verwendung von Unicode Literalen vorgenommen.
    Die Verbindungszeichenfolge habe ich aus den Settings entnommen.

    Zudem liefert die Methode die vergebene ID aus der Auftraege Tabelle zurück,
    die Du im einen oder anderen Fall benötigen könntest.

    Gruß Elmar


    Freitag, 17. Dezember 2010 09:27
    Beantworter
  • Hallo Elmar,

     

    danke für die Hilfe, hat mir sehr weitergeholfen.

    Aber so wie es aussieht, lag es da drann das ich die  ' vergessen habe wegzumachen und deswegen die Fehlermeldung entstanden ist.

    Jetzt muß ich mir nur noch überlegen mit welcher Abfrage ich die binär Daten wieder auslese.

     

    LG

    Montag, 20. Dezember 2010 08:56
  • Hallo,

    da fehlte noch ein klein wenig mehr als die Anführungszeichen.
    So hättest Du Dein cSpeicher in eine Binärkonstante (0x01020304....) umwandeln müssen,
    was durch den SqlParameter entfällt.

    Auslesen kannst Du die Daten einfach via SELECT und erhälst dabei das Byte-Array zurück.
    Alternativ (bei größeren Mengen => MBytes) kann Du auch Stück für Stück via GetBytes
    arbeiten, siehe Beispiel im ersten Beitrag von mir, wo man anstatt des FileStreams auch
    einen MemoryStream einsetzen kann.

    Gruß Elmar

    Montag, 20. Dezember 2010 09:37
    Beantworter
  • So, es scheint zu gehen.

    Ich hab das reinschreiben auch nochmal ohne Parameter getestet.

    Das Problem war wohl, das ich keinen Parameter verwendet habe.

    Beim Lesen klappt der Zugriff mit GetBytes auch.

    Wenn ich es allerdings mit nem DataGridview und einem TableAdapter und einem DataSet mache, werden mir die Daten nicht angezeigt.

    Liegt dann wohl an den Binärdaten.

    Dann mache ich mal zu.

    LG

     

    Montag, 20. Dezember 2010 10:58
  • Hallo,

    was soll denn das DataGridView dabei anzeigen?
    Für eine Folge von beliebigen Bytes hat es keinerlei Unterstützung.

    Gruß Elmar

    Montag, 20. Dezember 2010 11:44
    Beantworter
  • Hallo Elmar,

    ja, so habe ich es mir schon gedacht, das ein hex Wert 0 nicht als String angezeigt werden kann.

    Ich helfe mir halt mit einem Breakpoint um die Daten vorerstmal zu testen.

    Eine Umwandlung von einer Hex Zahl  0x01   als 0 und 1 als string wird da bestimmt nicht vorgesehen sein.

    (Tabelleneintrag wäre dann: 01 02 03 04, um bei meinem vorigen Beispiel zu bleiben)

    Vor allem nicht bei einem Byte Array, das mir die einzelnen Bytes in eine DataGridview Tabelle übersetzt.

    Danke nochmals für die Hilfe.

    LG

     

     

    Montag, 20. Dezember 2010 12:22
  • Hallo,

    der Inhalt eines Byte-Array ist nunmal undefinierbar.
    Direkt als Zeichen sind nicht alle darstellbar, da der untere ASCII -Bereich für
    Steuercodes reserviert ist und ab 128 hängt es wieder jeweiligen Zeichensatz ab.

    Was Du in Visual Studio im Debug-Fensterchen siehst, ist bereits eine Konvertierung in eine hexadezimale Darstellung.
    Gleiches gilt, wenn Du Dir die Ausgabe einer Abfrage im SQL Server Management Studio anguckst.

    Brauchst Du eine hexadezimale Darstellung häufiger, kannst Du dir eine Erweiterungsmethode erstellen.
    Unter http://stackoverflow.com/questions/623104/c-byte-to-hex-string gibt es einige Vorlagen.

    Prinzpiell könnte man sich zwar auch eine spezielle Darstellung für das DataGridView bauen,
    aber einem Normalanwender sagen solche Zeichen wenig (und bearbeiten sollte man Daten
    auf dem Wege schon gar nicht). Das sollte man sich nur als Entwickler vorbehalten.

    Gruß Elmar

    Montag, 20. Dezember 2010 17:15
    Beantworter