EF 4.1 Code First:如何将实体引用加载到内存中?

时间:2011-10-03 13:26:53

标签: entity-framework linq-to-entities entity-framework-4.1

我刚刚开始使用EF 4.1 Code First,并注意到默认情况下,引用(导航属性)没有使用您使用LINQ-to-Entity查询的POCO实体加载到内存中。我使用DbEntityEntry.Reference加载引用的实体没有成功。当我调用DbReferenceEntry.Load时,抛出以下异常:

  

“已经有一个与此命令关联的打开DataReader,必须先关闭它。”

当我处于几个LINQ查询的中间时,关闭DataReader并不是我真正想要做的事情。

例如,以下内容不起作用:

using (db1 = new NorthindDbContext(new SqlConnection(this.NORTHWIND))) { 
orders = db1.Orders.Where(o => !(o.CustomerId == null || o.ShipperId == null || o.EmployeeID == null));
            foreach (var o in orders) {
                Shipper s = o.Shipper;//exception: "There is already an open DataReader associated with this Command which must be closed first."
                DbEntityEntry<Order> entry = db1.Entry(o);
                DbReferenceEntry<Order, Shipper> shipper_reference = entry.Reference<Shipper>("Shipper");
                if (!shipper_reference.IsLoaded) {
                    shipper_reference.Load();                   
                }
            }
        }

以下是Order类:

public partial class Order
{
    public System.Int32 ID { get; set; }                
    public System.Nullable<System.DateTime> OrderDate { get; set; }
    public System.Nullable<System.DateTime> RequiredDate { get; set; }
    public System.Nullable<System.DateTime> ShippedDate { get; set; }       
    public System.Nullable<System.Decimal> Freight { get; set; }        
    public Employee Employee { get; set; }
    public Int32 CustomerId { get; set; }
    public Customer Customer { get; set; }
    public Int32 EmployeeID { get; set; }
    /// <summary>
    /// marked virtual for lazy loading
    /// </summary>
    public virtual Shipper Shipper { get; set; }
    public Int32 ShipperId { get; set; }
} 

我尝试将Order.Shipper属性标记为虚拟,如果运行代码,我仍会得到相同的异常。

ObjectQuery.Include方法, 工作:

[TestMethod]    
//configure MARS here?
//Order.Shipper is not marked virtual now
//...
            using (db = new NorthindDbContext(new SqlConnection(this.NORTHWIND))) {                         
            db.Orders.Include(o => o.Shipper)
.Where(o => !(o.CustomerId == null || o.ShipperId == null || o.EmployeeID == null));
            foreach (var o in orders) {
                Shipper s = o.Shipper;//null
                DbEntityEntry<Order> entry = db.Entry(o);
                DbReferenceEntry<Order, Shipper> shipper_reference = entry.Reference<Shipper>("Shipper");
                if (!shipper_reference.IsLoaded) {
                    shipper_reference.Load();//"There is already an open DataReader associated with this Command which must be closed first."
                }


            }

使用EF 4.1 Code First,如何将引用的实体加载到内存中?

1 个答案:

答案 0 :(得分:3)

如果您需要同时处理多个数据库读取,则必须allow MARS on your connection string,但您的真正问题是在其他地方。

默认情况下,EF不会加载导航属性。您必须使用懒惰或急切加载。延迟加载需要实体中的所有导航属性都是虚拟的:

public class Order
{
    ...

    public virtual Shipper Shipper { get; set; }
}

一旦在上下文中允许延迟加载和代理创建(默认),您的属性将在您的代码第一次访问时自动加载。此访问必须在用于加载订单的相同上下文的范围内发生(您仍然可以通过此处打开的DataReader来解决错误。)

其他方法是使用预先加载直接使用Shipper加载Order

var query = context.Orders.Include(o => o.Shipper)...