Как асинхронно вызвать отношение при ленивой загрузке с Entity Framework 6.4? - PullRequest
2 голосов
/ 02 апреля 2020

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

У меня есть следующая модель

public class Product
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool Available { get; set; }
    // ...
    public int CategoryId { get; set; }
    public virtual Category Category { get; set; }
}

В контроллере я хочу получить доступ к Category используя ленивую загрузку. Я знаю, что могу использовать расширение Include(), чтобы загружать отношения. Но вместо этого я хочу его лениво загрузить. Вот пример того, как я хотел бы использовать его

public async Task<ActionResult> Get(ShowProduct vm)
{
    if (ModelState.IsValid)
    {
        using (var db = new DbContext())
        {
            Product model = await db.Products.FirstAsync(vm.Id);

            vm.Title = model.Title;

            if (model.Avilable)
            {
                // How can call the **Category** property using await operator?
                vm.CategoryTitle = model.Category.Title;
            }
        }
    }

    return View(vm);
}

Как мне лениво загрузить отношение Category, используя оператор await, чтобы предотвратить блокировку текущего потока?

Ответы [ 2 ]

2 голосов
/ 02 апреля 2020

Как мне лениво загрузить отношение Category с помощью оператора await, чтобы предотвратить блокировку текущего потока?

Вы должны явно загрузить ссылку, если хотите asyn c.

Etither

await db.Entry(model).Reference<Category>().LoadAsync();

Или, явным образом извлеките связанную сущность и дайте трекеру изменений исправить свойство навигации.

var category = await db.Categories.FindAsync(model.CategoryId);
vm.CategoryTitle = model.Category.Title;
0 голосов
/ 08 апреля 2020

Я не знаю, что делает FirstAsync(vm.Id). Насколько я знаю, нет перегрузки, которая принимает целое число в качестве параметра. Я думаю, что ваша FirstAsyn c делает что-то похожее на:

Product model = await db.Products
    .Where(product => product.Id == vm.Id)
    .FirstAsync();

Или, может быть, даже лучше: FirstOrDefaultAsync.

Ваша цель - сделать левое внешнее соединение, а иногда нет, в зависимости от одного из свойств левого элемента.

Ваше решение извлекает левый элемент объединения, извлекает ключ объединения и выбирает правый элемент объединения. Фактически, ваш процесс выполняет объединение при выполнении двух запросов к базе данных.

Системы управления базами данных чрезвычайно оптимизированы для объединений. Два отдельных запроса к базе данных без объединения почти наверняка менее эффективны, чем один запрос с объединением.

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

var fetchedData = dbContext.Products
    .Where(product => product.Id == vm.Id)
    .Select(product => new
    {
        Title = product.Title,
        Available = product.Available,
        CategoryTitle = product.Category.Title,
    })
    .FirstOrDefaultAsync();

Теперь вы можете заполнить vm:

if (fetchedData != null)
{
    vm.Title = fetchedData.Title;
    if (fetchedData.Available)
    {
        vm.CategoryTitle = fetchedData.CategoryTitle;
    }
}
// TODO: decide what to do if there is no fetched data?
...