【IT168技术文档】
1. 什么是LINQ
在C#3.0中提出了诸如Lambda表达式、查询语法等一系列新的特性,这些新的特性帮助我们将查询数据理解为一个类编程的理念。我们称这个总的查询编程模型为“LINQ”--它指的是.NET 语言集合查询。LINQ 支持这样一个非常广泛的的扩展模型:该模型是针对不同的数据源而生成不同的高效的操作因子。.NET Framework 3.5版本内嵌了LINQ语言对对象,XML、实体、数据集和数据库的支持的词典,它所要解决的就是对象不等于数据的问题。LINQ体系结构图如下:

2. 什么是LINQ to SQL
通过上图我们已经可以看到了,LINQ to SQL其实就是LINQ对于数据库的支持,它是O/RM(对象关系映射)在.NET Framework 3.5中的一种实现,它允许你用.NET 的类来生成一个关系型的数据库。然后你可以用LINQ对从该对象中对数据库进行查询,更新/插入/删除。LINQ to SQL完全支持事务,视图和存储过程。它还提供了一种方便地在你的数据模型中对集合数据验证和业务逻辑规则的进行验证的方法。
在LINQ to SQL中,我们可以像下面的代码这样编写查询:
Code1:
AdventureWorksDWDataContext db = new AdventureWorksDWDataContext();在正式学习LINQ to SQL之前,我们需要先来熟悉一下C#3.0中的一些新特性,这些新的特性也是为了LINQ而产生的。
IEnumerable<DimCustomer> customers = from c in db.DimCustomer
where c.CustomerAlternateKey == "AW00011001"
select c;
3. 隐含类型局部变量
这是一个语言层面的功能,局部变量被声明为var,这个声明被视为隐含类型局部变量声明,然后由编译器根据局部变量初始化表达式来推断变量的类型。如下面的声明:
Code2:
var integer = 10;要注意的是var在这里是仅仅是一个关键字,它并不是C#3.0中的一种新的类型,而是负责告诉编译器,该变量需要根据初始化表达式来推断变量的类型,上面的语句相当于:
var name = "Terry Lee";
var numbers = new int[] { 1, 2, 3 };
int integer = 10;在Code2中,变量integer仍然具有强类型,可以做如下测试:
string name = "Terry Lee";
int[] numbers = new int[] { 1, 2, 3 };
Code4:
var integer = 10;编译时会报Cannot implicitly convert type 'string' to 'int'错误。
integer = "Terry Lee";
在这里我们将变量类型的推断任务交给了编译器,那就需要在声明的时候为变量赋值,也不能赋值为null,否则编译器也是无法推断的。另外,var只能用于局部变量,而不能用于字段和属性。
4. 自动属性
自动属性仍然是一个语言层面的功能,它可以使我们不用编写get/set操作以及字段,这一切都由编译其来完成。这样可以简化我们的代码,使我们的代码看起来更加的优雅。如下代码片断所示:
Code5:
public class Customer
{
public String Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
public class Demo
{
public static void Main()
{
Customer c = new Customer();
c.Name = "TerryLee";
}
}
在自动属性中,我们可以为get/set操作使用不同的访问级别,也就是说你可以这样去写:
Code6:
public String Name5. 对象初始化器
{
get;
protected set;
}
对象初始值主要允许在单一表达式中为多个属性或字段赋值,例如我们有一个Customer类:
Code7:
public class Customer平时我们创建对象的常见模式是:
{
public String Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
Code8:
public class Demo这样对每一个属性进行赋值,我们需要写很多行代码,而使用了对象初始化器之后,可以这样来写代码:
{
public static void Main()
{
Customer c = new Customer();
c.Name = "TerryLee";
c.Age = 24;
}
}
Code9:
public class Demo这里需要注意的几个地方:一是对象初始化器允许只对其中的一部分对象进行赋值;二是允许和构造函数同时结合使用,并且构造函数的优先级高于对象初始化器;三是对象初始化器允许省略构造函数的括号。如下面的代码所示:
{
public static void Main()
{
Customer c = new Customer() { Name = "TerryLee",Age = 24};
}
}
Code10:
public class Demo
{
public static void Main()
{
Customer c1 = new Customer("TerryLee",24) { Name = "TerryLee New"};
Customer c2 = new Customer{ Name = "TerryLee New"};
}
}
6. 集合初始化器
集合初始化器类似于我们上面所说的对象的初始化器,由编译器来为我们完成插入集合的操作,这里不再细说,如下示例代码:
public class Demo7. 匿名类型
{
public static void Main()
{
var customer = new List<Customer>
{
new Customer{Name = "TerryLee", Age = 24},
new Customer{Name = "lhj", Age = 25}
};
}
}
匿名类型允许在无需指定名称的情况下创建结构化类型,经常和var关键字配合使用。如下面的代码段所示:
Code12:
public class Demo以上代码会隐式创建一个具有Name、Age字段的类型:
{
public static void Main()
{
var temp = new { Name = "TerryLee", Age = 24 };
Console.WriteLine("Name:{0} Age:{1}", temp.Name, temp.Age);
}
}
Code13:
class可以看到,隐式创建的类型并没有名称,所以你不能通过名称引用以上类型。
{
public String Name;
public int Age;
}
8. 扩展方法
扩展方法允许开发人员往一个现有的CLR类型的公开契约(contract)中添加新的方法,而不用生成子类或者重新编译原来的类型。扩展方法有助于把今天动态语言中流行的对duck typing的支持之灵活性,与强类型语言之性能和编译时验证融合起来。扩展方法除了在LINQ有助于简化我们的查询示例之外,还是一种广泛有用的语言功能,其中一个最常见的用途可能是提供共享接口实现。例如,假设我们有以下接口:
Code14:
interface ILog
{
// 默认的
void Write();
void Write(String msg);
}
在没有C#3.0的时代,我们对于这样的接口就需要对两种重载都进行实现,有了扩展方法,接口就变得简单多了:
Code15:
interface ILog在没有C#3.0的时代,我们对于这样的接口就需要对两种重载都进行实现,有了扩展方法,接口就变得简单多了:
{
void Write(String msg);
}
Code15:
interface ILog扩展方法我们添加到另外一个类中:
{
void Write(String msg);
}
Code16:
static class LogExtensions这样我们对于以上接口的实现,只需实现单一的方法,而在客户端却可以自由的调用任何一种重载。对于扩展方法,还可以扩展到.NET Framework中提供的类型。
{
public static void Write(this ILog log)
{
log.Write("Info");
}
}
9. Lambda 表达式
Lambda表达式类似于.NET2.0中的匿名方法,为编写匿名方法提供了更简明的函数式的句法,但结果却在编写LINQ查询表达式时变得极其有用,因为它们提供了一个非常紧凑的而且类安全的方式来编写可以当作参数来传递,在以后作运算的函数。使用的语法如下:
(参数列表) => 表达式或者语句块
理解Lambda表达式最简单的方法就是理解成为行内方法,即在一行代码内实现我们以前函数体中的功能。示例代码如下:
Code17:
public class Demo在上面的示例中,第一个Lambda来指定获取特定客户时所用的过滤条件,用第二个Lambda来指定在计算平均年龄时该用Customer对象的哪个值。 与匿名方法不同的是匿名方法要求参数类型是明确地指明的,Lambda表达式允许省略参数类型,而允许它们根据用法来推断出类型。
{
List<Customer> Customers = new List<Customer>
{
new Customer{Name = "TerryLee", Age=24},
new Customer{Name = "lhj", Age=36},
new Customer{Name = "lihuijun", Age=24}
};
IEnumerable<Customer> customers = Customers.Where(p => p.Name = "TerryLee");
int age = Customers.Average( a => a.Age);
}
10. 查询句法
查询句法是使用标准的LINQ查询运算符来表达查询时一个方便的声明式简化写法。该句法能在代码里表达查询时增进可读性和简洁性,使得代码更加容易读懂,同时Visual Studio IDE对查询句法提供了完整的智能提示和编译时检查支持。如下示例代码所示:
Code18:
public class Demo在C#中,每个查询表达式的句法从from子句开始,以select或group子句结束。from子句表示你要查询什么数据。select子句则表示你要返回什么数据,且应该以什么构形返回,where子句表示你要进行查询的条件。更详细的句法我们将会在后续的文章中专门讲述。
{
List<Customer> Customers = new List<Customer>
{
new Customer{Name = "TerryLee", Age=24},
new Customer{Name = "lhj", Age=36},
new Customer{Name = "lihuijun", Age=24}
};
IEnumerable<Customer> customers = from c in Customers
where c.Name = "TerryLee"
select c;
}
11. 结束语
好了,作为LINQ to SQL系列的第一篇文章,我们主要对LINQ to SQL有一个概述的认识,并且了解了C#3.0中一些新的特性。在后续的文章中我将会详细介绍LINQ to SQL的各个方面,希望大家继续关注。