【问题标题】:Entity Framework - lazy loading or additional async/await query method?实体框架 - 延迟加载或其他异步/等待查询方法?
【发布时间】:2018-01-07 08:14:30
【问题描述】:

我有这些领域模型

public class Topic
{
    public int TopicId { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }

    public int? TopicId { get; set; }
    public virtual Topic Topic { get; set; }
}

例如,我想实现 TestAsync 方法,我想使用 Topic 对象和相关的 Posts 对象。

我可以使用 async 方法和 topicId 作为参数来获得主题模型。

public async Task<bool> TestAsync(int topicId)
{
    var topic = await topicService.GetByIdAsync(topicId);

    // posts ...
}

我有两种方法,如何获取相关帖子。 但是,如果我使用 LazyLoading 或仅使用另一个异步查询有什么区别?

// Example: 1 (LazyLoading)
var posts = topic.Posts;

// OR Example: 2 (Async method)
var posts = await postService.GetAllByTopicIdAsync(topicId);

所以,我认为 Example:1 将同步工作,并且我失去了 async/await 代码的所有优点。 但是示例:2让我想,那可能是我不知道延迟加载的所有魅力:) 谁能澄清我应该使用什么解决方案以及为什么?谢谢:)

【问题讨论】:

  • 坦率地说,异步/等待代码在 ASP.NET 中用于数据库访问的“优势”微乎其微。
  • 这不是异步/等待或延迟加载之间的选择恕我直言。可以选择在向数据库触发两个查询(使用延迟加载)或在 sql 中连接(使用 Include 进行急切加载)之间进行选择。
  • David,但在许多文章中,建议使用 async/await 来处理与数据库一起工作的方法,因为此时线程返回到线程池。 Peter,Eager Loading 可以成为一个解决方案,谢谢!
  • 原始线程可能会返回线程池,但我希望另一个线程被绘制来服务数据库请求,这最终会抵消大部分好处。不是对于网站,而是对于只有一个“UI 线程”的桌面,将工作委派给线程池会好得多。
  • @romeozor 迁移到异步模型通常是件好事。只是如果您需要阻塞线程来运行查询,您不必太担心。

标签: c# asp.net entity-framework async-await lazy-loading


【解决方案1】:

你不应该这样做。我假设您的 GetByIdAsync() 是这样实现的。

public async Task<Topic> GetByIdAsync(int id)
{
        return await context.Topics.FirstAsync(t=>t.Id == id);
}

你应该把它改成

public async Task<Topic> GetByIdAsync(int id)
{
        return await context.Topics.Include(t=>t.Posts).FirstAsync(t=>t.Id == id);
}

【讨论】:

  • 不了解内部情况不建议这样做,可能Topics包含数千条记录,而Post很少使用,那么这个解决方案是最无效的。
  • 或者也许向 GetByIdAsyc 添加一个参数以请求也加载帖子。
  • @Gusman 这就是我说“假设”的原因。主题中的行数如何影响这个解决方案?我很好奇。
  • 只是因为您拥有的主题记录越多,您拥有的帖子就越多。如果每个主题平均有 100 个帖子(只是随机数),那么加载 1000 个主题将加载 100000 个帖子。
  • 好吧,忘了几千个,我没有意识到它是通过 id 实现的,无论如何,如果该功能被广泛使用并且通常不需要 post 的,那将是一个性能杀手。
【解决方案2】:

延迟加载总是同步的,这很不幸。例如,EF Core 以其异步优先的心态,(尚)不支持延迟加载。

其他选项是按照 Peter 的建议进行连接(急切加载),它异步执行单个查询;或者做一个显式的第二个异步查询。你会选择哪一个取决于你的模型通常是如何使用的。

就个人而言,如果模型总是一起使用,我会选择进行预先加载,否则会进行多个异步查询。我自己不使用延迟加载,尽管没有什么可以阻止它工作。

【讨论】:

  • 感谢您的广泛建议。它帮助了我。也感谢彼得:)
  • 如果使用一次性资源,延迟加载可能会导致问题,因为该方法可能会在延迟加载触发之前退出其using 子句。
  • 延迟加载和导航属性通常很糟糕。即使使用预先加载,如果您加载超过一两个,它会使查询膨胀到无法识别,并最终跨行返回重复数据,因为它必须返回单个数据网格而不是多个活动结果集。跨度>
【解决方案3】:
        public async Task<TProperty> GetReferenceAsync<TEntity, TProperty>(DBContext context, TEntity entity, Expression<Func<TEntity, TProperty>> property)
        where TEntity : class, IIdentifiable where TProperty : class
        {
            var getProperty = property.Compile();
            var value = getProperty(entity);
            if (value != null) { return value; }

            await context.Entry(entity).Reference(property).LoadAsync();
            return getProperty(entity);
        }

用法:var posts = await GetReferenceAsync(context, topic, e =&gt; e.Posts);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多