2013-11-26 31 views
1

我新手在SQLITE和我目前使用System.Data.SQLite.dll版本在C#项目1.0.89.0。 我的数据库包含一个简单的表'文件'与下列:检索与System.Data.SQLite一个BLOB和C#

  • [ID] VARCHAR(50)NOT NULL
  • [块] INTEGER NOT NULL
  • [内容] BLOB NOT NULL
  • [大小] INTEGER NOT NULL
  • [date_ins] DATETIME NOT NULL

PRIMARY KEY(ID,块)

我创建了一个类(OfflineStorage)本表为BLOBS添加和检索文件。 保存方法效果不错,但负载GetStream扩展方法产生InvalidCastException的

public class OfflineStorage 
{ 

    private static string l_strConnectionTemplate = "Data Source={0};Version=3;Password=\"{1}\";"; 

    private string l_strConnection; 
    private int SQLITE_MAX_BLOB_LENGTH; 

    private string l_strCreateTable = @"CREATE TABLE IF NOT EXISTS [files]" + 
             "(" + 
             "  [id] VARCHAR(50) NOT NULL, " + 
             "  [chunk] INTEGER NOT NULL, " + 
             "  [content] BLOB NOT NULL, " + 
             "  [size] INTEGER NOT NULL, " + 
             "  [date_ins] DATETIME NOT NULL, " + 
             "  PRIMARY KEY(id,chunk) " + 
             ")"; 

    private string l_strSelectQuery = @"SELECT chunk, content, size FROM files WHERE id = @id ORDER BY chunk"; 
    private string l_strUpdateQuery = @"UPDATE files SET content = content || @new_content, size = size + @size WHERE id = @id AND chunk = @chunk"; 
    private string l_strInsertQuery = @"INSERT INTO files(id, chunk, content, size, date_ins) VALUES(@id, @chunk, @new_content, @size, DATETIME('now'))"; 

    public OfflineStorage(string strFilename, string strPassword = "") 
    { 
     SQLiteConnection l_objConnection = null; 
     if (!File.Exists(strFilename)) 
     { 
      l_strConnection = string.Format(l_strConnectionTemplate, strFilename, ""); 
      SQLiteConnection.CreateFile(strFilename); 
      l_objConnection = new SQLiteConnection(l_strConnection); 
      l_objConnection.SetPassword(strPassword); 
      l_objConnection.Close(); 
     } 

     l_strConnection = string.Format(l_strConnectionTemplate, strFilename, strPassword); 
     l_objConnection = getConnection(); 
     using (SQLiteCommand l_objCommand = new SQLiteCommand(l_strCreateTable, l_objConnection)) 
     { 
      l_objCommand.ExecuteNonQuery(); 
     } 
     SQLITE_MAX_BLOB_LENGTH = 1000000; 

     CloseConnection(l_objConnection); 
    } 

    private SQLiteConnection getConnection() 
    { 
     SQLiteConnection l_objConnection = null; 

     try 
     { 
      l_objConnection = new SQLiteConnection(l_strConnection); 
      l_objConnection.Open(); 
      return l_objConnection; 
     } 
     catch (Exception ex) 
     { 
      CloseConnection(l_objConnection); 

      throw new OfflineStorageException("Local Service open db error.", ex); 
     } 
    } 

    private void CloseConnection(SQLiteConnection objConnection) 
    { 
     if (objConnection != null) 
     { 
      objConnection.Close(); 
      objConnection = null; 
     } 
    } 

    public long Load(string strID, Stream objStream) 
    { 
     if (!objStream.CanWrite) 
      throw new NotSupportedException("Stream not writable."); 

     SQLiteConnection l_objConnection = getConnection(); 

     // Columns Identifier (name of file) 
     SQLiteParameter l_objID = new SQLiteParameter("@id", DbType.String); 
     l_objID.Value = strID; 

     SQLiteCommand l_objCommand = new SQLiteCommand(l_strSelectQuery, l_objConnection); 
     l_objCommand.Parameters.Add(l_objID); 

     // Load File Records 
     SQLiteDataReader l_objReader; 
     try 
     { 
      l_objReader = l_objCommand.ExecuteReader(); 
     } 
     catch (Exception ex) 
     { 
      CloseConnection(l_objConnection); 
      throw new OfflineStorageException("SQLite exception.", ex); 
     } 

     long l_lFileLength = 0;  // Complete file length 
     int l_iDBChunk = -1;  // Current chunk on database 
     int l_iChunk = 0;   // Current 'sub chunk' 
     long l_lChunkLength = -1; // Current 'sub chunk' length 

     try 
     { 
      // For each record of current file selected by identifier 
      while (l_objReader.Read()) 
      { 
       l_iDBChunk = l_objReader.GetInt32(0);  // Chunk ID 
       l_lChunkLength = l_objReader.GetInt64(2); // Chunk size 
       Trace.Assert(l_iChunk == l_iDBChunk);  // Compare expected Chunck with Database ID Chunk 
       l_lFileLength += l_objReader.GetStream(objStream, 1, l_lChunkLength); // Load chunk 
       l_iChunk++; 
      } 
     } 
     catch (Exception ex) 
     { 
      string l_strMessage = string.Format("SQLite exception on file {0}, DB chunk {1}: \n{2}", strID, l_iDBChunk, ex.Message); 
      throw new OfflineStorageException(l_strMessage, ex); 
     } 
     finally 
     { 
      l_objReader.Close(); 
      l_objCommand.Dispose(); 
      CloseConnection(l_objConnection); 
     } 

     if (l_iChunk < 1) 
     { 
      string l_strMessage = string.Format("File {0} not readed on db.", strID); 
      throw new OfflineStorageException(l_strMessage); 
     } 

     return l_lFileLength; 
    } 

    public void Save(string strID, Stream objStream, bool bOverwrite = false) 
    { 
     const int CHUNK_SIZE = 8 * 1024; 

     if (!objStream.CanRead) 
      throw new NotSupportedException("Stream not readable."); 
     long l_lOldPosition = objStream.Position; 

     SQLiteConnection l_objConnection = getConnection(); 
     byte[] lar_byBuffer = new byte[CHUNK_SIZE]; 

     SQLiteParameter l_objID = new SQLiteParameter("@id", DbType.String); 
     l_objID.Value = strID; 
     SQLiteParameter l_objContent = new SQLiteParameter("@new_content", DbType.Binary); 
     l_objContent.Value = lar_byBuffer; 
     SQLiteParameter l_objChunk = new SQLiteParameter("@chunk", DbType.Int32); 
     SQLiteParameter l_objSize = new SQLiteParameter("@size", DbType.Int32); 

     SQLiteCommand l_objCommand = new SQLiteCommand(l_strInsertQuery, l_objConnection); 
     l_objCommand.Parameters.Add(l_objID); 
     l_objCommand.Parameters.Add(l_objContent); 
     l_objCommand.Parameters.Add(l_objChunk); 
     l_objCommand.Parameters.Add(l_objSize); 

     int l_iReturn, l_lBytesRead; 
     int l_iChunk = 0;   // Current 'sub chunk' 
     int l_iDBChunk = 0;  // Current chunk on database 
     long l_lDBChunkLength = 0; // Current length of chunk 
     l_objChunk.Value = l_iDBChunk; 

     //Transaction 
     using (SQLiteTransaction l_objTransaction = l_objConnection.BeginTransaction()) 
     { 
      // Read File from stream 
      while ((l_lBytesRead = objStream.Read(lar_byBuffer, 0, lar_byBuffer.Length)) > 0) 
      { 
       // Check for next Chunk 
       if ((l_lDBChunkLength + l_lBytesRead) >= SQLITE_MAX_BLOB_LENGTH) 
       { 
        l_objCommand.CommandText = l_strInsertQuery; 
        l_iChunk = 0;  // reset 'sub chunk' counter 
        l_lDBChunkLength = 0; // reset chunk size 
        l_iDBChunk++;  // increase chunk ID 
        l_objChunk.Value = l_iDBChunk; 
       } 

       l_lDBChunkLength += l_lBytesRead; // Increase length of chunk 
       l_objContent.Size = l_lBytesRead; // Length of Content field 
       l_objSize.Value = l_lBytesRead;  // Chunk lenght (write on 'size' column) 

       #region WRITE 
       try 
       { 
        l_iReturn = l_objCommand.ExecuteNonQuery(); 
        if (l_iChunk == 0) 
        { 
         l_objCommand.CommandText = l_strUpdateQuery; 
        } 
       } 
       catch (Exception ex) 
       { 
        l_objTransaction.Rollback(); 
        CloseConnection(l_objConnection); 
        string l_strMessage = string.Format("SQLite exception on file {0}, DB chunk {1}, chunk {2}: \n{3}", strID, l_iDBChunk, l_iChunk, ex.Message); 
        throw new OfflineStorageException(l_strMessage, ex); 
       } 

       if (l_iReturn != 1) 
       { 
        l_objTransaction.Rollback(); 
        CloseConnection(l_objConnection); 
        string l_strMessage = string.Format("DB chunk {1}, chunk {2} of file {0} not inserted on db.", strID, l_iDBChunk, l_iChunk); 
        throw new OfflineStorageException(l_strMessage); 
       } 
       #endregion WRITE 

       l_iChunk++; 
      } 

      l_objTransaction.Commit(); 
     } 
     l_objCommand.Dispose(); 
     CloseConnection(l_objConnection); 
     objStream.Position = l_lOldPosition; 
    } 
} 

DB数据读取器扩展类:

public static class DbDataReaderExtension 
{ 
    public static long GetStream(this DbDataReader objReader, System.IO.Stream objStream, int iIndex = 0, long lFileLength = -1) 
    { 
     const int CHUNK_SIZE = 7 * 1024; 

     byte[] lar_byBuffer = new byte[CHUNK_SIZE]; // Buffer 
     long l_lBytesRead;   // Bytes readed from SQLite database column 
     long l_lFieldOffset = 0; // Field offset on database column 
     long l_lBytesRemainig = lFileLength; 

     while ((l_lBytesRead = objReader.GetBytes(iIndex, l_lFieldOffset, lar_byBuffer, 0, lar_byBuffer.Length)) > 0) 
     { 
      l_lFieldOffset += l_lBytesRead; // prepare next offset 

      if (l_lBytesRemainig > 0)  // check if a FileLength was set 
      { 
       l_lBytesRemainig -= l_lBytesRead; // remove readed bytes 
       if (l_lBytesRemainig < 0) // Cut last bytes not valid if file is bigger than column size 
        l_lBytesRead += l_lBytesRemainig; 
      } 

      // write only valid bytes 
      objStream.Write(lar_byBuffer, 0, (int)l_lBytesRead); 
     } 

     return lFileLength < 0 ? l_lFieldOffset : lFileLength; 
    } 
} 

我发现产生这种异常,因为的ReadBytes方法(SQLiteDataReader)调用VerifyType

private TypeAffinity VerifyType(int i, DbType typ) 
{ 
    CheckClosed(); 
    CheckValidRow(); 

    TypeAffinity affinity = GetSQLiteType(i).Affinity; 

    switch (affinity) 
    { 
    ...   
    case TypeAffinity.Text: 
     if (typ == DbType.SByte) return affinity; 
     if (typ == DbType.String) return affinity; 
     if (typ == DbType.SByte) return affinity; 
     if (typ == DbType.Guid) return affinity; 
     if (typ == DbType.DateTime) return affinity; 
     if (typ == DbType.Decimal) return affinity; 
     break; 
    case TypeAffinity.Blob: 
     ... 
    } 

    throw new InvalidCastException(); 
} 

在这个函数并不期望是典型等于DbType.Binary and affinity等于TypeAffinity.Text
任何人都可以帮助我理解这个问题吗?
谢谢

回答

1

好的,这里是我如何添加和检索blob。

我想这一切都取决于字节[]是否是一个可管理的大小。这适用于序列化到数据库的小对象。

使用GetDataTable来获取数据,然后在该行中问题的提取字节数组有以下:

public byte[] getByteArray(DataRow row, int offset) 
    { 
     object blob = row[offset]; 
     if (blob == null) return null; 
     byte[] arData = (byte[])blob; 
     return arData; 
    } 

这是我怎么添加它们:

private System.Object syncLock = new System.Object(); 

    public int ExecuteNonQueryWithBlob(string sql, string blobFieldName, byte[] blob) 
    { 
     lock (syncLock) 
     { 
      try 
      { 
       using (var c = new SQLiteConnection(dbConnection)) 
       { 
        using (var cmd = new SQLiteCommand(sql, c)) 
        { 
         cmd.Connection.Open(); 
         cmd.Parameters.AddWithValue("@" + blobFieldName, blob); 
         return cmd.ExecuteNonQuery(); 
        } 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
       return 0; 
      } 
     } 
    } 

    public DataTable GetDataTable(string sql) 
    { 
     lock (syncLock) 
     { 
      try 
      { 
       DataTable dt = new DataTable(); 
       using (var c = new SQLiteConnection(dbConnection)) 
       { 
        c.Open(); 
        using (SQLiteCommand cmd = new SQLiteCommand(sql, c)) 
        { 
         using (SQLiteDataReader rdr = cmd.ExecuteReader()) 
         { 
          dt.Load(rdr); 
          return dt; 
         } 
        } 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
       return null; 
      } 
     } 
    } 
+0

我试了一下你的建议使用DataTable,它工作正常!谢谢! – plotty83