打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
LINQ to SQL系列三 使用DeferredLoadingEnabled,DataLoadOption指定加载选项

本文中举例用到的数据模型如下:

Student和Class之间是多对一关系,Student和Course之间是多对多关系。

DataContext的DeferredLoadingEnabled属性指定是否需要延时加载,其默认值为true。以Student为例,其延时加载的对象是指Class和对应的Course。设定延时加载为true时,当访问到Student实例的Class属性或者StudentCourse属性时会自动加载Class表和StudentCourse表中的数据,如下示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(string[] args)
{
    using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))
    {
        using (DbAppDataContext db = new DbAppDataContext())
        {
            db.Log = writer;
 
            //设置延时加载属性为true
            db.DeferredLoadingEnabled = true;
            //获得一个Student
            var aStudent = db.Students.First();
            //直接访问Student的Class属性ClassName
            Console.WriteLine("{0}属于{1}",aStudent.Name ,aStudent.Class.ClassName);
        }
    }
    Console.ReadLine();
}

当设置DataContext的DeferredLoadingEnabled属性为true时,可以直接访问关系表中的数据,我们可以看下以上代码使用到的SQL语句:

1
2
3
4
5
6
7
8
9
SELECT TOP (1) [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]
FROM [dbo].[Student] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1
 
SELECT [t0].[ClassID], [t0].[ClassName]
FROM [dbo].[Class] AS [t0]
WHERE [t0].[ClassID] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

用到了两个sql语句,在我们访问到Student的Class属性时,DataContext自动去加载了Class的数据。

在有些时候可以将DeferredLoadingEnabled属性设置为false,设置为false时再直接访问Student的Class属性时将抛出空引用的异常。

有了延时加载,LoadWith方法是做什么用的呢?

MSDN上对LoadWith的解释是:通过使用 lambda 表达式检索与主目标相关的指定数据。

LoadWith不是DataContext的方法,而是DataLoadOptions的方法,可以给DataContext设定LoadOptions属性来改变DataContext加载数据的方式;换句话说是LoadOptions设定是否在select主表数据时同时用join加载关联表的数据。

假定场景:我希望在select Student表的数据时同时用一个sql将其class的属性select出来,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void Main(string[] args)
{
    using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))
    {
        using (DbAppDataContext db = new DbAppDataContext())
        {
            db.Log = writer;
 
            //声明DataLoadOptions变量
            var loadOptions = new DataLoadOptions();
            //设定需要在load Student时需要同时load什么
            loadOptions.LoadWith<Student>(s=>s.Class);
 
            //将DataLoadOptions实例赋值给db.LoadOptions
            db.LoadOptions = loadOptions;
            //加载一个Student
            var student = db.Students.First();
 
            Console.WriteLine("{0}属于{1}",student.Name,student.Class.ClassName);
        }
    }
    Console.ReadLine();
}

其效果和上面例子完全一样,但是执行的SQL却是不一样的,我们看下SQL:

1
2
3
4
SELECT TOP (1) [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc], [t1].[ClassID] AS [ClassID2], [t1].[ClassName]
FROM [dbo].[Student] AS [t0]
INNER JOIN [dbo].[Class] AS [t1] ON [t1].[ClassID] = [t0].[ClassID]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

这次执行只用了一个SQL语句,Student表inner join Class表,这就是LoadWith的意义所在。

LoadWith可以用一个sql语句加载相关表的数据,那么AssociateWith 方法又是做什么用的呢?

还是假定一个场景,我们想知道某个班级的信息和这个班级中体重大于30公斤的学生的信息,也就是说在取得关联表数据时附加了条件,请看下面的代码及注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static void Main(string[] args)
{
    using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))
    {
        using (DbAppDataContext db = new DbAppDataContext())
        {
            db.Log = writer;
 
            var loadOptions = new DataLoadOptions();
 
            //在加载Class的Students属性时附加上体重大于30的条件
            loadOptions.AssociateWith<Class>(c => c.Students.Where(s => s.WeightInKg > 30));
            db.LoadOptions = loadOptions;
 
            //将DataLoadOptions实例赋值给db.LoadOptions
            db.LoadOptions = loadOptions;
            //取得id为1的班级
            var aClass = db.Classes.Where(c=>c.ClassID == 1).Single();
 
            Console.WriteLine("{0}体重大于30kg的同学有:",aClass.ClassName);
            foreach (var item in aClass.Students)
            {
                Console.WriteLine("\t{0}",item.Name);
            }
        }
    }
    Console.ReadLine();
}

同样看下代码执行的真实SQL语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SELECT [t0].[ClassID], [t0].[ClassName]
FROM [dbo].[Class] AS [t0]
WHERE [t0].[ClassID] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1
 
SELECT [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]
FROM [dbo].[Student] AS [t0]
WHERE ([t0].[WeightInKg] > @p0) AND ([t0].[ClassID] = ((
    SELECT [t2].[ClassID]
    FROM (
        SELECT TOP (1) [t1].[ClassID]
        FROM [dbo].[Class] AS [t1]
        WHERE [t1].[ClassID] = @p1
        ) AS [t2]
    )))
-- @p0: Input Float (Size = -1; Prec = 0; Scale = 0) [30]
-- @p1: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

可以看到是两个sql语句,第二个sql语句中AssociateWith起了作用,第一个条件是体重大于@p0,第二个条件是classID;可以看到这儿生成的sql语句很不好,ClassID是唯一主键,按理说DataContext已经知道这事儿,但是在生成语句时还是用了嵌套的查询,可以说是LINQ to SQL的一个败笔。

linq to sql相关随笔:

1.  从CUD开始,如何使用LINQ  to SQL插入、修改、删除数据

2.  查询 使用LINQ to SQL做简单查询

3.  查询 延迟加载与立即加载,使用LoadWith和AssociateWith

4.  查询 inner join,left outer join

5.  Linq to SQL中的聚合grouping having

6.  LINQ to SQL查询优化,需要忧虑性能吗?

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
SQL查询入门(中篇)
SQL语句映射文件(1)resultMap
编写SQL语句查询出每个各科班分数最高的同学的名字,班级名称,课程名称,分数
PowerDesigner逆向工程生成PDM模型及数据库
Mybatis_Day02
MyBatis动态SQL标签用法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服