1

Это мой первый раз, когда я использовал Entity Framework 6.1 (сначала код). Я продолжаю сталкиваться с проблемой, когда мои свойства навигации имеют значение null, когда я не ожидаю, что они будут. Я включил ленивую загрузку.Entity Framework 6.1: свойства навигации не загружаются

Моя сущность выглядит следующим образом:

public class Ask 
{ 
    public Ask() 
    { 
     this.quantity = -1; 
     this.price = -1; 
    } 

    public int id { get; set; } 
    public int quantity { get; set; } 
    public float price { get; set; } 

    public int sellerId { get; set; } 
    public virtual User seller { get; set; } 
    public int itemId { get; set; } 
    public virtual Item item { get; set; } 
} 

Она имеет следующий картограф:

class AskMapper : EntityTypeConfiguration<Ask> 
{ 
    public AskMapper() 
    { 
     this.ToTable("Asks"); 

     this.HasKey(a => a.id); 
     this.Property(a => a.id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
     this.Property(a => a.id).IsRequired(); 

     this.Property(a => a.quantity).IsRequired(); 

     this.Property(a => a.price).IsRequired(); 

     this.Property(a => a.sellerId).IsRequired(); 
     this.HasRequired(a => a.seller).WithMany(u => u.asks).HasForeignKey(a => a.sellerId).WillCascadeOnDelete(true); 

     this.Property(a => a.itemId).IsRequired(); 
     this.HasRequired(a => a.item).WithMany(i => i.asks).HasForeignKey(a => a.itemId).WillCascadeOnDelete(true); 
    } 
} 

В частности, проблема заключается в том, что у меня есть Ask объекта правильно установить itemId (что соответствует к Item в базе данных), но свойство навигации item равно null, и в результате я получаю исключение NullReferenceException. Исключение в коде ниже, когда я пытаюсь получить доступ a.item.name:

List<Ask> asks = repo.GetAsksBySeller(userId).ToList(); 
List<ReducedAsk> reducedAsks = new List<ReducedAsk>(); 

foreach (Ask a in asks) 
{ 
    ReducedAsk r = new ReducedAsk() { id = a.id, sellerName = a.seller.username, itemId = a.itemId, itemName = a.item.name, price = a.price, quantity = a.quantity }; 
    reducedAsks.Add(r); 
} 

Смутно, навигация свойство seller работает отлично там, и я не могу найти что-нибудь, что я сделал по-другому в «User «сущность», ни в своем картографе.

У меня есть тест, который воссоздает, но она проходит без каких-либо проблем:

public void canGetAsk() 
{ 
    int quantity = 2; 
    int price = 10; 

    //add a seller 
    User seller = new User() { username = "ted" }; 
    Assert.IsNotNull(seller); 
    int sellerId = repo.InsertUser(seller); 
    Assert.AreNotEqual(-1, sellerId); 

    //add an item 
    Item item = new Item() { name = "fanta" }; 
    Assert.IsNotNull(item); 
    int itemId = repo.InsertItem(item); 
    Assert.AreNotEqual(-1, itemId); 

    bool success = repo.AddInventory(sellerId, itemId, quantity); 
    Assert.AreNotEqual(-1, success); 

    //add an ask 
    int askId = repo.InsertAsk(new Ask() { sellerId = sellerId, itemId = itemId, quantity = quantity, price = price }); 
    Assert.AreNotEqual(-1, askId); 

    //retrieve the ask 
    Ask ask = repo.GetAsk(askId); 
    Assert.IsNotNull(ask); 

    //check the ask info 
    Assert.AreEqual(quantity, ask.quantity); 
    Assert.AreEqual(price, ask.price); 
    Assert.AreEqual(sellerId, ask.sellerId); 
    Assert.AreEqual(sellerId, ask.seller.id); 
    Assert.AreEqual(itemId, ask.itemId); 
    Assert.AreEqual(itemId, ask.item.id); 
    Assert.AreEqual("fanta", ask.item.name); 
} 

Любая помощь будет очень ценится; это заставляло меня сходить с ума в течение нескольких дней.


EDIT:

База данных SQL Server 2014.

На данный момент я один общий контекст, инстанцирован уровень выше этого (мое хранилище слой для БД). Должен ли я создавать новый контекст для каждого метода? Или создание экземпляра на минимально возможном уровне (т. Е. Для каждого доступа db)? Например:

public IQueryable<Ask> GetAsksBySeller(int sellerId) 
{ 
    using (MarketContext _ctx = new MarketContext()) 
    { 
     return _ctx.Asks.Where(s => s.seller.id == sellerId).AsQueryable(); 
    } 
} 

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

public IQueryable<Transaction> GetTransactionsByUser(MarketContext _ctx, int userId) 
{ 
    IQueryable<Transaction> buyTransactions = GetTransactionsByBuyer(_ctx, userId); 
    IQueryable<Transaction> sellTransactions = GetTransactionsBySeller(_ctx, userId); 

    return buyTransactions.Concat(sellTransactions); 
} 

Тогда я мог бы просто создать экземпляр нового контекста всякий раз, когда я называю что-нибудь из слоя репо: repo.GetTransactionsByUser(new MarketContext(), userId);

Опять же, спасибо за помощь. Я новичок в этом и не знаю, какой подход был бы лучшим.

+1

Wat - это срок службы контекста (ов)? И какая это база данных? –

+0

Спасибо, что посмотрели. Я отредактировал вопрос, чтобы дать дополнительную информацию. –

ответ

1

Попробуйте добавить Include вызов в вашем хранилище вызова:

public IQueryable<Ask> GetAsksBySeller(int sellerId) 
{ 
    using (MarketContext _ctx = new MarketContext()) 
    { 
     return _ctx.Asks 
       .Include("seller") 
       .Include("item") 
       .Where(s => s.seller.id == sellerId).AsQueryable(); 
    } 
} 

Кроме того, существует метод Include расширение, которое принимает лямбда-выражение в качестве параметра и предоставляет проверку типов на этапе компиляции

http://msdn.microsoft.com/en-us/data/jj574232.aspx

+0

Большое спасибо, это работает! У меня создалось впечатление, что ленивая загрузка означает «Включить звонки» не нужно? Для справки у меня есть 'Configuration.LazyLoadingEnabled = true;' в конструкторе 'MarketContext'. –

0

Что касается контекстной продолжительности жизни, ваши хранилища должны иметь один контекст для каждого запроса, если это веб-приложение.Иначе это немного более произвольно, но это должно быть что-то вроде контекста для каждого случая использования или вызова службы.

Таким образом, шаблон будет: создавать контекст, передавать его в репозитории, участвующие в вызове, выполнять задачу и удалять контекст. Контекст можно рассматривать как вашу единицу работы, поэтому независимо от количества репозиториев, в конце концов, должно быть достаточно одного SaveChanges(), чтобы зафиксировать все изменения.

Я не могу сказать, разрешит ли это проблему ленивой загрузки, потому что из того, что я вижу, я не могу объяснить, почему этого не происходит.

Но хотя, если бы я был на вашем месте, я бы хотел разобраться с ним, ленивая загрузка - это то, на что нельзя полагаться слишком много. Посмотрите на свой (сокращенном) Код:

foreach (Ask a in asks) 
{ 
    ReducedAsk r = new ReducedAsk() 
        { 
         sellerName = a.seller.username, 
         itemName = a.item.name 
        }; 

Если отложенная загрузка будет работать, как и ожидалось, это будет выполнять два запроса к базе данных для каждой итерации цикла. Конечно, это очень неэффективно. Вот почему использование Include (как и в ответе Антона) во всяком случае лучше не только обойти вашу проблему.

Дальнейшая оптимизация, чтобы сделать проекцию (т.е. new {) в самом запросе:

var reducedAsks = repo.GetAsksBySeller(userId) 
         .Select(a => new ReducedAsk() { ... }) 
         .ToList(); 

(Предполагая, - и требует - то repo.GetAsksBySeller возвращается IQueryable).

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

+0

Это большая помощь, спасибо! –

 Смежные вопросы

  • Нет связанных вопросов^_^