.net 3.5 数据库开发 之 LINQ to SQL

 

图1 LINQ to SQL 框架  

 

  

     在.net 3.5推出之后,微软的数据库访问技术出现了两个ORM映射。LINQ to SQL,主要针对SQL Server数据库,和ADO.net Entity Framework,支持多种数据库。很多时候,我们要考虑数据库平台的可移植性,即现在程序可能运行在SQL Server上,但根据需要,也许有一天,我们要将它一直到Oracle上,在考虑到这种问题的时候,我们首选ADO.net Entity Framework。当然选择LINQ to SQL,还是ADO.net Entity Framework的依据不止这一个:

        LINQ to SQL针对SQL Server用户,期望能有一个快速开发、简单的ORM技术的群体。开发的系统多半是中小型应用程序,采用轻量化开发模式,不需要繁复的开发过程。还有一种情况:如果开发的系统中,不需要将同一个Entity Class对应到多个表,也就是说程序中写入一条数据,而该数据不会写入一个以上的表,那么可以选择LINQ to SQL

        ADO.net Entity Framework则针对使用SQL Server开发大型应用程序的用户,需要一整套数据库软件来管理。以及非SQL Server数据库的用户。在开发的系统中,如果需要将同一个Entity Class对应至多个表,那么ADO.net Entity Framework是目前唯一的选择。

    LINQ to SQL 有着自身的优势,由于LINQ是一个轻量化的ORM产品,性能在其设计之初是考虑重点之一,所以LINQ to SQL的性能是很让人满意的,而和其相对的ADO.net Entity Framework由于架构的复杂,在性能方面就会略逊一筹。

 

     在LINQ to SQL中,通过Visual Studio2008自带的设计器,IDE会根据用户添加的数据表,自动生成对应的数据库类和对应表的类。应用程序将DataContext(与一个实体数据库对应)中的Table<Entity class>(与实体表的一行对应)作为数据源,通过LINQ Expression进行访问,LINQ to SQL Provider会利用内建的Query Convert机制,将LINQ Expression转换为SQL指令封装于回传的DataQuery对象中,当第一次对DataQuery对象调用MoveNext是,这个SQL指令会经由ADO.net送到数据库执行后取回回传的结果,并将结果集中的元素一一转为Entity Object.

 

     在之前已经介绍了LINQ(包括LINQ to Entity),由于LINQ to SQL使用LINQ Expression,所以LINQ to SQL的操作和LINQ to Entity的操作相似,不同的是两者的数据源分别为数据库和对象集合。除此之外,LINQ to SQL 和 LINQ to Entity还有些地方不同:

受Query Convert机制(将LINQ Expression转换为SQL指令)的限制,原本可以使用的Extension Method或是自定义函数不能运用于LINQ to SQL中,如:

程序 4-1

static void Main(string[] args)

{

     NorthwindDataContext context = new NorthwindDataContext();

     var result1 = from s in context.Customers

                             where Check(s)

                             select s;

     foreach (var item in result1)

     {

          Console.WriteLine(item.CustomerID);

     }

     Console.ReadLine();

}

static bool Check(Customer item)

{

     return true;

}

程序4-1虽然能编译通过,但在执行时将会引发NotSupportException异常,这是因为运行是Query Convert不知道如何将Check转换为SQL指令的缘故,要让程序能正常运行,需要先调用ToArray将数据库中数据转化为对象进行操作。

 程序4-2

static void Main(string[] args)

{

     NorthwindDataContext context = new NorthwindDataContext();

     var result1 = from s in

                             (from s in context.Customers select s).ToList()

                             where Check(s)

                             select s;

     foreach (var item in result1)

     {

          Console.WriteLine(item.CustomerID);

     }

     Console.ReadLine();

}

 

下面,我主要介绍一些比较容易忽视的问题:

1、连接模式

由于LINQ to SQL底层采用ADO.net实现,所以,LINQ to SQL的查询有两种模式。连线模式和离线模式。在查询过程中,LINQ to SQL默认采用连接模式。当调用ToList(), ToArray()等的时候,程序会自动切换到离线模式。但下面的示例看起来是应该有错误的,实际上却可以正确执行,为什么呢?

程序4-3

NorthwindDataContext context = new NorthwindDataContext();

var result1 = from s in context.Customers

                       select s;

var result2 = from s in context.Orders

                       select s;

 

foreach (var item in result1)

{

        foreach (var item2 in result2)

        {

                break;

        }

}

我们知道,两个查询使用的是同一个DataContext,也是相同的Connection。如果采用连线模式,当第一个查询连接时,第二个查询执行将会抛出异常,而这里却可以正确执行为什么?

原来在LINQ to SQL中有一种Connection管理机制。在DataContext的管理机制中,每一个foreach都是一个User,当一个User正在执行时,另一个User(嵌套的foreach)尝试执行,就会激发Buffer机制。Buffer机制激活时,第一个User所关联的DataReader会将所有数据放入Buffer中,此时Buffer中就有了一个DataTable,然后DataReader被关闭,对应的User被移除。此时第二个User再执行连接,就不会出错了。

Buffer机制只对同一个DataContext有效,对于不同的DataContext,不会激活Buffer机制。所以我们建议尽量通过ToArray等方法将连接模式转换为离线模式进行操作。

LINQ to SQL执行查询时,由于默认采用连线模式,所以,当回传程序集的Enumerator调用MoveNext时,连接打开,直至Enumerator执行结束,连接才会关闭。以foreach为例,当foreach取出集合的第一个元素时,连接打开,当foreach执行结束时,连接关闭。

 

2 查询执行方式

程序4-4

static void Query()

{

NorthwindDataContext context = new NorthwindDataContext();

var first = FirstQuery(context);

var result   = SecondQuery(context, first);

foreach ( var item in result)

{

        Console.WriteLine(item.CustomerID);

}

}

 

static IEnumerable<Customer> FirstQuery(NorthwindDataContext context)

{

        IEnumerable<Customer> result = from s in context.Customers

                                                                     select s;

        return result;

}

 

static IEnumerable<Customer> SecondQuery(NorthwindDataContext context,

                                                                               IEnumerable<Customer> first);

{

        var result = from s in first

                             where s.CustomerID == "VINET";

        return result;

}

 

        当调用Query()方法进行断点调试时,可以看到,程序在执行FirstQuery时,会将LINQ Expression转化为标准SQL语句,而当程序执行SecondQuery时,却没有看到SQL语句,为什么呢?

        原因很简单,当SecondQuery执行时,参数first的类型为IEnumerable,程序执行到where处时,会调用IEnumerableWhere方法,这是程序是将IEnumerable对象作为数据源,所以编译器将LINQ Expression转化成LINQ to Objects进行调用了。

如果将程序改成:

程序4-5 

static IQueryable<Customer> FirstQuery(NorthwindDataContext context)

{

        IQueryable<Customer> result = from s in context.Customers

                                                                     select s;

        return result;

}

 

static IQueryable<Customer> SecondQuery(NorthwindDataContext context,

                                                                               IQueryable<Customer> first);

{

        var result = from s in first

                             where s.CustomerID == "VINET";

        return result;

}

此时的情况就不同了。这样在调试时,就可以看到两个方法中LINQ Expression都转成了SQL语句。

也就是说,当要将LINQ的回传值进行回传,或者作为参数传递给另一个函数时,类型的选择会直接影响编译器的Extension Method的调用,如果不注意,可能会造成难以察觉的错误。

 

3Translate DataReader加载Entity Objects

程序4-6

ContractedBlock.gif ExpandedBlockStart.gif Code 4-6
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", conn))

{

        conn.Open();

        
using (SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior,CloseConnection))

        {

                var result 
= context.Translate<Customers>(reader);

                
foreach (var item in result)

                {

                         Console.WriteLine(item.CustomerID);

                 }

        }



 

4Deferring Load

当一次性的将OrderOrderDetail(两个之间存在Association)中的行取回,当访问Order时,它的明细信息并没有加载,只有在访问OrderDetail的属性时,明细才会被加载,这时每访问一个OrderDetail属性,程序就会发送一次SQL语句到数据库,这种方式成为Deffering Load

这种方式好不好呢?这样根据情况来定,如果数据很多的话,这样可以很大的节省内存空间,而如果数据很少,这种方式的效率就相对较低了。所以要根据实际情况进行选择。

如何关闭Deferring Load功能:

程序4-7

NorthwindDataContext context = new NorthwindDataContext();

context.DeferredLoadingEnable = false;

System.Data.Linq.DataLoadOptions loadOptions =

                         new System.Data.Linq.DataLoadOptions();

loadOptions.LoadWith<Orders>(o => o.Order_Details);

context.LoadOptions = loadOptions;

 

var result = from s1 in context.Orders

                     where s1.OrderDate > DateTime.Parse("2009/03/04")

                     select s1;

先设置DataContextDeferredLoadingEnable属性为false,关闭DeferringLoad功能

然后创建DataLoadOptions对象,通过调用此对象的LoadWithAssociateWith来指定哪些Entity Object要一次性加载那一个关联相关的Entity Object

注意:1.不能在指定为DataContextLoadOptions属性后再调用LoadWith函数:

context.LoadOptions.LoadWith<Orders>(o => o.Order_Details);   是错误的

2.除了LoadWith函数外,之前也提过LoadOptions还有另外一个方法AssociateWith,此方法可以指定关联Entity Object时的过滤,可以和LoadWith搭配使用:

loadOptions.LoadWith<Orders>(o => o.Order_Details);

loadOptions.AssociateWith<Orders>(o => o.Order_Details.Where( p => p.productID == 60);

context.LoadOptions = loadOptions;

3.使用LoadWith时,不能出现循环的情况,如

loadOptions.LoadWith<Orders>( o => o.Order_Details);

loadOptions.LoadWith<Order_Details>(o => o.Orders);   //这种做法是错误的

 

5.继承

Entity Oject 继承是ORM不可或缺的技术之一。具体的解释如下:

有三个类,PeopleMaleFemale,数据库中有一张Persons表中有NameSexAge字段,这三个类可以都和Persons表关联,以Sex属性进行区分,当sex = 1时,对应的实体为malesex=2时,对应实体为femalesex = 0 则为people。这时,我们可以设置MaleFemale继承自People,而PeopleNameSexAge属性。其中Sex属性值就被成为“Discriminator Property”,而区分实体为PeopleSex属性值成为“Basic class DiscriminatorProperty Value”,而区分实体为MaleFemaleSex属性值成为“Derived Class DiscriminatorProperty Value”。当实体的Sex属性值既不属于Male,也不属于FemalePeople时,那么该属性值就视“Inheritance Default”属性值所设置的实体而定。

 

.net 3.5 数据库开发 之 ADO.net Entity Framework

 

转载于:https://www.cnblogs.com/Lucen/archive/2009/03/05/1404059.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值