使用实体框架C#更新一行

问题描述

我正在使用C#和实体框架开发Windows窗体应用程序。我将客户购买的所有产品放入DGVNewOrder。对于DGVNewOrder中的evry产品,我想为每个出售的产品更新(按名称)库存数量。这是我尝试的代码:'''

Product pro = new Product(); 

for (int i = 0; i < DGVNewOrder.RowCount - 1; i++)
{
    string Soled_Product_name = DGVNewOrder.Rows[i].Cells[0].Value.ToString();
    pro = db.Products.Where(d => d.Name == Soled_Product_name ).FirstOrDefault();
    int y = Convert.ToInt32(pro.QteInStock); // QteInStock befor selling 
    int x = Convert.ToInt32(DGVNewOrder.Rows[i].Cells[2].Value); // the sold quantity 
    pro.QteInStock = y - x;  //  The remaining quantity after the sale
    db.Entry(pro).State = EntityState.Modified;
    db.SaveChanges();
 }

这里的问题是数量没有更新,但是所有产品的名称都更改为与DGVNewOrder中存在的第一个产品相同的名称 我也试过了

string Soled_Product_name = DGVNewOrder.Rows[i].Cells[0].Value.ToString();
          
Product c = (from w in db.Products
              where w.Name == Soled_Product_name
             select w).First();

int y = Convert.ToInt32(pro.QteInStock);
int x = Convert.ToInt32(DGVNewOrder.Rows[i].Cells[2].Value);
c.QteInStock = y - x;

db.Entry(pro).State = EntityState.Modified;
db.SaveChanges();

但给出相同的结果

解决方法

每当您有一个显示集合元素的控件(例如DataGridView,ListView)时,直接编辑控件中的项目通常不是一个好主意。造成这种情况的原因之一是,您需要主动从控件中获取更改的项目。例如,如果控件的显示发生更改,则列的顺序更改了,或者DataGridView中的行的顺序更改了,您必须主动找出更改的内容。

为此使用DataBinding更容易。将需要显示的项目放在BindingList<T>中,并将其分配给DataGridView.DataSource

DataGridViewColum.DataPropertyName包含应在列中显示的<T>属性的名称。只要操作员编辑一个值并接受它,就会通知您BindingList已更改。您无需再阅读DataGridView,您将自动知道在BindingList中已更新了完整的DataGridView。甚至添加或删除的行都在BindingList中

示例:

class OrderLine
{
    public int Id {get; set;}           // primary key of the OrderLine
    public int ProductId {get; set;}    // foreign key to the Product in this OrderLine
    public string ProductDescription {get; set;}
    public decimal ProductPrice {get; set;}
    public int NrOfItems {get; set;}
    public decimal TotalPrice => this.ProductPrice * this.NrOfItems;
}

您想要一个DataGridView来显示ProductId / ProductDescription / ProductPrice / NrOfItems / TotalPrice。

Operatos可以在DataGridView中添加新的OrderLine,或删除OrderLine。他们无法更改ProductId / ProductDescription / ProductPrice,但可以更改NrOfItems。

DataGridView dgv = new DataGridView();

// Add columns. This is some tedious work,usually it is best to let visual studio designer
// do this
dgv.Columns.Add(new DataGridViewColumn()
{
     Name = "columnProductId",DataProperty = nameof(OrderLine.ProductId),ReadOnly = true;
     ...
}
您所有列的

等。

现在用一些初始项填充您的datagridview:

IList<OrderLine> initialOrderLines = ...
var actualOrderLines = new BindingList<OrderLine(initialOrderLines);
dgv.DataSource = actualOrderLines;

如果没有初始订单行,请使用默认构造函数

然后,所有列都填充了实际的OrderLines。在编辑过程中,此集合会自动更新。如果操作员添加了新行,则会自动使用默认的OrderLine对其进行初始化,或者,如果您愿意,如果您订阅事件BindingList.OnAddingNew,则可以提供OrderLine。

最后,在操作员完成所有更改之后,他按下按钮:

void OnButtonReady_Clicked(object sender,...)
{
    // you can be certain the the BindingList is up-to-date
    // you don't have to fetch the items from the DataGridView anymore.
    this.UpdateStock(this.actualOrderLines);
}

private void UpdateStock(IEnumerable<OrderLine> actualOrderLines)
{
     foreach (OrderLine orderLine in actualOrderLines)
     {
         // TODO: update the stock
     }
}

有趣的是:如果更改了“列”顺序或对“行”进行了排序,那么这对您的实际OrderLines无效。如果将来您决定添加或删除列:没问题,您只知道您的信息在ActualOrderLines中,谁在乎如何向操作员显示信息,或他如何编辑信息。

最后一件好事:如果要通过简单的鼠标单击标题来对列进行排序,请考虑Nuget Package BindingListView

使用BindingList<T>代替BindingListView<T>

var initialOrderLines = ...
BindingListView<OrderLine> actualOrderLines = new BindingListView<OrderLine>(initialOrderLines);
this.dgv.DataSource = actualOrderLines;

宾果游戏:只要单击标题,项目的列属性就按升序或降序排列。甚至字形(显示排序顺序的小箭头)也正确显示。