查找异步任务和包含LINQ语句

81

我目前的代码运行良好。

public async Task<ActionResult> Details(Guid? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    ItemDetailModel model = new ItemDetailModel();
    model.Item = await db.Items.FindAsync(id);
    if (model.Item == null)
    {
        return HttpNotFound();
    }

    return View(model);
}

但我想再包含1个表格,不能使用FindAsync

public async Task<ActionResult> Details(Guid? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    ItemDetailModel model = new ItemDetailModel();
    model.Item = await db.Items.Include(i=>i.ItemVerifications).FindAsync(id);

    if (model.Item == null)
    {
        return HttpNotFound();
    }           

    return View(model);
}

所以我面临这个错误

严重性 代码 描述 项目 文件 行 抑制状态 错误 CS1061 'IQueryable' 不包含定义为 'FindAsync' 的方法,也不存在接受类型为 'IQueryable' 的第一个参数的扩展方法 'FindAsync'(是否缺少 using 指令或程序集引用?)

有什么线索可以解决这个问题吗?


2
这篇帖子可能会给你一些答案。看起来.Include返回的是一个ObjectQuery<T>而不是一个具有FindAsync方法的DbSet - rory.ap
请查看这个问题 - Jeffrey Patterson
3个回答

142

最简单的方法是使用 FirstOrDefaultAsyncSingleOrDefaultAsync

model.Item = await db.Items.Include(i => i.ItemVerifications)
    .FirstOrDefaultAsync(i => i.Id == id.Value);

你遇到错误的原因是因为 Find / FindAsync 方法是针对 DbSet<T> 定义的,但 Include 的结果是 IQueryable<T>

另一种方法是将 FindAsync显式加载相结合:

model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
    return HttpNotFound();
}
await db.Entry(model.Item).Collection(i => i.ItemVerifications).LoadAsync();    

1
你可以通过解释他尝试的方法为什么不起作用来改进这个答案(请参见我在问题下的评论)。 - rory.ap
2
使用了第二个选项。谢谢。 - Tzvi Gregory Kaidanov
var entity = await context.Entities .Include("field") .FirstOrDefaultAsync(e => e.Id == id); - NiB
1
考虑到“Find”和“FirstOrDefault”哪个更快的争论,似乎指出“Find”更快,但这样做会对性能产生负面影响。 - Veverke
1
@Veverke 这里的问题不是哪个更快,而是哪个支持加载相关数据。Find不支持,所以任何进一步的讨论都是无用的。性能问题可以考虑,但功能是第一位的。 - Ivan Stoev

13

如果您正在使用通用存储库,而且您在运行时不知道PK,那么可以使用此方法:

public interface IGenericRepository<TEntity> where TEntity : class
{
    Task<TEntity> Get(int id, string[] paths = null);
}

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    private readonly ApplicationDbContext _context;
    private readonly DbSet<TEntity> _dbSet;

    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _dbSet = _context.Set<TEntity>();
    }

    public async Task<TEntity> Get(int id, string[] paths = null)
    {
        var model = await _dbSet.FindAsync(id);
        foreach (var path in paths)
        {
            _context.Entry(model).Reference(path).Load();
        }
        return model;
    }
}

2
我希望这会使得需要两次访问数据库,而不是在DbSet上使用Include时只需要一次。很遗憾FindAsync没有直接提供包含其他字段的功能。 - fubaar

2

当您使用 SOLID 原则和领域设计进行编程时,请使用泛型。Repository 模式使用泛型类。我将 lambda 表达式传递给 GetObjectsQueryable 函数。我已经使用 Code First Handlebars 将延迟加载设置为开启状态。然而,我正在转向非延迟加载并实现微服务架构。包含表是一个字符串,您可以使用 nameof(xxclass) 函数确保正确的名称。该函数返回 IQueryable 结果。仓储类方法可以由其派生类使用,增强方法是受保护的。这是一个 .NET Core 的演示。

public class Repository
    where T : class
{
    public IQueryable<T> GetObjectsQueryable(Expression<Func<T, bool>> predicate, string includeTable="")
    {
        IQueryable<T> result = _dbContext.Set<T>().Where(predicate);
        if (includeTable != "")
            result = result.Include(includeTable);

        return result;
    }
}

在 Entity Framework 的 OnConfiguring(DbContextOptionsBuilder optionsBuilder) 方法中,使用以下调用 optionsBuilder.UseLazyLoadingProxies().UseSqlServer(connectionString) 添加延迟加载代理。 - Golden Lion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接