Note introducting LINQ
LINQ is a methodology that simplifies and unifies the implenentation of any kind of data access. It offering a uniform way to access and manage data without forcing the adoption
of a "one size fies all" model. It is a programming model that introduces queries as a
first-class concept into any Microsoft.Net language.However, complete support for LINQ
requires some extensions int the language used. These extensions boost productivity, thereby
providing a shorter, meaningful, and expressive syntax to manipulate data.
A simple LINQ query for a typical software solution that returns the names of customers in Italy:
from c in Customers
where c.Country == "Italy"
select c.CompanyName;
foreach(string name in query) {
Console.WriteLine( name );
}
The result of this query is a list of stirngs. You can enumerate these values with a
foreach loop in C#.
*The same query(and the following foreach code) can be applied to an SQL database,to a DataSet, to an array of object in memory,or to many other kinds of data. Customers could
be a collection of objects.
Like this:
DataDet ds = GetDataSet();
DataTable Customers = ds.Tables["Customers"];
Customers could be an entity class that describies a physical table in a relational database.
DataContext db = new DataContext(ConnectionString);
Table<Customer> Customers = db.GetTable<Customer>();
describies a conceptual model and is mapped to a relational database.
NorthwindModel dataModel = new NorehwindModel();
ObjectQuery<Customer> Customers = dataModel.Customers;
Customer[]Customers=GetCustomers();
varquery=
fromcinCustomers
wherec.Country=="Italy"
selectc;
the compiler generates this code:
Customer[]Customers=GetCustomers();
IEnumerable<Customer>query=
Customers
.Where(c=>c.Country=="Italy");
becomes longer, as you see here:
varquery=
fromcinCustomers
wherec.Country=="Italy"
orderbyc.Name
selectnew{c.Name,c.City};
the generated code is longer too:
var query=
Customers
.Where(c=>c.Country=="Italy");
.OrderBy(c=>c.Name)
.Select(c=>new{c.Name,c.City});
The code apparently calls instance members on the object returaned from the previous
The Var keyword used to declare query infers the variable type declaration from the initial
assignment,which in this case will return an IEnumerable<T> type.
*The important concept is the timing of operations over data.In general,a LINQ query is
not really executed until there is access to the query result,because it describies a set
of operations that will be performed when necessary.
The access to a query result does the real work.
This can be illustrated in the case of a foreach loop:
varquery=fromcinCustomers...
foreach (stringnameinquery)...
There are also methods that iterate a LINQ query result, producing a persistent copy of data
in memory. For example, the ToList method produces a typed List<T> collection:
varquery=fromcinCustomers...
List<Customer>customers= query.ToList();
When the LINQ query operates on data that is on a relational database (such as Microsoft
SQL Server), the LINQ query generates an equivalent SQL statement instead of operating
with in-memory copies of data tables. The query execution on the database is delayed until
the first access to the query result. Therefore, if in the last two examples Customers was a
Table<Customer> type (a physical table in a relational database) or an ObjectQuery<Customer>
type (a conceptual entity mapped to a relational database), the equivalent SQL query would
not be sent to the database until the foreach loop was executed or the ToList method was
called. The LINQ query can be manipulated and composed in different ways until that time.
At first sight, LINQ might appear to be just another SQL dialect. This similarity has its roots in
the way a LINQ query can describe a relationship between entities such as an SQL join:
varquery=
fromcinCustomers
joinoinOrders
onc.CustomerIDequalso.CustomerID
selectnew{c.CustomerID,c.CompanyName,o.OrderID};
var query =
from c in Customers
join s in Suppliers
on c.City equals s.City
select new { c.City, c.Name, SupplierName = s.Name };
And something like the following will be returned:
City=Torino Name=Marco SupplierName=Trucker
City=Dallas Name=James SupplierName=FastDelivery
City=Dallas Name=James SupplierName=Horizon
City=Seattle Name=Frank SupplierName=WayFaster
When you build a LINQ query,it is always a set of Operations on instances of some classes.
Note You can create entity classes by using code-generation tools such as SQLMetal or
the LINQ to SQL Designer in Microsoft Visual Studio.
In Listing 1-3, you can see an example of a Product class that maps a relational table named
Products, with five columns that correspond to public data members.
Listing 1-3 Class declaration mapped on a database table
[Table("Products")]
public class Product {
[Column(IsPrimaryKey=true)] public int IdProduct;
[Column(Name="UnitPrice")] public decimal Price;
[Column()] public string ProductName;
[Column()] public bool Taxable;
[Column()] public decimal Tax;
}
When you work on entities that describe external data (such as database tables), you can
create instances of these kinds of classes and manipulate in-memory objects just as if data
from all tables were loaded in memory. These changes are submitted to the database through
SQL commands when you call the SubmitChanges method, as you can see in Listing 1-4.
Listing 1-4 Database update calling the SubmitChanges method
from p in db.Products
where p.Taxable == true
select p;
foreach( Product product in taxableProducts ) {
RecalculateTaxes( product );
}
db.SubmitChanges();
The Product class in the preceding example represents a row in the Products table of an
external database. When SubmitChanges is called, all changed objects generate an SQL
command to update the corresponding rows in the table.
LINQ has a different set of classes and extensions to support the manipulation of XML data.
Imagine that your customers are
able to send orders using XML files like the ORDERS.XML file shown in Listing 1-5.
Listing 1-5 A fragment of an XML file of orders
<orders xmlns="http://schemas.devleap.com/Orders">
<order idCustomer="ALFKI" idProduct="1" quantity="10" price="20.59"/>
<order idCustomer="ANATR" idProduct="5" quantity="20" price="12.99"/>
<order idCustomer="KOENE" idProduct="7" quantity="15" price="35.50"/>
</orders>
*Using standard Microsoft .NET 2.0 System.Xml classes,you can load the file using a DOM
approach or you can parse its contents using an XmlReader implementation.
If you need to extract all the products ordered with their quantities, you can parse the orders
file using an XmlReader to accomplish this, as shown in Listing 1-6.
Listing 1-6 Reading the XML file of orders using anXmlReader
String nsUri = "http://schemas.devleap.com/Orders";
XmlReader xmlOrders = XmlReader.Create( "Orders.xml" );
Order order = null;
while (xmlOrders.Read()) {
switch (xmlOrders.NodeType) {
case XmlNodeType.Element:
if ((xmlOrders.Name == "order") &&
(xmlOrders.NamespaceURI == nsUri)) {
order = new Order();
order.CustomerID = xmlOrders.GetAttribute( "idCustomer" );
order.Product = new Product();
order.Product.IdProduct =
Int32.Parse( xmlOrders.GetAttribute( "idProduct" ) );
order.Product.Price =
Decimal.Parse( xmlOrders.GetAttribute( "price" ) );
Introducing Microsoft LINQ
order.Quantity =
Int32.Parse( xmlOrders.GetAttribute( "quantity" ) );
orders.Add( order );
}
break;
}
}
You could also use an XQuery like the following one to select nodes:
for $order in document("Orders.xml")/orders/order
return $order
shows a LINQ to XML query made over the orders file.
Listing 1-7 Reading the XML file using LINQ to XML
XNamespace ns = "http://schemas.devleap.com/Orders";
var orders = from o in xmlOrders.Root.Elements( ns + "order" )
select new Order {
CustomerID = (String)o.Attribute( "idCustomer" ),
Product = new Product {
IdProduct = (Int32)o.Attribute("idProduct"),
Price = (Decimal)o.Attribute("price") },
Quantity = (Int32)o.Attribute("quantity")
};
In SQL, you can write the following:
SELECT * FROM Customers WHERE Country = 'Italy'
In C#, you would probably write this:
public List<Customer> ItalianCustomers( Customer customers[] )
{
List<Customer> result = new List<Customer>();
foreach( Customer c in customers ) {
if (c.Country == "Italy") result.Add( c );
}
return result;
}
IEnumerable<T> interface. You can use LINQ to write a query over Reflection:
var query =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
from method in type.GetMethods()
where method.IsStatic
&& method.ReturnType.GetInterface( "IEnumerable`1" ) != null
orderby method.DeclaringType.Name, method.Name
group method by new { Class = method.DeclaringType.Name,
Method = method.Name };
The equivalent C# code that handles data is longer to write, harder to read, and probably more
error prone. You can see a possible version that is not particularly optimized in Listing 1-10.
Listing 1-10 C# code equivalent to a LINQ query over Reflection
foreach( var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
foreach( var type in assembly.GetTypes() ) {
foreach( var method in type.GetMethods()) {
if (method.IsStatic &&
method.ReturnType.GetInterface("IEnumerable`1") != null) {
string fullName = String.Format( "{0}.{1}",
method.DeclaringType.Name,
method.Name );
if (results.IndexOf( fullName ) < 0) {
results.Add( fullName );
}
}
}
}
}
results.Sort();
Many possible evolutions could originate form LINQ, and we should not forget that SQL is a
widely adepted standard that cannot be easily replaced by another.
extension [ex·ten·sion || ɪk'stenʃn]
n. 延长, 范围, 扩充
boost [buːst]
n. 推进, 增加, 支援
v. 举, 抬; 推动; 推; 促进
thereby
adv. 因此, 在那方面, 从而
expressive [ex·pres·sive || ɪk'spresɪv]
adj. 表达的
syntax [syn·tax || 'sɪntæks]
n. 语法; 有条理的排列; 句法
manipulate [ma·nip·u·late || mə'nɪpjəleɪt /-jʊl-]
v. 操纵, 操作, 利用
typical [typ·i·cal || 'tɪpɪkl]
adj. 典型的, 象征性的
entity [en·ti·ty || 'entətɪ]
n. 实体; 本质; 存在
compiler [com·pil·er || kəm'paɪlə]
n. 从高级语言原始码制造程序的程序 (计算机用语); 编辑者; 编译器
from now on
从现在开始
domain [do·main || dəʊ'meɪn]
n. 领土; 范围; 领地, 自治区; 因特网根据国家和组织类型的分类 (计算机用语)
instance [in·stance || 'ɪnstəns]
n. 例证, 情况, 建议
v. 引以为例, 获得例证
declare [de·clare || dɪ'kleə]
v. 断言; 宣布; 宣称; 声明, 表示
concept [con·cept || 'kɒnsept]
n. 观念; 概念
preform ['prɪː'fɔrm /-'fɔːm]
v. 预先形成
manipulation [ma·nip·u·la·tion || mə'nɪpjəleɪʃn /-jʊl-]
n. 处理; 操纵; 操作
parse [pɑrz/pɑː-]
v. 解析; 符合语法
n. 分列, 为使程序处理容易而把输入分成小部分 (计算机用语)
evolution [ev·o·lu·tion || ‚iːvə'luːʃn]
n. 进化, 进展, 发展
adept [ad·ept || 'ædept]
adj. 熟练的, 内行的; 拿手的
n. 内行, 老手, 擅长者; 专家