linq-to-sql - 通过在 Task.Run() 中包装 linq 查询,在多线程中进行 linq to sql

标签 linq-to-sql

如下所示将linq 2 sql查询包装到Task.Run方法中好不好

var keywordlistquery = await Task.Run(() =>
        {
            using (DataContext context = new DataContext(connection))
            {
                context.ObjectTrackingEnabled = false;
                return from keyword in context.GetTable<KeywordsList>()
                       select new
                       {
                           keyword.search_text,
                           keyword.search_keyword
                       };
            }
        });

上面的代码线程安全吗,在生产环境中会不会有任何问题?有没有其他更好的方法来编写上面的代码。

最佳答案

这里的好答案在很大程度上取决于代码的意图。

但总的来说,请记住,Linq to SQL 技术是在 .Net 中实现 native 异步和等待模式之前构建然后停止使用的。

因此,除非您非常习惯手动维护异步任务,否则最好不要尝试将异步与 Linq to SQL 一起使用。很有可能,除非期望服务器能够处理非常高级别的请求并发,否则您不会获得太多性能提升,但是手动处理异步任务是引入真正难以检测的错误的绝佳方式,这些错误最终会意外阻止请求线程。

如果您确实需要像这样在代码中处理异步,有两种解决方案。

首先了解上面的代码创建了一个查询,但没有执行它。它返回的是一个 IQuerable...基本上,可以将其视为尚未运行的 SQL 语句。 Linq to SQL 将不会运行查询,直到调用 ToArray 或 ToList 之类的方法,或者直到在 foreach 循环或类似循环中使用它。

此外,当您使用 return 语句时,像这样使用匿名类型会变得很困难。您可能需要创建 DTO 类并使用选择投影来实例化它们

其次,您将上下文包装在一个 using block 中(这是一个很好的做法),但是如果您在实际执行之前返回查询,那么上下文将被释放。调用者将获得一个 IQueryable,但当它尝试使用它时,您将以异常结束,因为上下文已被释放。

所以....这里有两个选项,具体取决于这段代码是要返回实际数据,还是只返回一个查询,调用者可以进一步修改。

案例一)返回数据:

public async Task<object> DoThings(CancellationToken token)
{
    var keywordlistquery = await Task.Run(() =>
    {
        using (var context = new DataClasses1DataContext())
        {
            context.ObjectTrackingEnabled = false;
            return from keyword in context.GetTable<KeywordsList>()
                    select new
                    {
                        keyword.search_text,
                        keyword.search_keyword
                    };
        }
    }, token);
    return keywordlistquery;
}

请注意,方法本身应该是异步的,并且您应该尽可能尝试使用取消 token 。这会调用 ToArray 以强制查询立即执行并返回数据。请记住,这将返回整个表。如果调用者想要提供 where 子句或其他任何内容,代码仍将加载所有数据。

情况 2:返回 IQuerable

在情况 2 中,您希望您的方法只返回查询。这样,调用者可以在查询执行之前修改查询。这允许调用者添加语句以包含 where 子句或对结果进行排序等等;并将这些语句包含在生成的 TSQL 中。

在这种情况下,诀窍在于调用者必须控制数据上下文的生命周期,并且由于该方法实际上并不执行结果,因此它不需要异步。

public async Task CallingMethod()
{
    using (var context = new DataClasses1DataContext())
    {
        var token = new CancellationToken();
        context.ObjectTrackingEnabled = false;
        var query = DoThings(context);
        var result = await Task.Run(() => query.ToArray(),  token);
    }
}

public IQueryable<object> DoThings(DataContext context)
{
    var keywordlistquery = from keyword in context.GetTable<KeywordsList>()
        select new
        {
            keyword.search_text,
            keyword.search_keyword
        };
    return keywordlistquery;
}

不过正如我之前提到的,select new anonynous 在这种情况下效果不佳。最好创建一个 DTO 类并从中选择一个新类,或者返回整个表。

关于linq-to-sql - 通过在 Task.Run() 中包装 linq 查询,在多线程中进行 linq to sql,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39016876/

相关文章:

linq-to-sql - Linq 到 Sql : handle NewID()

linq-to-sql - 使用Linq-to-SQL插入通过反射确定的对象

c# - 在 SQL to LINQ 中使用 SP 时从 SQL Server 检索错误消息

c# - 如何在 orderby 子句中使用带有动态参数的 LINQ

Linq-to-Sql 计数

linq - 如何在linq中获取第二条记录到sql

c# - Linq2Sql : changing "delete" extensibility method to update a record

asp.net-mvc - Kigg MVC 应用程序是否干燥?我们可以调整存储库吗

c# - Linq-to-sql 外键 id 未自动添加

sql - 为具有自定义日期/时间格式的数据库实现 LINQ to SQL 表达式