LINQ to SQL - DataLoadOptions

[ 2007-08-25 21:26:20 | 作者: yuhen ]
字号: | |
我们先看下面这个 One-to-Many 显示的例子。

uploads/200708/25_212708_snap1.gif

using (DataClasses1DataContext context = new DataClasses1DataContext())
{
    context.Log = Console.Out;

    var groups = from g in context.Groups select g;
    foreach (var group in groups)
    {
        Console.WriteLine("Group:{0}", group.Name);

        foreach (var user in group.Users)
        {
            Console.WriteLine("\tUser:{0}", user.Name);
        }
        
        Console.WriteLine("--------------");
    }
}

输出(显示结果被调整过,便于查看)
SELECT [t0].[Id], [t0].[Name]
FROM [dbo].[Group] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

SELECT [t0].[Id], [t0].[Name], [t0].[Age], [t0].[GroupId]
FROM [dbo].[User] AS [t0]
WHERE [t0].[GroupId] = @p0
-- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [7]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

Group:group1
        User:user1
        User:user2
--------------
SELECT [t0].[Id], [t0].[Name], [t0].[Age], [t0].[GroupId]
FROM [dbo].[User] AS [t0]
WHERE [t0].[GroupId] = @p0
-- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [8]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

Group:group2
        User:user3
        User:user4
--------------

从输出结果中,我们发现遍历 group.Users 时会导致多次获取 User 的 SQL 执行,很显然这种方式存在性能问题,如何优化呢?用存储过程?还是……
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
    context.Log = Console.Out;

    DataLoadOptions options = new DataLoadOptions();
    options.LoadWith<Group>(g => g.Users);
    context.LoadOptions = options;

    var groups = from g in context.Groups select g;
    foreach (var group in groups)
    {
        Console.WriteLine("Group:{0}", group.Name);

        foreach (var user in group.Users)
        {
            Console.WriteLine("\tUser:{0}", user.Name);
        }
        
        Console.WriteLine("--------------");
    }
}

输出
SELECT [t0].[Id], [t0].[Name], [t1].[Id] AS [Id2], [t1].[Name] AS [Name2], [t1].[Age], [t1].[GroupId], (
    SELECT COUNT(*)
    FROM [dbo].[User] AS [t2]
    WHERE [t2].[GroupId] = [t0].[Id]
    ) AS [count]
FROM [dbo].[Group] AS [t0]
LEFT OUTER JOIN [dbo].[User] AS [t1] ON [t1].[GroupId] = [t0].[Id]
ORDER BY [t0].[Id], [t1].[Id]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

Group:group1
        User:user1
        User:user2
--------------
Group:group2
        User:user3
        User:user4
--------------

DataContext 通过 DataLoadOptions.LoadWith<T>() 来 "提示" LINQ to SQL "一次性" 载入所需的全部数据。

创建 DataLoadOptions 对象时有点小细节需要注意,如果我们按下面这种方式写的话,会导致异常。
context.LoadOptions = new DataLoadOptions();
context.LoadOptions.LoadWith<Group>(g => g.Users);

uploads/200708/25_212712_snap2.gif


我们还可以使用 DataLoadOptions.AssociateWith<T>() 对需要载入的 User 做出条件限制。
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
    context.Log = Console.Out;

    DataLoadOptions options = new DataLoadOptions();
    options.LoadWith<Group>(g => g.Users);
    options.AssociateWith<Group>(g => from u in g.Users where u.Age < 14 select u);
    context.LoadOptions = options;

    var groups = from g in context.Groups select g;
    foreach (var group in groups)
    {
        Console.WriteLine("Group:{0}", group.Name);

        foreach (var user in group.Users)
        {
            Console.WriteLine("\tUser:{0}, Age:{1}", user.Name, user.Age);
        }
        
        Console.WriteLine("--------------");
    }
}

输出
SELECT [t0].[Id], [t0].[Name], [t1].[Id] AS [Id2], [t1].[Name] AS [Name2], [t1].[Age], [t1].[GroupId], (
    SELECT COUNT(*)
    FROM [dbo].[User] AS [t2]
    WHERE ([t2].[Age] < @p0) AND ([t2].[GroupId] = [t0].[Id])
    ) AS [count]
FROM [dbo].[Group] AS [t0]
LEFT OUTER JOIN [dbo].[User] AS [t1] ON ([t1].[Age] < @p0) AND ([t1].[GroupId] = [t0].[Id])
ORDER BY [t0].[Id], [t1].[Id]
-- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [14]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

Group:group1
        User:user1, Age:13
--------------
Group:group2
        User:user3, Age:13
--------------

要是反过来,Many-to-One 会如何?
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
    context.Log = Console.Out;

    var options = new DataLoadOptions();
    options.LoadWith<User>(u => u.Group);
    context.LoadOptions = options;

    var user = (from u in context.Users select u).FirstOrDefault();
    Console.WriteLine(user.Name);
    Console.WriteLine(user.Group.Name);
}

输出
SELECT TOP 1 [t0].[Id], [t0].[Name], [t0].[Age], [t0].[GroupId], [t2].[test], [t2].[Id] AS [Id2], 
    [t2].[Name] AS [Name2]
FROM [dbo].[User] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[Id], [t1].[Name]
    FROM [dbo].[Group] AS [t1]
    ) AS [t2] ON [t2].[Id] = [t0].[GroupId]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

user1
group1

[lol] 同样有效。OK~~~ 继续看看更复杂点的测试。

uploads/200708/26_112319_snap1.gif

using (DataClasses1DataContext context = new DataClasses1DataContext())
{
    context.Log = Console.Out;

    DataLoadOptions options = new DataLoadOptions();
    options.LoadWith<Group>(g => g.Users);
    options.LoadWith<User>(u => u.Articles);
    context.LoadOptions = options;

    var groups = from g in context.Groups select g;
    foreach (var group in groups)
    {
        Console.WriteLine("Group:{0}", group.Name);

        foreach (var user in group.Users)
        {
            Console.WriteLine("\tUser:{0}", user.Name);
            foreach (var article in user.Articles)
            {
                Console.WriteLine("\t\tArticle:{0}", article.Title);
            }
        }

        Console.WriteLine("--------------");
    }
}

输出
SELECT [t0].[Id], [t0].[Name]
FROM [dbo].[Group] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

SELECT [t0].[Id], [t0].[Name], [t0].[Age], [t0].[GroupId], [t1].[Id] AS [Id2], 
[t1].[Title], [t1].[Content], [t1].[UserId], (
    SELECT COUNT(*)
    FROM [dbo].[Article] AS [t2]
    WHERE [t2].[UserId] = [t0].[Id]
    ) AS [count]
FROM [dbo].[User] AS [t0]
LEFT OUTER JOIN [dbo].[Article] AS [t1] ON [t1].[UserId] = [t0].[Id]
WHERE [t0].[GroupId] = @x1
ORDER BY [t0].[Id], [t1].[Id]
-- @x1: Input Int32 (Size = 0; Prec = 0; Scale = 0) [7]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

Group:group1
        User:user1
                Article:title1
        User:user2
                Article:title2
--------------
SELECT [t0].[Id], [t0].[Name], [t0].[Age], [t0].[GroupId], [t1].[Id] AS [Id2], [t1].[Title], 
    [t1].[Content], [t1].[UserId], (
    SELECT COUNT(*)
    FROM [dbo].[Article] AS [t2]
    WHERE [t2].[UserId] = [t0].[Id]
    ) AS [count]
FROM [dbo].[User] AS [t0]
LEFT OUTER JOIN [dbo].[Article] AS [t1] ON [t1].[UserId] = [t0].[Id]
WHERE [t0].[GroupId] = @x1
ORDER BY [t0].[Id], [t1].[Id]
-- @x1: Input Int32 (Size = 0; Prec = 0; Scale = 0) [8]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

Group:group2
        User:user3
                Article:title3
        User:user4
                Article:title4
--------------

[cry] 没效果~~ 反过来呢?
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
    context.Log = Console.Out;

    DataLoadOptions options = new DataLoadOptions();
    options.LoadWith<Article>(a => a.User);
    options.LoadWith<User>(u => u.Group);
    context.LoadOptions = options;

    var article = (from a in context.Articles select a).FirstOrDefault();
    Console.WriteLine(article.Title);
    Console.WriteLine(article.User.Name);
    Console.WriteLine(article.User.Group.Name );
}

输出
SELECT TOP 1 [t0].[Id], [t0].[Title], [t0].[Content], [t0].[UserId], [t4].[test], [t4].[Id] AS [Id2], 
    [t4].[Name], [t4].[Age], [t4].[GroupId], [t4].[test2], [t4].[Id2] AS [Id3], [t4].[Name2]
FROM [dbo].[Article] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[Id], [t1].[Name], [t1].[Age], [t1].[GroupId], [t3].[test] AS [test2], 
        [t3].[Id] AS [Id2], [t3].[Name] AS [Name2]
    FROM [dbo].[User] AS [t1]
    LEFT OUTER JOIN (
        SELECT 1 AS [test], [t2].[Id], [t2].[Name]
        FROM [dbo].[Group] AS [t2]
        ) AS [t3] ON [t3].[Id] = [t1].[GroupId]
    ) AS [t4] ON [t4].[Id] = [t0].[UserId]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

title1
user1
group1

这次倒是有效,就是不知道这么折腾,"性能" 真的如我们预期的那样提高吗? [confused]
[最后修改由 yuhen, 于 2007-08-26 11:26:13]
评论Feed 评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=546

这篇日志没有评论。

发表评论
表情图标
[smile] [confused] [cool] [cry]
[eek] [angry] [wink] [sweat]
[lol] [stun] [razz] [redface]
[rolleyes] [sad] [yes] [no]
[heart] [star] [music] [idea]
UBB代码
转换链接
表情图标
悄悄话
用户名:   密码:  
验证码 * 请输入验证码