【问题标题】:EF 4.1 Code First : How to load entity references into memory?EF 4.1 Code First:如何将实体引用加载到内存中?
【发布时间】:2011-11-29 22:35:11
【问题描述】:

我刚开始使用 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,如何将引用的实体加载到内存中?

【问题讨论】:

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


    【解决方案1】:

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

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

    public class Order
    {
        ...
    
        public virtual Shipper Shipper { get; set; }
    }
    

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

    另一种方法是使用即时加载直接使用Order 加载Shipper

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

    【讨论】:

    • 我已将 Shipper 属性标记为虚拟,但我仍然遇到相同的异常。该问题已更新以反映这一点。我将尝试 Inlcude 方法,看看是否有效。
    • 你的类中还有Customer 属性——为了支持延迟加载,所有导航属性必须是virtual。此外,您还必须配置 MARS 以在外部查询范围内使用延迟加载。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多