我是 SQLITE 中的新手,我目前正在 System.Data.SQLite.dll 版 1.0.89.0 中< strong> C#项目。 我的数据库包含一个简单的表'files',其中包含以下列:
PRIMARY KEY (id,chunk)
我创建了一个类( 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 Data Reader扩展类:
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();
}
在此函数中,typ不等于 DbType.Binary 且 affinity 等于 TypeAffinity.Text 即可。
任何人都可以帮我理解这个问题吗?
谢谢
答案 0 :(得分:1)
好的,这是我添加和检索blob的方式。
我想这一切都取决于byte []是否是可管理的大小。这适用于序列化到数据库的小对象。
使用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;
}
}
}