C#和SQL Server Express以及DataGridView在插入表后更新
我不熟悉SQL Server数据库,我在Winforms应用程序中有一个简单的datagridview。datagridview绑定到SQL Server数据库 我需要在按下按钮时更新datagridview和后端数据库C#和SQL Server Express以及DataGridView在插入表后更新,c#,sql-server,winforms,datagridview,C#,Sql Server,Winforms,Datagridview,我不熟悉SQL Server数据库,我在Winforms应用程序中有一个简单的datagridview。datagridview绑定到SQL Server数据库 我需要在按下按钮时更新datagridview和后端数据库 private void btnAdd_Click(object sender, EventArgs e) { var str = txtNewSource.Text; var data = this.tBL_SourceTableAdapter.GetDa
private void btnAdd_Click(object sender, EventArgs e)
{
var str = txtNewSource.Text;
var data = this.tBL_SourceTableAdapter.GetData();
var length = data.Rows.Count;
this.tBL_SourceTableAdapter.Insert(length + 1, str);
sourceDataGridView.refresh(); // This does not refresh the data in the form!
}
我想用刚刚添加到数据库中的新数据更新绑定的datagridview。我可以看到数据被添加到数据库中。如果使用datagridview关闭窗口并重新打开,则新添加的值可见
插入绑定数据库表后,如何刷新datagridview中的数据
我对其他能达到预期效果的方法持开放态度。我不允许用户编辑datagridview中的值,因为不是表中的所有列都对用户可见,而且我需要保持索引的正确顺序
我尝试过的事情:
我试图在gridview中添加新行,如下所示:
sourceDataGridView.rows.add(); // I get a runtime error cannot add row to bound datagrid.
sourceDataGridView.rows[n].cells[0].value = str;
我试图在datagrid上重置数据源,但也没有成功
谢谢。您不能用这种方式刷新网格视图。您必须清除现有数据网格中的行,并将其与数据源重新绑定您不能以这种方式刷新网格视图。您必须清除现有数据网格中的行,并将其与数据源重新绑定将数据与其显示方式分开 在现代编程中,有一种倾向是将数据(模型)与向操作员(视图)显示数据的方式分开 这使您可以自由更改显示,而无需更改模型。例如,如果您希望显示较少的列,或者希望以不同的颜色显示负数,或者希望在Excel工作表中显示负数 类似地,您可以更改模型而不必更改视图:如果您希望从CSV文件或Json而不是数据库获取数据,或者甚至可能从internet获取数据:您不希望更改基于此数据创建的视图 通常,您需要一个适配器使模型适合显示器。此适配器通常称为ViewModel。这三个项目一起缩写为MVVM。考虑阅读一些关于这方面的背景信息。 分离模型和视图后,您将拥有以下方法:
- 从存储库获取数据。存储库是您的数据库,但它可以是任何内容:CSV?Json、XML、internet,或者只是用于单元测试的字典
- 将数据写入存储库
- 显示数据
- 获取编辑的数据
class Product
{
public int Id {get; set;}
public string ProductCode {get; set;}
public string Name {get; set;}
public string Description {get; set;}
public decimal Price {get; set;}
public int StockCount {get; set;}
}
interface IRepository
{
IEnumerable<Product> FetchProductsToDisplay(...);
void UpdateProducts(IEnumerable<Product> product);
}
class Repository : IRepository
{
// TODO: implement
}
确切的SQL文本因您使用的每个数据库管理系统而异。例如,SQLight
使用Limit 30
而不是TOP 30
幸运的是,您将模型与视图分离,并将这些详细信息隐藏在存储库类中,因此,如果您决定使用其他方法访问数据库,则存储库之外的内容不会发生任何更改
您可能还需要一个左外部联接、GroupBy、Where、Order等。确切的SQL有点超出了问题的范围
重要的是要记住,使用运算符或其他外部源可能提供的输入值更改SQL字符串是非常危险的。如果你从未听说过,请阅读
始终将SQL设置为常量字符串。使用变量插入运算符输入
例如,如果您只想在特定仓库位置显示产品:
const string sqlTextFetchProducts = @"SELECT ... FROM Products;";
+ @" WHERE WareHouseLocationId = @WareHouseLocationId"
好的,让我们实现FetchProductsToDisplay:
private string DbConnectionString => ...; // gets the database connection string
IEnumerable<Product> FetchProductsToDisplay(int wareHouseLocationId);
{
const string sqlTextFetchProducts = @"...;";
using (var dbConnection = new SQLiteConnection(this.DbConnectionString))
{
using (var dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = sqlTextFetchProducts ;
dbCommand.Parameters.AddWithValue("@WareHouseLocationId", wareHouseLocationId);
dbConnection.Open();
// Execute the query and returns products one by one
using (SQLiteDataReader dbReader = dbCommand.ExecuteReader())
{
while (dbReader.Read())
{
Product fetchedProduct = new Product
{
Id = dbReader.GetInt64(0),
ProductCode = dbReader.GetString(1),
...
Price = dbReader.GetDecimal(4),
StockCount = dbReader.GetInt32(5),
};
yield return fetchedProduct;
}
}
}
}
}
为此创建一个特殊的SQL可能更有效,但是对于这个示例,您可以看到,您不需要将所有获取的数据转换为产品
使用参数
SQL文本是常量。参数有一个前缀@
,用于将其与文字文本区分开来。这仅仅是惯例,您可以更改它,但这样可以很容易地发现参数
参数值将逐个添加,例如,如果您只希望产品位于WareHouseLocation 10,库存数量至少为2,最高价格为25欧元,则可以更改SQL,使其包含@WareHouseLocation、@StockCount、@price
,然后添加:
IEnumerable<Product> FetchProductsToDisplay(
int wareHouseLocationId,
int minimumStockCount,
decimal maximumPrice)
{
using(...)
...
dbCommand.Parameters.AddWithValue("@WareHouseLocationId", wareHouseLocationId);
dbCommand.Parameters.AddWithValue("@StockCount", minimumStockCount);
dbCommand.Parameters.AddWithValue("@Price", maximumPrice);
...
只要存在未读的回迁数据,则返回true
Id = dbReader.GetInt64(0),
ProductCode = dbReader.GetString(1),
...
SQL文本中提取的项选择Id、ProductCode。。。从…
有索引,Id有索引0,ProductCode有索引1,等等。使用适当的dbReader。获取…
将获取的项目转换为适当的类型
将dbReader中获取的数据转换为类的确切方法可能因数据库管理系统而异,但我想您会明白要点
当然,您还需要一种方法来更新产品。这如果相当相似,,
IEnumerable<Product> FetchProductsToDisplay(
int wareHouseLocationId,
int minimumStockCount,
decimal maximumPrice)
{
using(...)
...
dbCommand.Parameters.AddWithValue("@WareHouseLocationId", wareHouseLocationId);
dbCommand.Parameters.AddWithValue("@StockCount", minimumStockCount);
dbCommand.Parameters.AddWithValue("@Price", maximumPrice);
...
while (dbReader.Read())
Id = dbReader.GetInt64(0),
ProductCode = dbReader.GetString(1),
...
public void UpdateProductPrice(int productId, decimal price)
{
const string sqlText = "UPDATE " + tableNameProducts
+ " SET Price = @Price"
+ " WHERE Id = @Id;";
using (SQLiteCommand dbCommand = this.DbConnection.CreateCommand())
{
dbCommand.CommandText = sqlText;
dbCommand.Parameters.AddWithValue("@Id", productId);
dbCommand.Parameters.AddWithValue("@Price", productPrice);
dbCommand.ExecuteNonQuery();
}
}
public MyForm()
{
InitializeComponent();
this.columnProductId.DataPropertyName = nameof(Product.Id);
this.columnProductName.DataPropertyName = nameof(Product.Name);
...
this.columnProductPrice.DataPropertyName = nameof(Product.Price);
}
private BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>) this.dataGridViewProducts.DataSource,
set => this.dataGridViewProducts.DataSource = value;
}
private IRepository Repository {get;} = new Repository();
private IEnumerable<Product> FetchProductsToDisplay()
{
return this.Repository.FetchProductsToDisplay(...);
}
public void InitProductDisplay()
{
this.DisplayedProducts = new BindingList<Product>(
this.FetchProductsToDisplay().ToList());
}
private void OnButtonApplyNow_Clicked(object sender, ...)
{
Collection<Product> editedProducts = this.Displayedproducts();
// find out which Products are changed and save them in the repository
this.ProcessEditedProducts(editedProducts);
}