如何使用此Linq2Sql减少db往返次数?

时间:2008-12-16 13:19:48

标签: .net linq-to-sql

我有以下的Linq2Sql并且它正在为我的'SELECT'语句进行多次往返。我不知道为什么。首先是代码,然后是解释: -

from p in db.Questions
select new Models.Question
{
    Title = p.Title,
    TagList = (from t in p.QuestionTags
               select t.Tag.Name).ToList()
}

现在数据库是

  

问题< -one to many-> QuestionTags< -many to one-> Tag

所以一个问题有一对多的标签,中间有一个链接表。这样,我可以多次重复使用标签。 (如果有的话,我会对更好的架构持开放态度。)

执行此操作会执行Linq2Sql生成的以下Sql代码

SELECT [t0].[QuestionId] AS [ID], etc....  <-- that's the good one

exec sp_executesql N'SELECT [t1].[Name]
FROM [dbo].[QuestionTags] AS [t0]
INNER JOIN [dbo].[Tags] AS [t1] ON [t1].[TagId] = [t0].[TagId]
WHERE [t0].[QuestionId] = @x1',N'@x1 int',@x1=1

第二个sql块被列为2x ..我认为这是因为第一个sql块返回了两个结果,所以第二个sql块会从第一个结果中触发每个结果。

有什么方法可以使这个sql语句而不是1 + n,其中n =第一个查询的结果数?

更新

我尝试了Eager和Lazy加载,没有区别。

DataLoadOptions dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Question>(x => x.QuestionTags);
dataLoadOptions.LoadWith<QuestionTag>(x => x.Tag);
db.LoadOptions = dataLoadOptions;

5 个答案:

答案 0 :(得分:5)

ToList()肯定会阻止你。您应该对整个查询执行ToList()。

我认为你可以做的另一件事是使用“let”。我认为在这种情况下,它可以创建延迟执行并包含在表达式树中,但是YMMV。

from p in db.Questions
let Tags = (from t in p.QuestionTags
               select t.Tag.Name)
select new Models.Question
{
    Title = p.Title,
    TagList = Tags
}

答案 1 :(得分:2)

这可能是LINQ本身不够的情况之一。您是否考虑过将此逻辑编写为UDF或SPROC,并简单地使用LINQ来调用它? LINQ-to-SQL非常擅长调用(实体框架在UDF中不是很好)。

然后你可以在数据库中进行标记组合,并将其作为varchar返回,例如。没有游标就有一个TSQL技巧:

DECLARE @foo varchar(max)
SET @foo = ''
SELECT @foo = @foo + [SomeColumn] + ',' -- CSV
FROM [SomeTable]
WHERE -- some condition

(也许删除尾随的逗号)

运行此操作后,@foo将成为值的CSV - 如果您返回单行,则非常有效。如果你要返回多个主要行,那就不那么好了。

答案 2 :(得分:2)

您可以尝试在此对象关联上配置Eager Loading。类似的东西:

        var dlo = new DataLoadOptions();
        // Configure eager loading
        dlo.LoadWith<Question>(q => q.QuestionTags);

        _context = new WhateverContext();
        _context.LoadOptions = dlo;

但您可能需要稍微重构一下代码。基本上你告诉框架发出SQL来引入更广泛的对象图,而不是等到访问对象关联(延迟加载是默认的)。

也许看(http://blog.codeville.net/2007/12/02/linq-to-sql-lazy-and-eager-loading-hiccups/)。顺便说一句不同的史蒂文!

答案 3 :(得分:0)

我想问题是你应该为整个查询调用.ToList()。这将从db。中返回整个集合。

在您的情况下,第一个SQL命令仅返回所有问题的ID,然后,每个问题都会发生单个SQL调用(在foreach循环中迭代期间) - 请参阅@ x1 param。

答案 4 :(得分:0)

你可以像这样进行延迟加载:

from p in db.Questions
let Tags = GetTags(Questions.Id)
select new Models.Question
{
    Title = p.Title,
    TagList = LazyList<string>(Tags)
}

public IQueryable<string> GetTags(int questionId) {
    from qt in db.QuestionTags
    join t in db.Tags on qt.TagId equals t.Id
    where qt.questionId = questionId
    select t.Name
}

LazyList是一个实现IList的IQueryable容器。一旦枚举了TagList属性,就会执行存储在里面的IQueryable。

LazyList类由Rob Connery编写,可在此处找到:http://blog.wekeroad.com/blog/lazy-loading-with-the-lazylist/