如何在 DataGridView 中的现有项目中添加数量?
How to add quantity in existing item in DataGridView?
我有一个表单要求用户输入 3 条信息。这些输入是 ProductName、Quantity 和 Price。这 3 个输入将进入数据库,并以单独的形式显示在数据网格视图中。我希望如果我要添加一个项目,它只会与数据库中的项目合并,如果它们具有相同的 input/data。例如,如果我将在我的数据库中添加价格为 50 和数量为 3 的产品“plates”,而我的数据库中已经有具有相同价格的产品“plates”,它将只是合并或者数量将是添加而不是添加新单元格。
这是 Add 按钮的代码:
double presyo, qty;
presyo = double.Parse(txtPrice.Text);
qty = double.Parse(txtQuantity.Text);
con = new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=CasaFranca;Integrated Security=True");
con.Open();
cmd = new SqlCommand("INSERT INTO Sales (ProductName, Quantity, Price, Total) VALUES (@ProductName, @Quantity, @Price, @Total)", con);
cmd.Parameters.Add("@ProductName", cmbProduct.Text);
cmd.Parameters.Add("@Quantity", txtQuantity.Text);
cmd.Parameters.Add("@Price", txtPrice.Text);
cmd.Parameters.Add("@Total", (presyo * qty));
cmd.ExecuteNonQuery();
MessageBox.Show("Item sucessfully added to database");
这是一个示例输出:
如您所见,即使它们具有相同的产品名称和价格,它们也是分开存储的。我希望将它们合并为一个并添加它们的数量。我该怎么办?
嗯..你已经以 lowest-level/hardest/most 乏味的方式进行了基本的数据库访问,所以我假设你会想以这种方式继续,但我认为这会让如果您考虑切换到一些旨在进行数据访问(数据集、EF)的库,生活会更轻松,但至少您应该调查 Dapper..
运行 一个更新,如果它产生 0 行,运行 一个插入:
con.Open();
var ins = "INSERT INTO Sales (ProductName, Quantity, Price, Total) VALUES (@ProductName, @Quantity, @Price, @Total)";
var upd = "UPDATE Sales SET Quantity = Quantity + @Quantity, Price = Price + @Price, Total = Total + @Total WHERE ProductName = @ProductName";
cmd = new SqlCommand(upd, con);
cmd.Parameters.Add("@ProductName", cmbProduct.Text);
cmd.Parameters.Add("@Quantity", txtQuantity.Text);
cmd.Parameters.Add("@Price", txtPrice.Text);
cmd.Parameters.Add("@Total", (presyo * qty));
if(cmd.ExecuteNonQuery() == 0){
cmd.CommandText = ins;
cmd.ExecuteNonQuery();
}
请注意,这不是很安全;如果两个人在同一时间进行销售,您仍然可以获得重复的行。考虑改为放置主键,在查询中使用它
此外,没有必要存储总数;你可以随时计算它。我还要说的是,除非这是针对保证商品价格相同的单个购物篮,否则如果它是销售历史记录日志,存储多行可能是有意义的,否则您会丢失每次销售价格等信息更改或折扣,如果你所做的只是存储总计
您的问题与DataGridView 无关。由于我看到您显示的 table 已正确更新,因此我认为您已经掌握了显示数据的技巧。
所以让我们专注于您的 SQL。
隐藏访问数据的方式:实施存储库
尝试说服您的项目负责人,如果您可以将界面切换到 Entity Framework 或 Dapper,您的工作效率会更高。
即使你不能说服他,你也需要一个 class 代表你的一行 table,以及一个存储库模式来添加/获取/更新/删除项目table.
像这样:
class Sale
{
public Id {get; set; }
public string ProductName {get; set;}
public int Quantity {get; set;}
public decimal Price {get; set;}
public decimal Total {get; set;}
}
存储库 class 是一个 class,它隐藏了您存储销售的方式。它可以在数据库中,但据您所知,它可以在 XML 文件中,或者在 Internet 上的某个地方。或者也许在内部它只是一个用于单元测试的字典。
所以存储库是一个代表一些存储的对象:您可以在其中存储一些 classes 的项目,稍后您可以检索存储的项目,即使在重新启动计算机后也是如此。
隐藏项目存储方式的好处在于,您可以自由更改内部结构,而无需更改使用存储库的代码。 (还有你的单元测试!)。
可能稍后您决定更改数据库,使其具有第一个规范化形式:一个单独的 table 包含产品信息,并且在您的销售中有一个指向产品的外键 table , 因此您可以从产品 table 中获取名称,而不是每次销售都一遍又一遍地使用相同的产品名称。
通过隐藏项目在存储库中的存储方式,存储库的用户将不知道,也不必知道销售实际上存储在单独的 table 中。
典型的存储库class 将实现一个接口。在您的情况下,它将具有如下方法:
interface ISaleRepository
{
int Add(Sale sale); // return the primary key
Sale Fetch(int id); // fetch by primary key
Update(Sale sale);
Remove(int id);
int Count(); // returns number of Sales;
IEnumerable<Sale> FetchAll();
}
我不知道你的销售模式,但如果你认为 ProductName 是唯一的,请考虑添加:
Sale Fetch(string productName); // fetch by unique productName
回到你的问题
您想要“添加或更新”:如果销售已经在数据库中,则只更新数量:
Sale AddOrUpdate(Sale sale); // returns the updated Sale
如果您对更新后的特卖不感兴趣,请考虑return作废。
您的 AddOrUpdate 可以重用其他 ISaleRepository 方法:
public Sale AddOrUpdate(Sale sale)
{
// Is there already a Sale?
Sale existingSale = this.Fetch(sale.ProductName);
if (existingSale == null)
{
// no such Sale yet. Add it now:
this.Add(sale); // don't forget to update the Id! Usually done in Add
existingSale = sale;
}
else
{
// Sale exists. Update
existingSale.Quantity += sale.Quantity;
this.Update(existingSale);
}
return existingSale; // return the updated value as it is in the database
}
此解决方案需要两次数据库访问。我假设您不会每秒数百次 AddOrUpdate 项目。在那种情况下,代码的可重用性和简单性会与性能损失进行权衡。
如果真的要看表演,有些SQL方言让你一口气更新
因为我用的是entity framework,所以我的SQL有点生疏,但是this solution gets you on track
const string sqlText = @"
begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
update table set ...
where key = @key
end
else
begin
insert into table (key, ...) values (@key, ...)
end
commit tran";
string dbConnectionString = FetchDbConnectionString();
using (var dbConnection = new dbConnection(dbConnectionString))
{
using (var dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = sqlText;
// add parameters:
dbCommand.Parameters.AddWithValue(@ProductName, sale.ProductName);
dbCommand.Parameters.AddWithValue(@Quantity, sale.Quantity);
...
// Open the database and execute the query:
dbConnection.Open();
...
}
}
不获取数据的正常添加将是:
dbCommand.ExecuteNonQuery();
如果你return只有添加项目的Id:
return (int)dbCommand.ExecuteScalar();
如果您正在return更新数据:
using (var dbReader = dbcommand.ExecuteReader())
{
// expect only one item:
if (dbReader.MoveNext())
{
return new Sale
{
ProductName = dbReader.GetString(0),
Quantity = dbReader.GetInt32(1),
...
};
}
// else: nothing returned. Decide what to do: exception?
}
我有一个表单要求用户输入 3 条信息。这些输入是 ProductName、Quantity 和 Price。这 3 个输入将进入数据库,并以单独的形式显示在数据网格视图中。我希望如果我要添加一个项目,它只会与数据库中的项目合并,如果它们具有相同的 input/data。例如,如果我将在我的数据库中添加价格为 50 和数量为 3 的产品“plates”,而我的数据库中已经有具有相同价格的产品“plates”,它将只是合并或者数量将是添加而不是添加新单元格。
这是 Add 按钮的代码:
double presyo, qty;
presyo = double.Parse(txtPrice.Text);
qty = double.Parse(txtQuantity.Text);
con = new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=CasaFranca;Integrated Security=True");
con.Open();
cmd = new SqlCommand("INSERT INTO Sales (ProductName, Quantity, Price, Total) VALUES (@ProductName, @Quantity, @Price, @Total)", con);
cmd.Parameters.Add("@ProductName", cmbProduct.Text);
cmd.Parameters.Add("@Quantity", txtQuantity.Text);
cmd.Parameters.Add("@Price", txtPrice.Text);
cmd.Parameters.Add("@Total", (presyo * qty));
cmd.ExecuteNonQuery();
MessageBox.Show("Item sucessfully added to database");
这是一个示例输出:
如您所见,即使它们具有相同的产品名称和价格,它们也是分开存储的。我希望将它们合并为一个并添加它们的数量。我该怎么办?
嗯..你已经以 lowest-level/hardest/most 乏味的方式进行了基本的数据库访问,所以我假设你会想以这种方式继续,但我认为这会让如果您考虑切换到一些旨在进行数据访问(数据集、EF)的库,生活会更轻松,但至少您应该调查 Dapper..
运行 一个更新,如果它产生 0 行,运行 一个插入:
con.Open();
var ins = "INSERT INTO Sales (ProductName, Quantity, Price, Total) VALUES (@ProductName, @Quantity, @Price, @Total)";
var upd = "UPDATE Sales SET Quantity = Quantity + @Quantity, Price = Price + @Price, Total = Total + @Total WHERE ProductName = @ProductName";
cmd = new SqlCommand(upd, con);
cmd.Parameters.Add("@ProductName", cmbProduct.Text);
cmd.Parameters.Add("@Quantity", txtQuantity.Text);
cmd.Parameters.Add("@Price", txtPrice.Text);
cmd.Parameters.Add("@Total", (presyo * qty));
if(cmd.ExecuteNonQuery() == 0){
cmd.CommandText = ins;
cmd.ExecuteNonQuery();
}
请注意,这不是很安全;如果两个人在同一时间进行销售,您仍然可以获得重复的行。考虑改为放置主键,在查询中使用它
此外,没有必要存储总数;你可以随时计算它。我还要说的是,除非这是针对保证商品价格相同的单个购物篮,否则如果它是销售历史记录日志,存储多行可能是有意义的,否则您会丢失每次销售价格等信息更改或折扣,如果你所做的只是存储总计
您的问题与DataGridView 无关。由于我看到您显示的 table 已正确更新,因此我认为您已经掌握了显示数据的技巧。
所以让我们专注于您的 SQL。
隐藏访问数据的方式:实施存储库
尝试说服您的项目负责人,如果您可以将界面切换到 Entity Framework 或 Dapper,您的工作效率会更高。
即使你不能说服他,你也需要一个 class 代表你的一行 table,以及一个存储库模式来添加/获取/更新/删除项目table.
像这样:
class Sale
{
public Id {get; set; }
public string ProductName {get; set;}
public int Quantity {get; set;}
public decimal Price {get; set;}
public decimal Total {get; set;}
}
存储库 class 是一个 class,它隐藏了您存储销售的方式。它可以在数据库中,但据您所知,它可以在 XML 文件中,或者在 Internet 上的某个地方。或者也许在内部它只是一个用于单元测试的字典。
所以存储库是一个代表一些存储的对象:您可以在其中存储一些 classes 的项目,稍后您可以检索存储的项目,即使在重新启动计算机后也是如此。
隐藏项目存储方式的好处在于,您可以自由更改内部结构,而无需更改使用存储库的代码。 (还有你的单元测试!)。
可能稍后您决定更改数据库,使其具有第一个规范化形式:一个单独的 table 包含产品信息,并且在您的销售中有一个指向产品的外键 table , 因此您可以从产品 table 中获取名称,而不是每次销售都一遍又一遍地使用相同的产品名称。
通过隐藏项目在存储库中的存储方式,存储库的用户将不知道,也不必知道销售实际上存储在单独的 table 中。
典型的存储库class 将实现一个接口。在您的情况下,它将具有如下方法:
interface ISaleRepository
{
int Add(Sale sale); // return the primary key
Sale Fetch(int id); // fetch by primary key
Update(Sale sale);
Remove(int id);
int Count(); // returns number of Sales;
IEnumerable<Sale> FetchAll();
}
我不知道你的销售模式,但如果你认为 ProductName 是唯一的,请考虑添加:
Sale Fetch(string productName); // fetch by unique productName
回到你的问题
您想要“添加或更新”:如果销售已经在数据库中,则只更新数量:
Sale AddOrUpdate(Sale sale); // returns the updated Sale
如果您对更新后的特卖不感兴趣,请考虑return作废。
您的 AddOrUpdate 可以重用其他 ISaleRepository 方法:
public Sale AddOrUpdate(Sale sale)
{
// Is there already a Sale?
Sale existingSale = this.Fetch(sale.ProductName);
if (existingSale == null)
{
// no such Sale yet. Add it now:
this.Add(sale); // don't forget to update the Id! Usually done in Add
existingSale = sale;
}
else
{
// Sale exists. Update
existingSale.Quantity += sale.Quantity;
this.Update(existingSale);
}
return existingSale; // return the updated value as it is in the database
}
此解决方案需要两次数据库访问。我假设您不会每秒数百次 AddOrUpdate 项目。在那种情况下,代码的可重用性和简单性会与性能损失进行权衡。
如果真的要看表演,有些SQL方言让你一口气更新
因为我用的是entity framework,所以我的SQL有点生疏,但是this solution gets you on track
const string sqlText = @"
begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
update table set ...
where key = @key
end
else
begin
insert into table (key, ...) values (@key, ...)
end
commit tran";
string dbConnectionString = FetchDbConnectionString();
using (var dbConnection = new dbConnection(dbConnectionString))
{
using (var dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = sqlText;
// add parameters:
dbCommand.Parameters.AddWithValue(@ProductName, sale.ProductName);
dbCommand.Parameters.AddWithValue(@Quantity, sale.Quantity);
...
// Open the database and execute the query:
dbConnection.Open();
...
}
}
不获取数据的正常添加将是:
dbCommand.ExecuteNonQuery();
如果你return只有添加项目的Id:
return (int)dbCommand.ExecuteScalar();
如果您正在return更新数据:
using (var dbReader = dbcommand.ExecuteReader())
{
// expect only one item:
if (dbReader.MoveNext())
{
return new Sale
{
ProductName = dbReader.GetString(0),
Quantity = dbReader.GetInt32(1),
...
};
}
// else: nothing returned. Decide what to do: exception?
}