[原文作者]:Amanda Silver, Paul Vick, Scott Wisniewski
[原文链接]:Lambdas, and Take While, and Group By, Oh My!
太棒了,Visual Basic 2008 Beta2版本将于今天发布 ,即将回归到我们用户所热爱的桌面上,这个自然的栖息地。(请大家注意在登陆界面上GPM 微笑的脸哦,--- Beta 版本的完成使他格外的开心。)
新版本到底有什么新玩意呢?加入了比之前Beta1版本更多的新特性,能否列举一些呢?这里有个关于Beta2 版本VB Language详细的清单:
- Query 操作符: Group By, Group Join, Take (While), Skip [While], Aggregate, Count, Sum, Min, Max, Average, From->Let
- Nullable 支持
- Lambda 表达式(aka inline Funcitons)
- Partial 方法—ScottGu 前几天提起过,ScottWis前几周也发表过一些这方面的资料。
- Anonymous 类型 w/Keys-这个PaulV已经开始讨论这些了。
这里是一些查询语句的例子,好了你现在就可以开始用VB写代码了!
' Find suppliers in the same city as customers
From cust In db.Customers _
Join sup In db.Suppliers _
On cust.City Equals sup.City _
Select cust.CompanyName, sup.ContactName, cust.City
' Find suppliers in the same city, with the same postalcode as customers
From cust In db.Customers _
Join sup In db.Suppliers _
On cust.City Equals sup.City And cust.PostalCode Equals sup.PostalCode
Select cust.CompanyName, sup.ContactName, cust.City
' Find the average unit price and count for products by category
From prod In db.Products _
Group By prod.CategoryID _
Into Average(prod.UnitPrice), Count()
' For each customer find the group of the suppliers in the same country
From cust In db.Customers _
Group Join sup In db.Suppliers _
On cust.Country Equals sup.Country _
Into SuppliersInCountry = Group _
Select cust.CompanyName, SuppliersInCountry
' For each customer, the count of the number of suppliers in the same country
From cust In db.Customers _
Group Join sup In db.Suppliers _
On cust.Country Equals sup.Country _
Into NumSuppliers = Count() _
Select cust.CompanyName, NumSuppliers
' Avg price, avg # in stock, and max on order for non-discontinued products
Aggregate prod In db.Products _
Where Not prod.Discontinued _
Into AvgPrice = Average(prod.UnitPrice), _
AvgInStock = Average(prod.UnitsInStock), _
MaxOnOrder = Max(prod.UnitsOnOrder)
' Find orderID, OrderDate, and OrderTotal for each order
From order In db.Orders _
Where order.OrderDate > #7/22/1996# _
Aggregate ordDet In order.Order_Details _
Into OrderTotal = _
Sum(ordDet.UnitPrice *ordDet.Quantity * (1 - ordDet.Discount))
Select order.OrderID, order.OrderDate, OrderTotal
' Fill a dictionary with the customers by CompanyName
db.Customers.ToDictionary(Function(cust As Customer) cust.CompanyName)
另外为了支持新增的语言环境,我们现在提供了关键字的智能感知。

我们有关于XML的智能感知,难以置信吗?对,我们做到了。

而且我们大大改善了后台编译的性能,如果你对这个新版本有任何意见建议请告诉我们吧(如果你在beta1上再安装beta2或是在Vs2005上装beta2,你可以看看Scott的文章,他有提到如何避免AJAX 的扩展问题)
还有不要错过VB 9和Linq其他令人激动的新特性啊,举个例子来说, linq 现在支持Like操作符了! 还有新的ASP:LinqDataSource 控件。
还等什么呢快来领略吧J
[原文作者]: Benth Massi
[原文链接]: Converting VS 2005 Projects to VS 2008 - Enabling LINQ
假如现在你有一个用Visual Studio 2005 开发的应用程序你想进一步挖掘和转换它,并开始使用VS2008中的 LINQ功能。那么在这里,我打算简单介绍下使LINQ正常工作, 你需要什么步骤, 这些步骤也同样取决于你需要用到LINQ的什么功能。
在Visual Studio 2008 中,有多种的对象特性使你能够用.NET 2.0, 3.0 或者3.5框架来在2008的环境中编写程序。Scott Gu的文章 和Rick Strahl的对此都有介绍。这就意味着你不需要为了正常运行先前版本的程序而去安装多个版本的Visual Studio IDE在你的电脑上(注意,但是如果你先前用.NET 1.0 或 1.1开发的,那么还是需要安装2002或2003IDE的)。这是个好消息, 因为它不但节省了磁盘空间和省去了前后的转换,还能够使你获得更多的有利条件像调试和编辑而不需要冒着升级项目文件的风险。但是如果你希望把项目升级成3.5的版本去使用LINQ功能,你需要自己添加一些新的命名空间。
当你第一次在Visual Studio 2008 中打开一个Visual Studio 2005的项目时,系统会提示升级你的项目。实际上这样做是为了升级了你的项目文件(.vbproj) 和解决方案 (.sln) 使其和2008兼容。而这个项目仍然可以用2005打开,它是向下兼容的。但是解决方案文件是单一的,因此如果你是个团队开发项目并且混合有2005和2008 两种版本IDE时,你需要保留两个解决方案,不过幸运的是,你的项目文件(比解决方案要有更多的变化)是可以共享的。
如今所有的这些升级过程所做的是同时升级项目文件和解决方案,你的程序仍然以.NET2.0的平台为对象,为了升级你的程序去使用新的特性比如LINQ功能,你需要去改变到需要的Framework版本和添加一些新的引用。你也希望根据那些特性打开新的推断特性的选项,这允许编译器通过估计右值表达式去推测出局部变量的申明类型。这将对编写LINQ的查询非常有用。为了支持它,在解决方案资源管理器中双击我的项目打开项目属性,选择编译标签,选择下方的“On”按钮。
现在去改变目标Framework,点击"Advanced Compile Options..."
选择Framework 3.5,按ok,项目就会被关闭后重新开启。如果你再次打开项目看引用标签你会发现System.Core.dll 3.5的版本被自动的引用了。但是为了能使用LINQ,还需要引入两个命名空间和一些和LINQ有关的引用,为了完全支持Linq to Objects,需要添加 System.Linq的引用。现在你可以编写查询对象的代码如下:
Dim currentFiles = From File In My.Computer.FileSystem.GetFiles(CurDir) _
Select My.Computer.FileSystem.GetFileInfo(File)
为了能编写出作用于DataSet数据集的查询,你需要添加System.Data.DataSetExtensions的引用, 之后你要重新运行与你想编写的LINQ程序相关的DataSet的生成器。右键DataSet 选“Run Custom Tool”, 如此会重新产生DataSet 编码,这个编码可以使DataTables 继承位于System.Data.DataSetExtension 命名空间里 的一个叫做TypedTableBase的LINQ-ready 的类。 接下来你就可以在已经被定义的DataSet上编写程序了,例如:
Dim total = Aggregate Products In Me.CategoryProductDataSet.Products _
Where Products.CategoryID = 1 AndAlso _
Products.Discontinued = False _
Into Sum(Products.UnitPrice * Products.UnitsInStock)
如果你要开始使用LINQ 对XML 支持,你需要添加向System.Xml.Linq.dll的引用以及加入 System.Xml.Linq 命名空间,之后你就可以编写作用于xml的语句了 如:
Dim survey = XElement.Load(CurDir() & "\questions.xml")
Dim questions = From q In survey...<question> Select q
最后,如果你想要在你最新升级的项目中使用LINQ to SQL,那也是相当简单的。只要右键选择添加新的项目并选择“LINQ to SQL”类模板,它将会开启一个新的O/R设计器并且自动的为你添加System.Data.Linq.dll的引用。这允许你编写类似下面的查询(依靠SQL-server):
Dim countryList = From Customer In Db.Customers _
Where Customer.Country <> "" _
Order By Customer.Country _
Select Customer.Country Distinct
希望我已经清楚的呈现了怎么把你当前的项目中转换到VS2008使用LINQ的第一步, 那么你还等什么呢。
[原文作者]:Beth Massi
[原文链接]:Another Way to LINQ
我曾经在ScottGu的博客上读过他的部分文章,是关于在ASP.NET中LinqDatasource数据源如何使用LINQ to SQL的。文章浅显易懂,想必Scott为此花了不少的心思,如果你之前没有读过他的一些文章那么你最好花点时间去读一读,这会对你非常有好处的。而且他在例子中分别使用了VB和C#代码。下面我要着重介绍的一个例子是关于使用Lambda表达式得到每样产品的总价:
Dim products = From p In db.Products _
Where p.Category.CategoryName.StartsWith("C") _
Select p.ProductID, _
p.ProductName, _
p.UnitPrice, _
NumOrders = p.Order_Details.Count, _
Revenue = p.Order_Details.Sum(Function(details) _
details.UnitPrice * details.Quantity)
在这里,我们所使用的lambda用关键字”Function”来标识,它被用在了这个叫”Sum”的扩展方法里,其目的是完成对每样产品总价的计算。在VB 9里面我们也可以使用"Aggregate"来完成同样的功能。
另外,我们可以在Where子句中使用”Like”,这种用法和上面的作用是相同的:
Dim products = From p In db.Products _
Where p.Category.CategoryName Like "C*" _
Select p.ProductID, _
p.ProductName, _
p.UnitPrice, _
NumOrders = p.Order_Details.Count, _
Revenue = _
Aggregate detail In p.Order_Details _
Into Sum(detail.UnitPrice * detail.Quantity)
VB 9 提供了很多其他的比较通用的LINQ表达式,这样你就省去了手动再写一遍的时间,例如求和Sum, 取最小值Min, 最大值Max, 取平均Average等等。
我个人比较喜欢那些容易理解的VB 表达式句法,因此我更倾向于使用这些已有的。
如果你希望了解更多的有关Query的信息,我们为你准备了一些视频
希望大家能够从中发现乐趣!
[原文作者]: Benth Massi
[原文链接]: Metadata Programming and LINQ
提到LinQ,我最喜欢的应用之一是用她查询元数据(Metadata)以编写灵活的程序。或者反过来,用LinQ迅速地创建自己的object的元数据。基本上,元数据就是“描述数据的数据(data about data)”,比如描述内容的信息。在这里我想通过一个简单的例子来展示如何应用数据库里的信息描述窗体上的控件【即查询并应用元数据】;然后,我会创建一些描述这些控件信息的XML数据【即创建元数据】。注意:这个例子是用Visual Studio 2008 的Beta2版本创建的。
下面是这个具体的例子:现在,我想根据所选的语言从数据库得到控件的文本信息,这些文本会根据语言种类的不同而不同。我已经在数据库里创建了一个非常简单表格:ControlData,它有三个字段:Name, Description 和 Language,如下:
Name Description Language
---------- ------------- ---------
Button1 Hello English
Button2 Goodbye English
Button3 I love VB English
Button4 LINQ Rocks! English
TextBox1 Hello English
TextBox2 Goodbye English
Button1 Buon Giorno Italian
Button2 Ciao Italian
Button3 Amo VB Italian
Button4 LINQ è potente Italian
TextBox1 Buon Giorno Italian
接下来我创建了一个包含若干控件的Form。有些控件的名字和上面数据库的Name字段是一致的,有些不然。我们要做的是写一些查询语句以找出和数据库里Name字段匹配的控件,并根据所选的语言(由Combobox选择)改变其显示文本。它看起来像这样:

接下来的第一件事是创建一个LINQ to SQL class, 我们用O\R designer来实现这个操作。只需在你的Solution Explorer里右击project【在这个例子里是“LINQMetaData1”】-> “Add New Item”-> 选择“LINQ to SQL Class”模板。这样会生成一个.dbml文件并打开O/R designer(我用了默认的名字DataClasses1.dbml)。你可以把所需的表从Server Explorer里直接拖进来,然后它会像DataSet designer一样帮你生成所有的代码。

在这个例子里,我把ControlData 表拖到O/R designer上,这会自动创建一个DataContext,它有一个叫做ControlData的类。现在我们就可以用LinQ了,ControlData会帮我们把LinQ转化为SQL。【你可以在Server Explorer里点击“Show All Files”,点开“DataClasses1.dbml”,打开“DataClasses1.designer.vb”查看背后生成的代码。】下面我们只需创建一个DataContext的实例并查询ControlData。
我第一个要查询的是Combobox需要显示的Language信息。可以用distinct关键字把信息查询出来并把它设为ComboBox1的DataSource:
Public Class Form1
Private db As New DataClasses1DataContext
Private Sub Form1_Load() Handles MyBase.Load
Dim choices = From info In db.ControlDatas _
Select info.Language Distinct
Me.ComboBox1.DataSource = choices
End Sub
End Class
'choices'变量得到的将是string的list(IQueryable(Of String)),把它设为Combobox的DataSource毫无问题 – 我们最终得到的是两个string,"English" 和 "Italian"。
然后我们来搞定ComboBox的SelectedIndexChanged事件 - 目的是让各个控件的文本随之改变(我们这里只去设置Button的文本)。我们可以利用Me.Controls这个集合来找出所有顶层(top-level)的button,这个集合包含的类型是ControlsCollection,我们需要将它转化成Button类型:
Dim buttons = From c In Me.Controls _
Where TypeOf c Is Button _
Select CType(c, Button)
愿意的话,我们现在就可以遍历这个button的list(IEnumerable(Of Button))并设置它们的Text属性:
For Each button In buttons
button.Text = "LINQ is cool"
Next
然而我们想做的是找出这个list中与ControlData对象的Name字段相匹配的项,并根据语言来设置相应的文字,所以SelectedIndexChanged的event handler的完整代码应该是这样:
Private Sub ComboBox1_SelectedIndexChanged() Handles ComboBox1.SelectedIndexChanged
Dim buttons = From c In Me.Controls _
Where TypeOf c Is Button _
Select CType(c, Button)
Dim buttonInfo = From Button In buttons _
From info In db.ControlDatas _
Where info.Name = Button.Name _
AndAlso info.Language = Me.ComboBox1.SelectedValue.ToString _
Select Button, info.Description
For Each item In buttonInfo
item.Button.Text = item.Description
Next
End Sub
请注意上面上面代码里的buttonInfo,如果你注意到我们Select的是Button和info.Description,你就应该意识到buttonInfo是一个“匿名类型(anonymous type)” 的list【匿名类型,简单的说就是编译器生成的临时类型,这个类型不需要用户自己声明,但是可以像用一般的类型一样存取对象的各个字段】– 当我们使用它的时候,编译器会为我们创建一个匿名类型的list。 如果我们选择语言,button的文本就会改变:

最后一件事情是创建元数据,我要创建一个简单的XML文件以存储前面的form和数据库的元数据。这可以用LINQ to XML和 XML literals技术来实现。很简单,唯一的小把戏是我们需要使用group join, 这是因为有些控件不在数据库中,并且一些控件有多行信息。我们有零个或多个ControlData对象与每个控件相匹配【因为有多个Language】,我用group join来将这些描述查询出来,并作为子查询(sub-query)放到<Text>元素里,然后将<Text>放到<Control>元素的最后,而select出来的<Control>元素被放到了根节点<Form>的下面:
Private Sub cmdMakeXML_Click() Handles cmdMakeXML.Click
Dim formData = <?xml version="1.0"?>
<Form
name=<%= Me.Name %>
height=<%= Me.Height %>
width=<%= Me.Width %>
top=<%= Me.Top %>
left=<%= Me.Left %>
font=<%= Me.Font %>>
<%= _
From item In Me.Controls _
Let c = CType(item, Control) _
Group Join info In db.ControlDatas On info.Name Equals c.Name Into Group _
Order By c.Name _
Select <Control
type=<%= c.GetType %>
name=<%= c.Name %>
height=<%= c.Height %>
width=<%= c.Width %>
top=<%= c.Top %>
left=<%= c.Left %>
font=<%= c.Font %>>
<%= From info In Group _
Order By info.Language _
Select <Text language=<%= info.Language %>>
<%= info.Description %>
</Text> %>
</Control> _
%></Form>
My.Computer.FileSystem.WriteAllText("Form1.xml", formData.ToString, False)
Process.Start("notepad.exe", "Form1.xml")
End Sub
下面是生成的XML文件:

LinQ真的很强大,她为我们简化了很多常见的操作,比如从多个form里查询数据,数据库/对象集合/XML操作等。我附上了刚才的例子,但是请注意如果你用的不是Visual Studio 2008 Beta 2版本的话,有些特性可能不会正常工作【你可能还需要重新做一遍添加LINQ to SQL class的工作(在Solution Explorer里删除DataClasses1.dbml,重新添加,拖拽数据表)】。如果想学习更多关于LinQ的知识,请查看MSDN的LinQ Project。
相关程序: http://blogs.msdn.com/bethmassi/attachment/4037074.ashx
Enjoy!
多窗体之间数据处理
[原文作者]: Beth Massi
[原文链接]: Using Data Across Multiple Windows Forms
最近我遇到很多关于多窗体之间数据交互处理的问题,如果你已经看过我的Forms over Data系列课程,你应该了解到:怎样建立一个database、连接它、保存你的数据、以及在代码中操作数据。这篇文章是对那些课程的扩展,这里我将利用一个示例描述怎样基于一个DataSet创建多窗体程序,我们将利用Northwind数据库中的Catagories表和Products表作为示例。
根据具体情况,实际上有很多方法可以将多个窗体连接到同一个DataSet上,这里我将用一个比较典型的例子来说明一下:从一个表格窗体中显示可编辑的详细信息窗体,如下图所示:

首先建立一个工程,将Form1窗体(也就是主窗体)的Text属性设置为Catagories and Products。
通过数据源窗口新建一个Data Source对象;选择SQL Server Database File,数据库为Northwind.mdf,选择Categories 和 Products两个表,将DataSet名称设为CategoriesProductsDataSet。
完成以后会有一个CategoriesProductsDataSet.XSD被创建(参见One-to-Many video)。当DataSet创建完成后,将CategoriesProductsDataSet中Categories 和 Products两个表从数据源窗口拖拽到Form1窗体中(注意:Products为Categories下面的节点)。
此时主窗体中的两个DataGridView将分别显示Categories和其相关的Products信息,窗口底部将会创建两个BindingSource,并且它们被绑定到了相应Grid的DataSource属性中,与此同时,有一个BindingNavigator工具条出现在窗体上。现在我们想要做的是创建第二个窗体以编辑产品的详细信息。
我首先对Categories 表和 Products表的TableNewRow事件处理方法添加了两行代码,以便使数据处理更简单,在这个事件处理方法中我只是对一些非空字段设置了默认值。如要打开DataSet的后台代码进行编辑,只要用鼠标右键点击Solution_Explorer窗口中的CategoriesProductsDataSet.XSD文件,接着选择"View Code"就可以进入代码编辑窗口,在这里你还可以对一些字段进行简单验证,就像我在video on adding validation中演示的那样。
Partial Class CategoriesProductsDataSet
Partial Class CategoriesDataTable
Private Sub CategoriesDataTable_TableNewRow(ByVal sender As Object, _
ByVal e As System.Data.DataTableNewRowEventArgs) _
Handles Me.TableNewRow
'Set defaults for non-nullable fields
Dim category As CategoriesRow = CType(e.Row, CategoriesRow)
category.CategoryName = "New Category"
End Sub
End Class
Partial Class ProductsDataTable
Private Sub ProductsDataTable_TableNewRow(ByVal sender As Object, _
ByVal e As System.Data.DataTableNewRowEventArgs) _
Handles Me.TableNewRow
'Set defaults for non-nullable fields
Dim product As ProductsRow = CType(e.Row, ProductsRow)
product.ProductName = "New Product"
product.Discontinued = False
End Sub
End Class
End Class
接着我添加了一个新窗体Form2到工程中(也就是详细信息窗体,用来编辑产品的详细信息),将Form2的Text属性设置为Products Detail。
将数据源窗口中Products的显示属性设为details,并将它拖拽到Form2中,这次用不到BindingNavigator工具条,将它删除,并在窗体底部添加两个按钮:OK和Cancel。
然后删除下面的ProductsTableAdapter组件,打开Form2窗体的后台代码,在Form.Load事件处理方法中删除编辑器自动生成的Fill代码,因为我们不需要重新从数据库中填充DataSet,而是将数据从主窗体传递到详细信息窗体中。
此时,传递数据最简单的方式是在详细信息窗体的后台代码中创建一个新的构造函数,将主窗体上我们正在编辑的CategoriesProductsDataSet和打算编辑的Product行的主键作为参数。这样我们就可以在详细信息窗口中设置ProductBindingSource的DataSource 和 Filter 属性值, 以使它显示选中的行,从而保持主窗体和详细信息窗体同步。在详细信息窗体后台代码中类定义的下方输入“Sub New”, 并按回车键来自动生成正确的构造函数调用,接着修改它的签名和设置ProductBindingSource属性。
Sub New(ByVal ds As CategoriesProductsDataSet, ByVal id As Integer)
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
' Set the DataSource of the BindingSource and then set the Filter
' so that the correct row will be displayed on the detail form.
Me.ProductsBindingSource.DataSource = ds
Me.ProductsBindingSource.Filter = "ProductID = " & id.ToString
End Sub
回到主窗体中,在工具条上添加一个按钮,并把他的Text属性设为Edit Product Detail,双击按钮进入事件处理方法,加入如下代码:
Private Sub ToolStripButton1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles ToolStripButton1.Click
Me.ProductsBindingSource.EndEdit()
If Me.ProductsBindingSource.Position > -1 Then
'Get the current product row
Dim row As CategoriesProductsDataSet.ProductsRow
row = CType(CType(Me.ProductsBindingSource.Current, _
DataRowView).Row, CategoriesProductsDataSet.ProductsRow)
'Open the product detail form passing the dataset and the product ID
Dim frm As New Form2(Me.CategoriesProductsDataSet, row.ProductID)
frm.Show()
End If
End Sub
此方法中,首先调用了ProductsBindingSource.EndEdit(),这样在打开详细信息窗体编辑Product之前,所有在主窗体中所做的改变被保存到DataSet中。将ProductBindingSource.Current属性赋值给ProductRow来获取当前Product行,并将ProductID和主窗体中的CategoriesProductsDataSet传递给详细信息窗体的构造函数。
这里我允许用户打开任意数量的详细信息窗体,但是如果你想一次只打开一个窗体,只需要将frm.Show()修改为frm.ShowDialog();这个例子演示了编辑当前行,如果没有任何行被选中,将不会弹出窗体;这种情况下,如果你想在详细信息窗体被打开之前自动添加一新行,可以在EndEdit()之前调用ProductBindingSource.AddNew()(提示:如果这样做,那么在DataTable partial class中像我前面所描述的那样,对非空字段设置默认值是非常重要的)。
最后对按钮OK、Cancel和Form2 Close添加事件处理方法,以确认是否保存对Product所做的修改。代码如下:
Private Sub Form2_FormClosing(ByVal sender As Object, _
ByVal e As System.Windows.Forms.FormClosingEventArgs) _
Handles Me.FormClosing
Me.ProductsBindingSource.CancelEdit()
End Sub
Private Sub cmdCancel_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cmdCancel.Click
Me.ProductsBindingSource.CancelEdit()
Me.Close()
End Sub
Private Sub cmdOK_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cmdOK.Click
Me.ProductsBindingSource.EndEdit()
Me.Close()
End Sub
我已附加了一个完整的基于Northwind数据库的代码示例,里面也包括了我在这个视频和这篇文章中演示的怎样恰当地保存相关的DataTables。
记住,要在窗体中很好地对数据进行处理,关键是使用BindSource对象,这个类使数据处理和窗体控制变的非常简单。更多信息请参见Understanding Data video。
附件: MultiFormSample.zip
[原文作者]: Timothy Ng
[原文链接]: Lambda Expressions and Expression Trees
最近我的关于Lambda表达式的文章被发表在MSDN杂志上。在此篇文章中,我将那篇文章中的某些理念做一个拓展。如果你们对这篇文章中有什么不理解的,尽可以参考那篇文章。
|
Dim f As Func(Of Integer, Integer) = function(x) x * 2
Dim r = f(10) |
这个示例的第一行生成一个Lambda表达式,并将其奇迹般地赋给一个可调用委托f,第二行调用f, 并返回值20.
|
Dim e As Expression(Of Func(Of Integer, Integer)) = function(x) x * 2 |
在这个代码示例中,e不是一个可调用委托,而是表达式树。表达式树是lambda表达式的一种数据表示形式,它能使你更方便地读取和推断Lambda表达式。
默认情况下,拓展了Ienumerable接口的表达式能被赋给Lambda表达式;而拓展了Iqueryable接口的查询表达式只能被表达式树赋值。因此,当你生成一个带IQueryable类型查询的时候,编译器将生成一个表达式树而不是Lambda表达式。
在这个问题上,我组里的一个开发人员,Scott,曾经写过一篇很好的文章。他在那篇文章里用了非常好的图表来表现说明一些Lambda树的表达式树表示形式。
你能用表达式树做些什么呢?
大致上,你可以做两件事:
1. 推断表达式树内数据,并对它做自定义处理。你也能将表达式树中的数据转换到其他的域的数据形式(如XML).
2. 用 .Compile() 方法去将一个表达式树转换为一个委托,然后你可以执行它。
3. 编译包含动态条件的查询:参考Jonathan’s的文章,写的非常好的。他举出了大量的例子告诉你如何巧妙地处理那些表达式树和编译你的带自定义条件的lambda表达式。
其中第二条是很有趣的,因为它意味着你能将一个lambda表达式复制进一个表达式树,推断它,并将它转换为一个可调用委托。
|
Dim e As Expression(Of Func(Of Integer, Integer)) = function(x) x * 2
Dim f = e.Compile
Dim r = f(10)
|
在这个示例中,变量r的值是20,和我们之前举的将lambda表达式直接赋给一个委托类型得到的结果一样。
这个为什么说它有趣呢?
没有表达式树的话,我们是不可能用描述格式来表示一个表达式的。他们唯一的表示方式是IL (中间语言), 这种语言对于重新表述用户的意图来说太低层次了。然后,表达式树对一个表达式提供了一个高级的树表示方式来表现表达式,这样你就能很容易地理解用户的该表达式的意图。
如下是一些可能比较有趣的表达式树的用法:
1. 推断表达式,然后将他们转换为委托并调用他们;
2. 将表达式转换为SQL, 这样他们就能被服务器执行了 (这是linq到sql所做的工作)
3. 将表达式转换为XML并将他们写到磁盘上;
4. 将表达式转换为自定义形式并将他们通过某种网络协议发送到能将之通过某种格式重编译为表达式树,重新生成表达式分析并执行他们的服务器上。
在后面的几篇文章中,我将特别讲述关于VB编译器生成的表达式树,以及VB树与C#树之间的区别以支持VB同语义学。
我也将对你们能做的事情给出一些小技巧,这样任何你们所写的用到表达式树的类库将为VB开发程序员提供最好的体验。
[原文作者]: Young Joo
[原文链接]: New Data Tools Features in Visual Studio Orcas
下面是 Visual Studio 2008中数据工具方面新特性的列表。 首先让我们了解一些术语。
O/R Designer——对象—关系设计器
LINQ——语言集成查询
Hierarchical Update——级联更新
N –Tier——多层架构
SQL Compact Edition/ SQL CE——SQL精简版
DataSet——数据集
Typed DataSet——强类型数据集
Local Data Cache——本地数据缓存
对象—关系设计器(O/R Designer)
对象—关系映射技术允许您将关系数据库映射为对象。 一旦完成映射,您就可以将他们当成普通对象来处理,无需编写复杂的数据访问逻辑就可以轻松地将更改提交回数据库。对于这种增强型的数据访问技术,目前已有许多不同的实现机制。 但是,综观目前O/R 映射实现工具,并未带有易于创建和修改映射的图形设计器。
Visual Studio 中的对象-关系设计器正是Microsoft处理对象—关系到底应该如何映射的回答。 图形设计器它允许您轻松地将数据库对象,如表和存储过程映射为 LINQ到SQL的类和方法。只需将数据库对象从数据库管理器拖放到设计器。 设计器就会自动创建映射并生成正确的 LINQ到SQL的代码。您还可通过设计器修改映射的不同方面的配置,设计器会自动更新代码。关联和继承关系也很容易地创建。支持存储过程这一特性也很独特。对于INSERT、UPDATE和DELETE操作,您可以基于存储过程作为对象映射的方法来替代默认的操作。
如果您已经体验过三月份的LINQ的CTP版本,您可能记得那时我们提供的对象—关系设计器叫做DLinq 设计器。这就是下一代对象-关系设计器的预览版,目前更名为O/R设计器。

强类型DataSet的级联更新
持续跟踪跨多个相关的datatable的所有插入、更新和删除操作,并将更改以正确顺序提交回服务器,这在向来不是一个简单的任务。如何在确保客户的新订单被正确地添加到系统中,同时更新该客户的送货地址并删除其中一个已被取消的旧订单?
具有级联更新支持的强类型DataSet中,您只需调用我们新添加的TableAdapterManager组件的UpdateAll() 方法即可自动完成所有操作。它负责收集所有更改并将它们按正确顺序提交给服务器。当然,所有工作都会放在一个事务中。
我们相信这将极大的提高使用强类型DataSet来创建数据应用程序开发人员的工作效率。 您可以在三月份的 Visual Studio Orcas CTP版本中尝试这一新特性.
强类型DataSet中多层架构(N-tier)的支持
我承认,Visual Studio 2005 中生成强类型DataSet的代码并未对多层架构提供很好的支持。如果您打开强类型DataSet的代码文件,您会注意到我们已经很好的将类型从数据访问逻辑分开。DataSet和强类型的datatables是在一个强类型DataSet中声明的,而所有 TableAdapter 类则是在单独的命名空间中声明的。但是我们将它们放在了一个代码文件中。这对于大多数应用程序来说没有多大问题。但在构建多层架构的应用程序时, 将他们放在一个文件中并没有多少帮助。对该问题更细节方面的讨论请参考Steve Lasker的Splitting Typed Datasets from TableAdapters一文。他在该文中提出的解决方案是打开生成的代码并手动的将类型声明拷贝到一个新的类文件中。尽管这的确使得您可以在 N 层架构中使用强类型的DataSet。但是这个方案最大的缺憾就是由于需要您手动修改生成的代码,任何时候您在设计器中对强类型DataSet进行了更改,您就不得不再次拷贝类型声明部分的代码,很是繁琐。
在 Visual Studio 2008中,您可以指示设计器将DataSet代码生成的解决方案中的另一个项目中而无须手动的拷贝。并确保您在设计器中的进行的更改可以自动更新到指定的项目中去。同时您仍可选择保持现有的2层架构模型,设计器仍将所有内容生成到单个文件中。当您已准备好将2层变为 N 层时,只需告诉设计器哪个项目用来存放DataSet即可。简单吧!此特性的预览版在三月份的CTP of Visual Studio Orcas中就有,Beta 1版本中的该特性更加的完善。
使用SQL精简版的本地数据缓存
SQL精简版为开发人员带来了许多激动人心的方案。最有意思的情况是使用SQL 精简版数据库文件作为不经常更改的数据的本地缓存。例如,您的应用程序可能会在与远程服务器交换订单信息时,将产品列表保留在本地缓存中。在需要的时候,您可以将产品列表与远程服务器进行同步,但是大多数情况下,使用的还是还是本地缓存。
由于SQL精简版是系统开销很少的轻量级数据库,因此作为本地数据缓存正合适。可以使用 ADO.NET的同步服务来同步远程数据库服务器和本地 SQL精简版数据库文件之间的数据。不错吧?但是该如何设置呢?
Visual Studio 2008包含了一个新的项目模板,叫做“本地数据缓存”。将“本地数据缓存”添加到您的项目中将会创建.sync的XML文件,该文件用于描述什么数据将被同步。此文件带有设计器以便您可以轻松地进行同步配置。设计器还会创建 SQL精简版数据库文件作为本地数据缓存,并生成与同步服务进行交互的代码。此特性的预览版在三月份的CTP of Visual Studio Orcas中就有,Beta 1版本中的该特性更加的完善。
数据工具在其他方面的增强
上面四个是在 Visual Studio 2008中最主要的数据工具的新特性。但同时我们也大量的增强了现有的数据工具的功能。如果有机会,我单独写一篇文章描述我们所做的这些工作。
[原文作者]: Paul Yuknewicz
[原文链接]:
几个星期之前,我提到我现在所从事项目的部分工作。接着足球,网球和开学采购旺季…喔!我知道我不该转移话题,不过剩余的部分我不能再多说了。现在,我已经开始把 Charles Petzold 所写的《Applications = Code + Markup》一书中的所有代码示例整理出来,再用VB重写这些代码并发布到VB社区。我也邀请了一帮家伙做同样的事情,因为这是学习WPF的好方法,然而为了客户,今后几年我将关注(小型程序开发者以及IT部门),这是隐藏在我疯狂行为身后的原动力;特别是对那些一直在使用VB6的家伙们。
同我共事的项目经理Paul Yuknewiczu促使我联系Charles并和他达成协议,允许我发布他书中所有的示例代码的VB版。在家时我已经使用VB重写了十个章节的示例代码,但为了达到我所要求的代码质量,我必须重头再来一遍。 所以今天我在附件中向你们提供第一章节的示例代码。另外,我在Visual Studio 8 beta 2中创建了相关的项目文件以鼓励你们下载最新的代码。
出于下述的几个理由,我邀请你们同我一起进行WPF的学习之旅。
1. 我一直认为VB是要用来快速创建应用程序的。WPF 是一个非常强大的技术,我期待建立的社区里面有着一堆这样的程序员,他们相互分享最终用户体验的各种想法,比如不再使用灰色的窗体和白色的文本框。
2. 我们花费了大量的投资在Silverlight上。随着时间的推移,Silverlight也在不断的完善,不久的将来,使用WPF创建的强悍的桌面应用程序只需做些小小的努力就能转变成Silverlight程序。
昨天,我们一伙人把自己反锁在房间里琢磨如何为小型程序开发者和IT部门改善Visual Basic。此前我曾说过, 我们正在寻求这方面的人,希望能从他们那里了解更多关于使用VB快速开发应用程序时的痛处。现在,我们将更进一步。不管你在哪,只要你愿意贡献一点时间帮助我们定制我们的产品,请联系我(patd@microsoft.com)
最后,在我去和我的孩子们进行足球游戏之前,我想提醒大家,我们已经建立了一个小型程序开发人员论坛。欢迎大家来这里分享经验或者提问。
Patrick Dengler
附件:AppsCodeMarkup_VB.zip