[英]Can I use varbinary type to store image in SQL Server database?
[英]Can I use a hidden DataGridView column to manage a SQL Server VarBinary field?
我有一個表,我想在一個字段中存儲一個字節數組。 字節數組大約是20個字節(160位)的密鑰數據。
我正在使用幾個DataGridView
來管理該表和該應用程序的其他表。 我目前有兩個例程,可以讓我提供一個SQL選擇字符串,而DataGridView
允許用戶編輯數據。
private void InitializeUsersDataGrid()
{
string sql = "SELECT UserId, Enabled, AccessLevel, Name, KeyValue FROM Users";
DataGridViewIntialize(dgvUsers, sql);
dgvUsers.Columns[fdKeyValue].Visible = false;
}
private void DataGridViewIntialize(DataGridView dataGridView, string sql)
{
dataGridViewInUse = dataGridView; // This is the current DataGridView
OleDbConnection oleDbConnection = new OleDbConnection(txtConnectionString.Text);
dataAdapter = new OleDbDataAdapter(sql, oleDbConnection);
OleDbCommandBuilder commandBuilder = new OleDbCommandBuilder(dataAdapter); //Creates SQL commands for IUD
dataTable = new DataTable();
dataTable.Locale = System.Globalization.CultureInfo.InvariantCulture;
dataAdapter.Fill(dataTable);
bindingSource1.DataSource = dataTable;
dataGridView.DataSource = bindingSource1;
// Resize the DataGridView columns to fit the newly loaded content.
dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
AddDeleteRefreshContextMenu(dataGridView);
}
我有一個保存按鈕,提供驗證,然后調用TableSave
。
private void TableSave()
{
// Update the database with the user's changes.
try {
dataAdapter.Update((DataTable)bindingSource1.DataSource);
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
當我將varbinary
字段添加到表中時,它在DataGridView
為損壞的位圖圖像。 這與我無關,因為無論如何我都不會顯示它。
當我嘗試為調用分配一個新的字節數組時,它將生成一個DataGridView默認錯誤對話框。
/// <summary>
/// Change the password hash for the selected login
/// </summary>
/// <param name="e"></param>
void ChangePassword( DataGridViewCellEventArgs e)
{
// Request a new password with confirmation
fPassword form = new fPassword();
form.Confirm = true;
DialogResult dr = form.ShowDialog();
if (dr == DialogResult.OK) {
// Combine the UserID with the password to generate a new key
byte[] salt;
byte[] key;
string p = dgvUsers.Rows[e.RowIndex].Cells["UserId"].Value + form.Value;
Data.PBK2DF2Hash.GenerateSaltAndKey(p, out salt, out key);
if (dgvUsers.Rows[e.RowIndex].Cells[fdKeyValue].ValueType == key.GetType()) {
// We do get this far Error occurs on the assignment in the next line
dgvUsers.Rows[e.RowIndex].Cells[fdKeyValue].Value = key;
}
}
}
DataGridView默認錯誤對話框
DataGridView中發生以下異常:System.ArgumentException:參數無效。
在System.Windows.Forms.Formatter.FormatObjectInternal(對象值,在System.Drawing.Image.FromStream(流流,布爾值useEmbeddedColorManagement,布爾值validateImageData)在System.Windows.Forms.Formatter。在System.Windows.Forms.Formatter.FormatObject(對象值,類型targetType,TypeConverter sourceConverter,TypeConverter targetConverter,字符串formatString,IFormatProvider formatInfo,對象formattedNullValue, System.Windows.Forms.DataGridViewCell.GetFormattedValue(Object value,Int32 rowIndex,DataGridViewCellStyle&cellStyle,TypeConverter valueTypeConverter,TypeConverter formattedValueTypeConverter,DataGridViewDataErrorContexts上下文的對象dataSourceNullValue)要替換此默認對話框,請處理DataError事件。
看來這與CellFormatting
但我不知道如何將其關閉。
添加以下代碼后,將出現一個更簡單的對話框,顯示以下錯誤消息:
System.FormatException:單元格的格式化值具有錯誤的類型。
private void dgvUsers_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
DataGridViewColumn c = dgvUsers.Columns[fnKeyValue];
if (c != null) {
if (e.ColumnIndex == c.Index) {
if (e.Value != null) {
e.FormattingApplied = true;
}
else
e.FormattingApplied = false;
}
}
}
更新#1-使用Ivan Stoev建議的方法的其他代碼,並顯示了我用來請求密碼更改的按鈕單元。 它還增加了保存鹽值。
此時的問題是,當我按下“更改密碼”按鈕時,可以打開密碼對話框,獲取密碼。 生成鹽和鍵,將其保存到該行鹽和鍵值的datagrid單元中。
如果我執行TableSave()
(請參閱前面的代碼),則除非我在網格中的另一行上單擊鼠標,否則這些字段不在SQL Server表中。 該操作似乎表明該行是臟的,然后保存將起作用。
在更改密碼之前或之后,我還可以更改用戶可見的字段之一,該行將保存。
private void InitializeUsersDataGrid()
{
string sql = "SELECT UserId, Enabled, AccessLevel, Name, Salt, KeyValue FROM Users";
AddAccessLevelColumn(dgvUsers); //Add column if needed
AddPasswordChangeColumn(dgvUsers);
DataGridViewIntialize1(dgvUsers, sql);
dgvUsers.Columns[UsersFieldName.fdAccessLevel].Visible = false; //Hide the raw column from the database
MoveAccessLevelColumn(dgvUsers, AccessLevelComboBoxColumn, AccessLevelColumnPosition);
MoveAccessLevelColumn(dgvUsers, PasswordButtonColumn, PasswordColumnPosition);
}
private void DataGridViewIntialize1(DataGridView dataGridView, string sql)
{
dataGridViewInUse = dataGridView; // This is the current DataGridView
OleDbConnection oleDbConnection = new OleDbConnection(txtConnectionString.Text);
dataAdapter = new OleDbDataAdapter(sql, oleDbConnection);
OleDbCommandBuilder commandBuilder = new OleDbCommandBuilder(dataAdapter); //Creates SQL commands for IUD
dataTable = new DataTable();
dataTable.Locale = System.Globalization.CultureInfo.InvariantCulture;
dataAdapter.Fill(dataTable);
dataTable.Columns[Data.UsersFieldName.fdSalt].ColumnMapping = MappingType.Hidden;
dataTable.Columns[Data.UsersFieldName.fdKey].ColumnMapping = MappingType.Hidden;
bindingSource1.DataSource = dataTable;
dataGridView.DataSource = bindingSource1;
// Resize the DataGridView columns to fit the newly loaded content.
dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
AddDeleteRefreshContextMenu(dataGridView);
}
private void AddPasswordChangeColumn(DataGridView dataGridView)
{
// This column will remain unless manually removed, so it only needs to be added
// the first time the DVG is initialized.
if (dataGridView.Columns[PasswordButtonColumn] == null) {
// creating new ComboBoxCell Column
DataGridViewButtonColumn btnColumn = new DataGridViewButtonColumn();
btnColumn.Text = "Change Password";
btnColumn.HeaderText = "Change Password";
btnColumn.UseColumnTextForButtonValue = true;
btnColumn.Name = PasswordButtonColumn;
btnColumn.FlatStyle = FlatStyle.Popup;
dataGridView.Columns.Insert(0, btnColumn);
// Add a CellClick handler to handle clicks in the button column.
dataGridView.CellClick += dataGridView_CellClick;
}
}
private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
// Ignore clicks that are not on button cells.
if (e.RowIndex >= 0 && e.RowIndex < dgvUsers.RowCount - 1 && e.ColumnIndex == dgvUsers.Columns[PasswordButtonColumn].Index) {
ChangePassword(e);
}
}
/// <summary>
/// Change the password hash for the selected login
/// </summary>
/// <param name="e"></param>
private void ChangePassword(DataGridViewCellEventArgs e)
{
// Request a new password with confirmation
fPassword form = new fPassword();
form.Confirm = true;
DialogResult dr = form.ShowDialog();
if (dr == DialogResult.OK) {
// Combine the UserID with the password to generate a new key
byte[] salt;
byte[] key;
string p = dgvUsers.Rows[e.RowIndex].Cells[Data.UsersFieldName.fdUserId].Value + form.Value;
Data.PBK2DF2Hash.GenerateSaltAndKey(p, out salt, out key);
var gridRow = dgvUsers.Rows[e.RowIndex];
var dataRow = (DataRowView)gridRow.DataBoundItem;
var value = dataRow[Data.UsersFieldName.fdKey];
dataRow[Data.UsersFieldName.fdSalt] = salt; //This assignment now works
dataRow[Data.UsersFieldName.fdKey] = key;
}
}
更新#2將'NotifyCurrectCell'臟添加到'ChangePassword'方法中,通知網格需要保存單元格而不更改UI中的行。
private void ChangePassword(DataGridViewCellEventArgs e)
{
// Request a new password with confirmation
fPassword form = new fPassword();
form.Confirm = true;
DialogResult dr = form.ShowDialog();
if (dr == DialogResult.OK) {
// Combine the UserID with the password to generate a new key
byte[] salt;
byte[] key;
string p = dgvUsers.Rows[e.RowIndex].Cells[Data.UsersFieldName.fdUserId].Value + form.Value;
Data.PBK2DF2Hash.GenerateSaltAndKey(p, out salt, out key);
var gridRow = dgvUsers.Rows[e.RowIndex];
var dataRow = (DataRowView)gridRow.DataBoundItem;
var value = dataRow[Data.UsersFieldName.fdKey];
dataRow[Data.UsersFieldName.fdSalt] = salt; //This assignment now works
dataRow[Data.UsersFieldName.fdKey] = key;
dgvUsers.NotifyCurrentCellDirty(true); //Tells the UI that this row has changed and needs updating
}
}
您遇到的問題是因為DataGridView
在默認情況下為byte[]
數據類型創建了DataGridViewImageColumn 。
這里有一些選擇。
答:將DataGridView.AutoGenerateColumns屬性設置為false
並手動創建網格列。
B.如果您確實不需要網格中的該列,則不要嘗試隱藏網格列,而根本不要創建網格列。 但是如何實現呢? 對於類屬性,可以使用Browsable(false)
,但這是DataTable
。 好吧,盡管沒有記錄, MappingType.Hidden
出於相同的目的,可以將DataColumn.ColumnMapping屬性與MappingType.Hidden
一起使用。
您的情況下,請刪除此行
dgvUsers.Columns[fdKeyValue].Visible = false;
並在DataGridViewIntialize
方法內部使用
// ...
dataAdapter.Fill(dataTable);
dataTable.Columns[fdKeyValue].ColumnMapping = MappingType.Hidden;
bindingSource1.DataSource = dataTable;
// ...
您仍然可以使用這樣的基礎數據源訪問列數據
var gridRow = dgvUsers.Rows[...];
var dataRow = (DataRowView)gridRow.DataBoundItem;
var value = dataRow[fdKeyValue];
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.