在Linq to SQL中处理DataContext后无法访问相关数据

Jon*_*das 5 c# sql-server datacontext entity-framework linq-to-sql

重述了我的问题,旧文本在下面

由于我仍然希望得到答案,我想重申我的问题.Image我有一个带有两个列表的GUI,一个列表显示数据库的所有条目tblOrders,另一个列表显示每个订单中的项目.

我可以使用Linq2sql或EF从数据库中获取所有订单,如下所示:

using (DataClasses1DataContext DataContext = new DataClasses1DataContext())
    ListOfOrders = DataContext.tblOrder.ToList();
Run Code Online (Sandbox Code Playgroud)

我可以在列表中显示这些顺序或datagridview.然后选择其中一个作业,我想从表中访问实体集合tblItems.我可以这样做:

ListOfOrders.First().tblItems.toList();
Run Code Online (Sandbox Code Playgroud)

除了我不能,因为我需要DataContext处理掉的.这在某种程度上是有道理的,因为我不能保证自从我检索ListOfOrders. So ideally I would like to check if there has been an addition to a tblOrder.tblItems集合后没有添加到该订单的新项目,并且只在需要时才从服务器重新检索该集合.

背景是,我的模型有点复杂:它由Orders组成,它由Parts组成,包括Tasks.因此,为了评估每个订单的进度,我必须检索属于订单的所有部件,并且对于每个订单,我必须看到已经完成了多少任务.在一个有200个作业的数据库中,每个都有1到10个部分,这只会让我的程序在响应性方面变慢...

谁能帮我?

原始问题

我发现了很多问题DataContext,但我还没有找到解决问题的方法.如果我执行以下操作:

using (DataClasses1DataContext DataContext = new DataClasses1DataContext())
    ListOfOrders = DataContext.tblOrder.ToList();
Run Code Online (Sandbox Code Playgroud)

这给了我一个表中实体的tblOrder列表.但现在我想这样做:

DataTable Orders = new DataTable();
Orders.Columns.Add("Order-ID", typeof(int));
Orders.Columns.Add("Order-Name", typeof(string));
Orders.Columns.Add("Order-Items", typeof(string));

dataGridView1.DataSource = Orders;

foreach (tblOrder Order in ListOfOrders)
{
    var newRow = Orders.NewRow();
    newRow["Order-ID"] = Order.orderID;
    newRow["Order-Name"] = Order.orderName;
    newRow["Order-Items"] = string.Join(", ", Order.tblItem.Select(item=> item.itemName).ToList());
        // System.ObjectDisposedException
    (dataGridView1.DataSource as DataTable).Rows.Add(newRow);
}
Run Code Online (Sandbox Code Playgroud)

我不能因为访问tblItem表中与外键相关的所有实体似乎没有存储.

什么工作:

DataClasses1DataContext DataContext = new DataClasses1DataContext();
ListOfOrders = DataContext.tblOrder.ToList();

DataTable Orders = new DataTable();
Orders.Columns.Add("Order-ID", typeof(int));
Orders.Columns.Add("Order-Name", typeof(string));
Orders.Columns.Add("Order-Items", typeof(string));

dataGridView1.DataSource = Orders;

foreach (tblOrder Order in ListOfOrders)
{
    var newRow = Orders.NewRow();
    newRow["Order-ID"] = Order.orderID;
    newRow["Order-Name"] = Order.orderName;
    newRow["Order-Items"] = string.Join(", ", Order.tblItem.Select(item=> item.itemName).ToList()); 
    (dataGridView1.DataSource as DataTable).Rows.Add(newRow);
}

DataContext.Dispose();
Run Code Online (Sandbox Code Playgroud)

但据我所知,这是不可取的.

编辑

我将我的示例扩展为Controller-View-Pattern.

using System.Collections.Generic;
using System.Linq;

namespace TestApplication
{
    class Controller
    {
        private List<tblOrder> _orders;
        public IList<tblOrder> Orders
        {
            get
            {
                return _orders;
            }
        }

        public Controller()
        {
            using (var DataContext = new DataClasses1DataContext())
            {
                _orders = DataContext.tblOrder.ToList();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

视图现在从控制器检索Orders:

using System.Data;
using System.Linq;
using System.Windows.Forms;

namespace TestApplication
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            Controller controller = new Controller();

            DataTable Orders = new DataTable();
            Orders.Columns.Add("Order-ID", typeof(int));
            Orders.Columns.Add("Order-Name", typeof(string));
            Orders.Columns.Add("Order-Items", typeof(string));

            dataGridView1.DataSource = Orders;

            foreach (tblOrder Order in controller.Orders)
            {
                var newRow = Orders.NewRow();
                newRow["Order-ID"] = Order.orderID;
                newRow["Order-Name"] = Order.orderName;
                newRow["Order-Items"] = string.Join(", ", Order.tblItem.Select(item=> item.itemName).ToList());
                (dataGridView1.DataSource as DataTable).Rows.Add(newRow);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可悲的是问题仍然存在......

Mik*_*eTV 4

实体框架延迟加载对象数据,这意味着它会尽可能晚地加载最少量的数据。接受您的查询:

ListOfOrders = context.tblOrder.ToList();
Run Code Online (Sandbox Code Playgroud)

您在这里请求表中的所有记录tblOrdertblItem实体框架不会在您的程序中提前读取,并且知道您将在处理上下文后查看该表,因此它假设它可以tblItem稍后加载数据。由于懒惰,它加载了最低限度的请求: 中的记录列表tblOrder

两种方法可以解决这个问题:

禁用延迟加载

    using (var context = new DataClasses1DataContext())
    {
        data.Configuration.LazyLoadingEnabled = false;
        _orders = context.tblOrder.ToList();
    }
Run Code Online (Sandbox Code Playgroud)

使用LazyLoadingEnabled=false实体框架将选择表的全部内容tblOrder以及通过外键与其连接的所有表。这可能需要一段时间并使用大量内存,具体取决于相关表的大小和数量。

(编辑:我的错误,禁用 LazyLoading 不会启用预加载,并且预加载没有默认配置。对错误信息表示歉意。.Include下面的命令看起来是唯一的方法。)

包括附加表

    using (var context = new DataClasses1DataContext())
    {
        data.Configuration.LazyLoadingEnabled = false;
        _orders = context.tblOrder.Include("tblItems").ToList();
    }
Run Code Online (Sandbox Code Playgroud)

tblItems这告诉实体框架在加载表数据时从前面加载所有相关数据tblOrders。EF 仍然不会从其他相关表加载任何数据,因此在释放上下文后其他数据将不可用。

然而,这并不能解决陈旧数据的问题——也就是说,随着时间的推移,其中的记录dataGridView1将不再是最新的。您可以有一个触发刷新的按钮或计时器。最简单的刷新方法是重新执行整个过程 - 重新加载_orders,然后有选择地重新填充dataGridView1