EF 4.1 Code First: Как загрузить ссылки на сущности в память? - PullRequest
1 голос
/ 03 октября 2011

Я только начал работать с EF 4.1 Code First и заметил, что по умолчанию ссылки (свойства навигации) не загружаются в память с сущностью POCO, которую вы запрашивали с помощью LINQ-to-Entity. У меня не было успеха с загрузкой ссылочных объектов с использованием DbEntityEntry.Reference . Когда я вызываю DbReferenceEntry.Load, выдается следующее исключение:

"Уже есть открытый DataReader, связанный с этой командой, который должен быть закрыт первым."

Закрытие DataReader - это не то, что мне действительно нужно делать, когда я нахожусь в середине нескольких запросов LINQ.

Например, следующее не будет работать:

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();                   
                }
            }
        }

Вот класс заказа:

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 Во-первых, как вы заставляете ссылочную сущность загружаться в память?

1 Ответ

3 голосов
/ 03 октября 2011

Если вам нужно, чтобы несколько операций чтения базы данных работали одновременно, вы должны разрешить MARS в строке подключения , но ваша реальная проблема в другом месте.

EF не загружает свойства навигации по умолчанию. Вы должны либо использовать ленивую или нетерпеливую загрузку. При отложенной загрузке все свойства навигации в сущности должны быть виртуальными:

public class Order
{
    ...

    public virtual Shipper Shipper { get; set; }
}

Как только отложенная загрузка и создание прокси разрешено в контексте (по умолчанию), ваша собственность будет автоматически загружена при первом обращении к вашему коду. Этот доступ должен происходить в пределах того же контекста, который использовался для загрузки ордера (вы все еще можете встретить ошибку с открытым DataReader здесь).

Другой способ - загрузить Shipper напрямую с Order, используя готовую загрузку:

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