为 DataGridView 保持 DbContext 打开

Keep DbContext open for DataGridView

我有一个 DataGridView,DataGridView 的数据源是我通过 context.Person.Local.ToBindingList() 从 Entity Framework (V6) 获得的一个 BindingList。 在我将 DataSource 设置为此 BindingList 后,我​​处理了上下文,因为我读到保持上下文打开是不好的做法。

因此,如果我想添加一个新行,我会单击 "add" 按钮,该按钮随 BindingNavigator 一起出现,当我将 "people" 对象数据源拖到我的 Windows表格。 每次单击 "add" 按钮时,都会出现一个异常,告诉我上下文已被释放。 使用 DataGridView 时是否需要一直保持上下文打开?哦还有:根据列表框项的选择,数据源可能会在运行时发生变化。

此外,当上下文已被处理并且我从 DataGridView 编辑了一行时,我如何才能找出(在多次更改之后)哪一行发生了更改? 我尝试这样做:

foreach(DataGridViewRow row in peopleDataGridView.Rows)
{
    People item = (People)row.DataBoundItem;
    if (item != null)
    {
        db.People.Attach(item);
    }
}
db.SaveChanges();

...但 SaveChanges() 未识别出任何更改。但是,如果我强制每个附加项目都处于 "modified" 状态,它就会起作用。但是我不想将 100 个项目更改为 "modified",如果实际上只有一个被修改的话。

有什么想法吗?

编辑 1 哦,好吧,所以我更改了我的代码以始终保持上下文打开(或者至少只要显示表单)。 现在,我 运行 遇到了一个不同的问题(人们可能有很多工作):

private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
    People p = (People)listBox1.SelectedItem;
    if(p != null)
    {
        //jobBindingSource.Clear(); this caused another error at runtime...
        db.Entry(p).Collection(b => b.Job).Load();
        jobBindingSource.DataSource = db.Job.Local.ToBindingList();
    }
}

绑定到此 jobBindingSource 实例的 DataGridView 显示一个人的正确工作,但 此外 先前选择的人的工作。我尝试清除 () 条目,但如果我这样做并两次单击同一个人,则 datagridview 开始有时根本不显示任何条目。 st运行ge 行为。 我现在做错了什么?

编辑 2 好的...我自己找到了解决方案。但我拒绝接受这是正确的做法:

private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
    People p = (People)listBox1.SelectedItem;
    if(p != null)
    {
        db.Dispose();
        db = new PeopleJobsEntities();
        db.People.Attach(p);
        db.Entry(p).Collection(person => person.Job).Load();
        jobBindingSource.DataSource = db.Job.Local.ToBindingList();
    }
}

只有当我处理上下文并重新打开它时,整个过程才有效。原因是如果我清除本地缓存(db.Job.Local),即使我使用 Load() 方法也不会重新加载它的条目。有什么方法可以强制重新加载实体吗?

  • 请确定您的商品是否为空。

  • 检查您的连接字符串。

然后,试试这个:

db.People.Add(item);

而不是:

db.People.Attach(item);

虽然我尽量不让 DBContext 长时间打开,但对于数据网格,您别无选择。我将网格的 DataSource 属性 设置为 IQueryable<T>,然后所有编辑、删除和添加都由网格和上下文本身处理。只要您想保留更改,您只需调用 dbContext.SubmitChanges() 即可。每次用户离开行时,您都可以保存 RowLeaveRowValidated 事件。或者您可以在关闭表单时保存。但也要确保在关闭表单时也调用 dbContext.Dispose()

要找出更改的行,您可以查看通过执行以下操作返回的 ChangeSet

var changes = dbContext.GetChangeSet();
dbContext.SubmitChanges();

好的,感谢@jaredbaszler,我想出了这个适合我的解决方案。 我决定让 DbContext 一直保持活动状态。为了清除本地缓存,我在循环中分离了每个实体。我认为这是一种非常令人作呕的方式。 一定有更好的方法...

这是我的:

PeopleJobsEntities db;

public FormTest()
{
    InitializeComponent();
    db = new PeopleJobsEntities();
    db.Database.Log = Console.Write;
    db.People.Load();
    List<People> peoplelist = db.People.Local.ToList();
    listBox1.DataSource = peoplelist;
}

private void FormTest_FormClosing(object sender, FormClosingEventArgs e)
{
   if (db != null)
        db.Dispose();

}

private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
    People p = (People)listBox1.SelectedItem;
    if(p != null)
    {
        List<Job> oldlist = db.Job.Local.ToList();
        foreach (Job j in oldlist)
        {
            db.Entry(j).State = EntityState.Detached;
        }
        db.Entry(p).Collection(b => b.Job).Load();
        jobBindingSource.DataSource = db.Job.Local.ToBindingList();
    }
}

private void jobBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
    foreach(DataGridViewRow row in jobDataGridView.Rows)
    {
        if(row != null && row.DataBoundItem != null)
        {
            Job j = (Job)row.DataBoundItem;
            if(db.Entry(j).State == EntityState.Added)
            {
                if(j.People.Count == 0)
                {
                    People people = (People)listBox1.SelectedItem;
                    if (people != null)
                        j.People.Add(people);
                }
            }
        }
    }
    db.SaveChanges();
}
  • 编辑条目有效
  • 添加新条目有效
  • 删除条目有效