当前位置: 首页 > news >正文

C# 3.0入门(二)

Select 操作

先给关注dlinq的朋友们道歉,最近工作实在忙,没有时间来写blog。从本节开始,我们讲dlinq语法咯。我们先从select子句讲起。看下面的例子。

varq =
fromc
in db.Customers
selectc.ContactName;

这是一个最简单的dlinq查询语句,查询得到联系人的名字。在这里,我需要提醒下大家的是,像这个语句只是个声明,dlinq并没有真正把数据取出来,只有当你需要该数据的时候,它才会帮你去取,这就是延迟加载(deferred loading)。如果,你想在声明的时候就希望dlinq帮你取到数据,你可以使用ToList() 或ToArray()方法。如上例。

varq = (fromc in db.Customers
selectc.ContactName).ToArray();
varq = (fromc in db.Customers
selectc.ContactName).ToList();

在这里,我还要提醒大家一点。dlinq返回的结果集是对象的集合,不是数据的。
在dlinq执行的时候,它会先将上面的标准查询转换成dlinq的API(也有人叫级连方法),比如,下面语句

varq =
fromc
in db.Customers
wherec.City
== " London "
selectc;

就会先被转化成 var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 也就是说,这两个语句是等价的。而后,dlinq会解析影射文件,根据dlinq的query语句,自动产生sql语句,并把sql送到sql server服务器,根据返回的数据集,创建相应的对象。在这里,你可能会对c=>c感到非常陌生。这是Lambda表达式(expression),你可以理解c为结果集里的任一对象,这对象的类型是和你结果集里元素类型是一致的。这里理解起来可能困难。我们一起来理解下数据即是对象的概念。我相信这会帮我们理解Lambda表达式。

在dlinq之前,在java领域有Hibernate,在net领域有NHibernate技术,来实现object/relational 持久和查询服务。dlinq其实质上,是在吸收了众多技术的基础上,比他们更加强大的工具。数据即对象的含义有两层。第一,数据结构(表结构)即是类。可以描述为Table Schema--Class。第二,表里的数据即是变量,描述为Data--object(variable)。那么,我们在来理解Lambda表达式可能就容易些。刚才我们已经说了,var q = db.Customers.Where(c=>c.City== "London").Select(c=>c);将会返回Customers对象的集合,也就说,这个集合的每个元素就是一个Customer。Lambda表达式是对c# 2.0中的anonymous methods(匿名方法)的扩展。它更加简化匿名方法的实现形式。这里的c是一种隐式的声明,编译器会自动推断它的实际类型,也可以显示声明,比如, var q = db.Customers.Where((Customer c) => c.City == "London").ToList(); Lambda表达式用=>符号跟随一个表达式,这个表达式,需要返回一个类型,其实质就是一个方法返回一个类型。它只是更加简洁的匿名方法。然后,where等操作符用它返回的这个类型做为参数。关于Lambda表达式的具体实现,我会在进阶部分详细讲解。这里不再赘述。

有一点要提醒大家的是,标准的查询语句,必须是select语句在最后,而级连表达式,各种操作符的位置并不是很重要。比如var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 可以写成var q = db.Customers.Select(c=>c).Where(c=>c.City== "London");它们两个是一样的,但是,标准查询就不可以换位子,select语句必须在最后。虽然在级连表达式,各种操作符的位置并不是很重要,但是他们还是有区别的。特别是在使用匿名类后,区别很明显。但万变不离其宗,我们只要记住,下一个操作符总是在上一个操作符所筛选的数据集的基础上进行筛选。这点,我会在以后的blog中,更加详细的说明。
在select语句中,另一个难点是匿名类。比如列子

varq =
fromc
in db.Customers
select
new {c.ContactName,c.Phone};

其实不光在select操作中有匿名类,其他操作中也有。让我们一起来理解下匿名类。上面的语句与
var q = db.Customers.Select(c=>new {c,ContactName,c.Phone});是等价的。匿名类是c# 3.0中新出现的特性。其实质是编译器根据用户定义,自动产生一个匿名的类,帮用户实现临时变量的储存。注意,是临时变量。大量使用匿名类会使程序可读性降低。匿名类还依赖于另外一个特性,就是在c# 3.0可以支持根据property来创建对象。比如,有类

public class Person
{
private string name;

public string Name
{
get { return name;}
set {name = value;}
}
}

以前,我们只可以用构造函数来创建其对象,现在在3.0中支持用property来创建,即,可以用
var d = new Person { Name = "s" }; 来创建对象。在这里,你可能还对var类型产生疑问。你可能以为c#3.0和javascript一样是弱类型的。其实var并不是c#3.0的类型,它是编译器的关键字,编译器根据实际变量的返回类型,自动推断类型。那么var c = null; 是无法编译通过,因为编译不知道null代表那个类型。所以,c#3.0还是强类型的。

现在3.0可以支持用property来创建对象了,那么就有了匿名类的出现。比如,var d = new { Name = "s" };编译器自动产生一个有property叫做Name的匿名类,然后按这个类型分配内存,并初始化对象。在这个地方,还有个问题,比如,var d = new { "s" };是编译不通过的。因为,编译器不知道匿名类中的property的名字。但是,如果, string c = "d"; var d = new { c}; 则是可以通过编译的。编译器会创建一个叫做匿名类带有叫c的property。

在dlinq中,比如new {c,ContactName,c.Phone});这里出现ContactName和Phone都是我们在影射文件中定义的和表中字段相对应的property。编译器在取会数据并创建对象时,会创建一个匿名类,这个类有两个属性,为ContactName和Phone,然后根据数据初始化对象。匿名类还有另外一种形式。

varq =
frome
in db.Employees
select
new {Name = e.FirstName + " " + e.LastName,Phone = e.HomePhone};
这种形式和第一种不同的是,编译器会重命名property的名字。当然也可以把两种形式组合起来。
varq =
fromp
in db.Products
select
new {p.ProductID,HalfPrice = p.UnitPrice / 2 };


第一个属性的名字不会变,第二个会被重新命名。
好,就先讲这几个,下节我会介绍几个更复杂的用法。

Where操作

从本节开始,本文正式更名为C#3.0入门系列。先发布一则消息,VS2007 Beta版本已经发布咯,下载地址:
http://www.microsoft.com/downloads/details.aspx?FamilyID=1FF0B35D-0C4A-40B4-915A-5331E11C39E6&displaylang=en
大家快去下载呀,我也好和大家一起体验该版本最新功能呀。
dlinq也更名为linq to sql.本文也跟着做相应变化,稍候,我会去更新前面的文章。我们先接着讲linq的语法。
Select操作
最简单的

,varq =
fromc
in db.Customers
selectc.ContactName;

匿名类的

,varq =
fromc
in db.Customers
select
new {c.ContactName,c.Phone};
,varq =
frome
in db.Employees
select
new {Name = e.FirstName + " " + e.LastName,Phone = e.HomePhone};
,varq =
fromp
in db.Products
select
new {p.ProductID,HalfPrice = p.UnitPrice / 2 };

条件的

varq =
fromp
in db.Products
select
new {p.ProductName,Availability = p.UnitsInStock - p.UnitsOnOrder < 0 ? " OutOfStock " : " InStock " };

这种条件的会被翻译成sql中{case when condition then else}的。
name type形式的:

varq =
frome
in db.Employees
select
new Name{FirstName = e.FirstName,LastName = e.LastName};

只所以是name type的,是因为Name类是已经定义好的,也就是说,你可以用这种方式,返回你需要类型的对象集.
shaped形式的:

varq =
fromc
in db.Customers
select
new {
c.CustomerID,
CompanyInfo
= new {c.CompanyName,c.City,c.Country},
ContactInfo
= new {c.ContactName,c.ContactTitle}
};

该形式,其select操作使用了匿名对象,而这个匿名对象中,其属性也是个匿名对象。
nested形式的:

varq =
fromo
in db.Orders
select
new {
o.OrderID,
DiscountedProducts
=
fromod
in o.OrderDetails
whereod.Discount
> 0.0
selectod,
FreeShippingDiscount
= o.Freight
};

其返回的对象集中的每个对象DiscountedProducts属性中,又包含一个小的集合。也就是每个对象也是一个集合类。
Distinct形式的:

varq = (
fromc
in db.Customers
selectc.City)
.Distinct();

该形式,筛选该字段中不相同的值。会被翻译为
select distinct city from customers

where操作:
最简单的

,varq =
fromc
in db.Customers
wherec.City
== " London "
selectc;

,varq =
frome
in db.Employees
wheree.HireDate
>= new DateTime( 1994 , 1 , 1 )
selecte;

或与关系的where条件

,varq =
fromp
in db.Products
wherep.UnitsInStock
<= p.ReorderLevel && ! p.Discontinued
selectp;
,varq =
fromp
in db.Products
wherep.UnitPrice
> 10m || p.Discontinued
selectp;
,varq =
db.Products.Where(p
=> p.UnitPrice > 10m).Where(p => p.Discontinued);

在上例中,1和2语句先被翻译成类似3语句的形式,再被翻译成sql语句,送回数据服务器。他们基本上一样的。
欠套在first操作中的where条件:
first操作,其实质就是在sql语句前,加了一个top 1.

,Customercust = db.Customers.First(c => c.CustomerID == " BONAP " );
Orderord = db.Orders.First(o => o.Freight > 10.00M );

第一个例子,是筛选customerid为"BONAP"的客户,第二个筛选订单运费大于10的订单。First操作必须用这种级连的形式。比如

Shippershipper = db.Shippers.First();

也可以把linq的expression和级连的形式混合使用,比如第一个例子,加入first操作,

varq =
(fromc
in db.Customers
wherec.City
== " London "
selectc).First();

如果加入first操作,其返回是一个具体的对象,而不是一个集合。如果first操作没有条件,它只是简单的在sql语句中添加top 1,如果有条件,它在翻译时,就会加入条件语句。

OrderBy操作

本节讲orderby操作.我突然在想这么一个问题,读者会T-SQL吗?要是不知道,那我写的是不是太简单了呢?做个调查哦,不知道的举手.
OrderBy操作
简单的,按雇用日期排序,默认为升序

varq =
frome
in db.Employees
orderbye.HireDate
selecte;

带where条件的,shipcity为london的。

varq =
fromo
in db.Orders
whereo.ShipCity
== " London "
orderbyo.Freight
selecto;

varq
=
fromo
in db.Orders
orderbyo.Freight
whereo.ShipCity
== " London "
selecto;

在这里where和orderby的顺序并不重要。而在T-SQL中,where和orderby有严格的位置限制。
OrderByDescending的,按价格降序。

varq =
fromp
in db.Products
orderbyp.UnitPricedescending
selectp;

ThenBy的和ThenByDescending,也就是按多个列进行排序,第一个列子是先按city,city相同的再按contactname排序,第二个例子中,第二序列为降序。

ThenBy:
varq
=
fromc
in db.Customers
orderbyc.City,c.ContactName
selectc;

ThenByDescending:
varq
=
fromo
in db.Orders
whereo.EmployeeID
== 1
orderbyo.ShipCountry,o.Freightdescending
selecto;

对这两个句子解释下。

对于ThenBy操作,其级连形式为:
varq
= db.Customers.OrderBy(c => c.City).ThenBy(c => c.ContactName).ToList();
因为T
- SQL中,并没有ThenBy语句,其依然翻译为OrderBy
所以,也可以用下面语句来表达
varq
= db.Customers.OrderBy(c => c.ContactName).OrderBy(c => c.City).ToList();
所要注意的是,是两个orderby的顺序,多个orderby操作时,级连方式是按逆序.即先按city排时,city要放在最后。

对于降序的,用相应的降序操作符替换即刻。

varq = db.Customers.OrderByDescending(c => c.City).ThenByDescending(c => c.ContactName).ToList();

需要说明的是,orderby操作,不支持按type排序,也不支持匿名类。
比如var q = db.Customers.OrderBy(c => c).ToList();和
var q = db.Customers.OrderBy(c => new {c.City,c.ContactName}).ToList();

会被抛出异常。但是,既然提了,大家在这个问题就不会犯错误,常见的错误是前面的操作有匿名类,再跟orderby时,比较的是类别。比如

var q = db.Customers.Select(c => new { c.City, c.Address }).OrderBy(c => c).ToList();

如果你想使用OrderBy(c => c),其前提条件是,前面步骤中,所产生的对象的类别必须为C#语言的基本类型。比如
var q = db.Customers.Select(c=>c.City).OrderBy(c => c).ToList();
city为string类型。

还有一点需要说明的时,linq和dlinq在orderby操作中,稍微有一点区别。linq支持按type排序,但是,需要你自己去实现IComparable接口。

比如语句:var q = db.Customers.ToList().OrderBy(c => c).ToList();
第一个ToList()会把数据库中所有数据取出,放到内存中,以后所有的操作全部是对内存操作。后面的所有操作均为linq操作,不是dlinq。(这一点,我前面的文章中讲过)如果,你想用按客户进行排序,你必须在Customer类中,实现IComparable接口

#regionIComparableMembers

publicintCompareTo(objectobj)
{
returnthis._CustomerID.CompareTo(((Customers)obj).CustomerID);
//thrownewException("Themethodoroperationisnotimplemented.");
}

#endregion

Orderby操作,会自动调用该接口的方法,实现按类别排序。如果,你的映射文件中没有实现该接口,系统会抛出异常。
你也可以使用generic,如下,
public partial class Customers : System.Data.Linq.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged,IComparable<Customers>

#regionIComparable<Customers>Members

publicintCompareTo(Customersother)
{
returnthis.CustomerID.CompareTo(other.CustomerID);
//thrownewException("Themethodoroperationisnotimplemented.");
}

#endregion

好处就是你无须把object强制转化为customer类。
我们再来定义个,先按订单号排序,相同订单按产品号排序的。
public partial class OrderDetails : System.Data.Linq.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged,IComparable<OrderDetails>

#regionIComparable<OrderDetails>Members

publicintCompareTo(OrderDetailsother)
{
intk=this._OrderID-other.OrderID;
if(k==0)
{
k
=this._ProductID-other.ProductID;
}
returnk;
//thrownewException("Themethodoroperationisnotimplemented.");
}

好了,更多的功能,等待大家自己去实现。下次讲Groupby操作。

OR工具介绍

不得不再次给关注dlinq的朋友道歉了。好久都没有更新blog了。因为工作的变动,还要赶期限,没有时间关注这里了。

先发布一则消息。Orcas Beta1, 这个才是beta1,可以到http://www.microsoft.com/downloads/details.aspx?FamilyID=f10fb5df-e369-4db4-b9a7-845dbf793368&DisplayLang=en 下载。5月1号的版本。最早4月19号就出来过一个,只是没有在意。还有一个http://www.microsoft.com/downloads/details.aspx?FamilyID=36b6609e-6f3d-40f4-8c7d-ad111679d8dc&DisplayLang=en 。 一个是self-extracting 版本的,一个是Virtual PC version的。不知道什么区别。没有装过。
本节舍去原来的计划,而改讲映射工具。在入门三一文中,我们提到了sqlmetal这个工具。http://www.cnblogs.com/126/archive/2006/09/06/492332.html sqlmetal的功能是将数据库的信息抽提出来,生成映射代码。Orcas还有另外一个工具,就是O/R Designer.
先讲sqlmetal. 自上次的版本后,sqlmetal,又增加了一些新的功能。比如,支持SQLCE版本的Sql Server, 支持直接输入connection string, 等等。 这是sqlmetal的帮助信息,是不是比上次多了很多?

<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
Microsoft(R)DatabaseMappingGenerator2008Beta2version1.00.20612
forMicrosoft(R).NETFrameworkversion3.5
Copyright(C)MicrosoftCorporation.Allrightsreserved.

SqlMetal[options][
<inputfile>]

GeneratescodeandmappingfortheLINQtoSQLcomponentofthe.NETframework.SqlMetalcan:
-Generatesourcecodeandmappingattributesoramappingfilefromadatabase.
-Generateanintermediatedbmlfileforcustomizationfromthedatabase.
-Generatecodeandmappingattributesormappingfilefromadbmlfile.

Options:
/server:
<name>Databaseservername.
/database:
<name>Databasecatalogonserver.
/user:
<name>LoginuserID(default:useWindowsAuthentication).
/password:
<password>Loginpassword(default:useWindowsAuthentication).
/conn:
<connectionstring>Databaseconnectionstring.Cannotbeusedwith/server,/database,/us
eror/passwordoptions.
/timeout:
<seconds>TimeoutvaluetousewhenSqlMetalaccessesthedatabase(default:0wh
ichmeansinfinite).

/viewsExtractdatabaseviews.
/functionsExtractdatabasefunctions.
/sprocsExtractstoredprocedures.

/dbml[:file]Outputasdbml.Cannotbeusedwith/mapoption.
/code[:file]Outputassourcecode.Cannotbeusedwith/dbmloption.
/map[:file]Generatemappingfile,notattributes.Cannotbeusedwith/dbmloption
.

/language:
<language>Languageforsourcecode:VBorC#(default:derivedfromextensiononcodefilename).
/namespace:
<name>Namespaceofgeneratedcode(default:nonamespace).
/context:
<type>Nameofdatacontextclass(default:derivedfromdatabasename).
/entitybase:
<type>Baseclassofentityclassesinthegeneratedcode(default:entitieshavenobaseclass).
/pluralizeAutomaticallypluralizeorsingularizeclassandmembernamesusingEnglishlanguagerules.
/serialization:
<option>Generateserializableclasses:NoneorUnidirectional(default:None).
/provider:
<type>Providertype(default:providerisdeterminedatruntime).

<inputfile>MaybeaSqlExpressmdffile,aSqlCEsdffile,oradbmlintermediatefile.

CreatecodefromSqlServer:
SqlMetal/server:myserver/database:northwind/code:nwind.cs/namespace:nwind

GenerateintermediatedbmlfilefromSqlServer:
SqlMetal/server:myserver/database:northwind/dbml:northwind.dbml/namespace:nwind

Generatecodewithexternalmappingfromdbml:
SqlMetal/code:nwind.cs/map:nwind.mapnorthwind.dbml

GeneratedbmlfromaSqlCEsdffile:
SqlMetal/dbml:northwind.dbmlnorthwind.sdf

GeneratedbmlfromSqlExpresslocalserver:
SqlMetal/server:.\sqlexpress/database:northwind/dbml:northwind.dbml

Generatedbmlbyusingaconnectionstringinthecommandline:
SqlMetal/conn:"server='myserver';database='northwind'"/dbml:northwind.dbml


因为sqlmetal已经讲过,不再多讲,大家知道它是个命令行的工具,用来做映射数据库信息的,会使用就可以。再接着讲O/R Designer. 新建任一工程。右击工程,选择add->new item. 如图所示:

弹出item对话框,category中选择data,然后选择Linq to Sql Class, 如图所示:

选择添加。这样,你的工程就多了一个O/R Desiger项目。在菜单View->Sever Explore 打开Sever Explore,添加一个Data Connection, 如图所示:

后,填写数据库服务器,用户名,密码和数据库名。如果,你没有,你可以使用Sql Express 版本。添加之后的效果,如图所示:

打开刚才添加的DataClasses1.dbml 文件,将Sever Explore中的Customer 表或view拖入Designer中来,如图所示:


也可以将Store procedure 和 User define function 拖进来,会在方法面板上形成方法。如图所示:

如果,两个表之间有关系,OR Designer可以显示它们的关系,将Order表和 OderDetail表拖入 如图所示:

打开DataClasses1. Designer.cs 文件,你就可以看到OR Designer给你产生的映射代码了。

<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->usingSystem.Data.Linq;
usingSystem.Data.Linq.Mapping;
usingSystem.Data;
usingSystem.Collections.Generic;
usingSystem.Reflection;
usingSystem.Linq;
usingSystem.Linq.Expressions;
usingSystem.ComponentModel;
usingSystem;


[System.Data.Linq.Mapping.DatabaseAttribute(Name
="northwind_may06ctp")]
publicpartialclassDataClasses1DataContext:System.Data.Linq.DataContext
{

privatestaticSystem.Data.Linq.Mapping.MappingSourcemappingSource=newAttributeMappingSource();

ExtensibilityMethodDefinitions#regionExtensibilityMethodDefinitions
partial
voidOnCreated();
partial
voidInsertOrder(Orderinstance);
partial
voidUpdateOrder(Orderinstance);
partial
voidDeleteOrder(Orderinstance);
partial
voidInsertOrder_Detail(Order_Detailinstance);
partial
voidUpdateOrder_Detail(Order_Detailinstance);
partial
voidDeleteOrder_Detail(Order_Detailinstance);
#endregion


staticDataClasses1DataContext()
{
}


publicDataClasses1DataContext(stringconnection):
base(connection,mappingSource)

相关文章:

  • 使用weixin-java-miniapp实现微信小程序登录接口
  • Python 下的虚拟环境的使用
  • C#3.0入门系列(三)
  • 一入 Java 深似海 -- S02E02 学习笔记
  • C#3.0入门系列(四)
  • GCC 的简单使用
  • 关于Keil 的一些看法
  • Jackson 简单使用记录
  • java基础设计 开源框架
  • Spring源码学习笔记
  • 正则资料笔记
  • C#接口慨述
  • Inversion of Control 简要笔记
  • 定义接口及定义接口成员
  • 小马哥讲Spring核心编程思想 - 第二章 重新认识IoC
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 5、React组件事件详解
  • CentOS 7 修改主机名
  • JS 面试题总结
  • mysql外键的使用
  • npx命令介绍
  • Python打包系统简单入门
  • Redis学习笔记 - pipline(流水线、管道)
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 记一次用 NodeJs 实现模拟登录的思路
  • 来,膜拜下android roadmap,强大的执行力
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 网络应用优化——时延与带宽
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • #Linux(make工具和makefile文件以及makefile语法)
  • #QT项目实战(天气预报)
  • #图像处理
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • $$$$GB2312-80区位编码表$$$$
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (33)STM32——485实验笔记
  • (4)logging(日志模块)
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (pojstep1.1.2)2654(直叙式模拟)
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (四)汇编语言——简单程序
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .net framework profiles /.net framework 配置
  • .Net FrameWork总结
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .net 提取注释生成API文档 帮助文档
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .net6Api后台+uniapp导出Excel