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

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

2010051708533366.png

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

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

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语句:

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出来,如下代码:

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:

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公斤的学生的信息,也就是说在取得关联表数据时附加了条件,请看下面的代码及注释:

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语句:

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查询优化,需要忧虑性能吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值