新消息:开发者开始趋向于在VB.Net中进行关于XML的工作了

 
 
       Visual Basic 9.0 提供了一个新的功能XML Literals,它将使得对XML的编程更加简单方便,并且在很大程度上的减少了我们的代码量。实际上,XML Literals使得我们用Visual Basic去做XML变得如此简单,以至于很多C#开发者也开始趋向于在VB.NET中来进行XML相关工作!如果想要了解更多信息,请参看下面来自TechEd US的内容:

在VB中LINQ To Dataset如何工作

[原文作者]Jonathan Aneja
 
      LINQ的核心是要求可以对任何数据源进行可查询,意味着它必需实现IEnumerable接口。(实际上实现起来远比这个复杂,需要详细了解的请参阅Visual Basic 9.0 Language Specification)。现在当我们使用LINQ to Dataset时遇到了一个问题:DataTable没有实现IEnumerable,我们怎样去建立对它的查询呢?
之前我们了解到Visual Studio 2008中包含一个程序集System.Data.DataSetExtensions.dll,里面定义了一个扩展方法AsEnumerable()。形式如下:
 
                <Extension()> _
                Public Function AsEnumerable(source As DataTable) As EnumerableRowCollection(Of DataRow)
 
       这个函数接受一个DataTable的参数,返回一个实现IEnumerable的对象,从而可以使用LINQ的标准查询操作。你要做的就是导入程序集System.Data(工程模板默认已经做了),然后就可以调用AsEnumerable()来使用针对DatasetLINQ
 
                Dim customers = TestDS.Tables("Customers")
 
                Dim franceCustomers = From cust In customers.AsEnumerable() _
                          Where cust!Country = "France" _
                          Select cust
 
      现在有一个有趣的事是在VBLINQ的工作方法,不需要在代码中明确地调用AsEnumerable()来实现编译通过。其实是这样的,尽管Datatable没有实现IEnumerable接口,然而编译器通过搜寻特定的方法来帮助实现,该方法能够使Datatable转变为某种可查询的类型。当VB编译器遇到基于某个类型的LINQ没有实现IEnumerable, IEnumerable(Of T), IQueryable, 或者IQueryable(Of T),它将按顺序做下面一些事:
1. 看类型中是否有一个一致的查询方法可见。
2. 看类型中是否有一个名字为AsQueryable的实例方法,改方法返回一个可查询的类型。
3. 看类型中是否有一个名字包含AsQueryable的扩展方法,改方法返回一个可查询的类型。
4. 看类型中是否有一个名字为AsEnumerable的实例方法,改方法返回一个可查询的类型。
5. 看类型中是否有一个名字包含AsEnumerable的扩展方法,改方法返回一个可查询的类型。
      其中任何一步,如果编译器发现一个匹配的方法,将插入一个对它的调用。对于Datatable,当命名空间System.Data已经导入时,编译器在第五步为其发现了一个匹配方法,于是建立了一个对AsEnumerable的调用。结果你就能够写如101 LINQ SamplesLINQ to Dataset的代码了:
 
        Dim customers = TestDS.Tables("Customers")
 
        Dim franceCustomers = From cust In customers _
                              Where cust!Country = "France" _
                              Select cust
 
      实际上就是要你提供一个AsEnumerable的扩展方法,该方法返回一个可查询的类型,从而使LINQ起作用。
注意对于强类型的Datasets,你不需要调用AsEnumerable,因为它们继承自TypedTableBase(Of T),该类型实现了IEnumerable,它是VS2008中的一个新类型,在VS2005Dataset设计器将生成继承自DataTable的代码,并且显式地实现IEnumerable
 
 

LiveRun – 即时查看程序输出的VS插件

         
      [原文作者]Lucian
      [原文链接]LiveRun – a VS plugin to see the output of your program immediately
 
     假设你正在会议室里演示即时编译的程序。有什么最佳的方式来进行演示呢?
     你还在往代码窗口里输入代码?这样,你得依赖听众的想像力――他们在脑海中构造这个程序是怎样运行的。此外,你还得依赖他们相信你的代码真的像和你所说的一样运作。
     或者你要不停的运行你的代码,这样程序的输出窗口会弹出,然后听众可以看到代码的实际工作情况?这是有风险的,因为每次切换代码和输出窗口会中断流程。并且你得依赖听众会记得他们每次看到的输出信息所对应的代码。
     我想这是一个可以被技术所解决的问题!我为Visual Studio 2008 写了一个小插件。它关注的是当前文本缓冲中所包含的代码,并在后台编译它,然后在前端窗口中显示输出。这一系列动作大概每2秒钟重复一次。你甚至不需要保存再重新编译代码就能看到输出了。这个插件特意制做成独立的控制台程序因为它用不着输入。这是一幅截图:
                    
     

     源代码很小而且简单易懂,并且可以从上面的链接中获得。
 
     有两个关键的地方。第一是要使用多线程进行处理。我想让源代码在后台线程中编译因此它并不会影响Visual Studio UI. 但是要获取当前缓冲的文本你必须使用UI线程,而且要显示输出你也得使用它。我使用System.Timers.Timer在后台线程触发它的事件,同时为需要UI线程的任务调用form.Invoke(…)
     我还使用了“non-AutoReset”计时器。我让它获取源码并进行编译+运行+显示,暂停2秒后,再去获取源码并进行编译+运行+显示,如此循环。换句话说,计时器的时间间隔必须设为2 秒在处理完先前的定时事件之后。
 
”’ <summary>
”’ OnTimer 处理non-autoreset计时器信号。它运行在后台线程中。从当前的缓冲区中获取
”’ 源代码并编译它,然后显示输出。
”’ </summary>
”’ <remarks></remarks>
Sub OnTimer() Handles t.Elapsed
    Try
        Dim oldsrc = src
                ‘我们在后台线程中。但只能从 UI 线程获取源
        此委托将获取源代码并将它存储在"src"字段中
        f.Invoke(New Action(AddressOf GetSource))
        If src <> oldsrc Then
            Dim oldoutput = output
            在后台编译并运行
            output = CompileAndRun(src)
            If output <> "" OrElse oldoutput = "" Then
                在屏幕上显示输出必须在UI线程中完成
                此委托从"output"字段中得到要输出的内容并显示它
                f.Invoke(New Action(AddressOf ShowOutput))
            End If
        End If
    Finally
        t.Start()
    End Try
End Sub
 
 
     另一个关键时刻与如何运行代码并捕获它的输出有关。在“My”命名空间中,VB在这方面有很多有用的函数。我的主要问题是合理的处理异常不留任何遗漏。(注意:获取临时文件名的代码并不完全准确:      事实上你在某个状态之前得到一个未使用的临时文件名并不意味着这个文件名会一直不被使用;同样,它也并不意味着添加了“.vb”扩展名的临时文件名会一直不被使用。但是把这种情况处理的更准确看直来并不十分划算;不管怎样,在异常处理中我们将所有的问题恢复到正常状态。)
 
Function CompileAndRun(ByVal src As String) As String
    Dim fn_exe = ""
    Dim fn_src = ""
    Dim vbc As System.Diagnostics.Process = Nothing
    Dim exe As System.Diagnostics.Process = Nothing
    Try
        准备编译
        fn_src = My.Computer.FileSystem.GetTempFileName() & ".vb"
        My.Computer.FileSystem.WriteAllText(fn_src, src, False)
        fn_exe = My.Computer.FileSystem.GetTempFileName() & ".exe"
        Dim framework = Environment.ExpandEnvironmentVariables("%windir%\Microsoft.Net\Framework")
        Dim latest_framework = (From d In My.Computer.FileSystem.GetDirectories(framework) Where d Like "*\v*" Select d).Last
 
        编译
        vbc = System.Diagnostics.Process.Start(New ProcessStartInfo _
                            With {.CreateNoWindow = True, _
                                  .UseShellExecute = False, _
                                  .FileName = latest_framework & "\vbc.exe", _
                                  .Arguments = String.Format("/out:""{0}"" /target:exe ""{1}""", fn_exe, fn_src)})
        Dim vbc_done = vbc.WaitForExit(3000)
        If Not vbc_done Then Return ""
        If vbc.ExitCode <> 0 Then Return ""
 
        运行
        Dim pinfo = New ProcessStartInfo With {.CreateNoWindow = True, _
                                               .UseShellExecute = False, _
                                               .FileName = fn_exe, _
                                               .RedirectStandardOutput = True}
        exe = New System.Diagnostics.Process With {.StartInfo = pinfo}
        exe.Start()
        Dim output = exe.StandardOutput.ReadToEnd
        Dim exe_done = exe.WaitForExit(3000)
        If Not exe_done Then Return ""
        Return output
    Finally
        我们可以巧妙的关闭VBC进程
        If vbc IsNot Nothing Then
            If Not vbc.HasExited Then
                Try : vbc.Kill() : Catch ex As Exception : End Try
                Try : vbc.WaitForExit() : Catch ex As Exception : End Try
            End If
            Try : vbc.Close() : Catch ex As Exception : End Try
            vbc = Nothing
        End If
 
        我们可以巧妙的关闭EXE
        If exe IsNot Nothing Then
            If Not exe.HasExited Then
                Try : exe.Kill() : Catch ex As Exception : End Try
                Try : exe.WaitForExit() : Catch ex As Exception : End Try
            End If
            Try : exe.Close() : Catch ex As Exception : End Try
            exe = Nothing
        End If
 
        删除剩余的文件
        Try : My.Computer.FileSystem.DeleteFile(fn_exe) : Catch ex As Exception : End Try
        Try : My.Computer.FileSystem.DeleteFile(fn_src) : Catch ex As Exception : End Try
    End Try
End Function
 
 
     一直以来,我都很喜欢听到建议,bug 修复,代码改进以及评论!

TableAdapter和多组数据结果

[原文作者]Young Joo                                          
       
     很多人问我TableAdapter是否能够从存储过程里读取多组数据结果。最直接的回答是:不能。你不能通过TableAdapter.Fill()方法来得到一个Dataset。但是我们可以通过另一种简单的方法来实现。
 
DataAdapter.Fill()和多组数据结果
 
     TableAdapter.Fill()方法通过调用DataAdapter.Fill()从数据库中读取数据。DataSet.Fill() 方法可以从存储过程里读取多组数据结果。为了获得多组数据结果,可以应用DataAdapter.Fill()的一个重载方法,它将Dataset作为参数,这样就可以把存储过程的多组数据结果返回给包含有多个表的Dataset
    这里,我们通过一个简单的示例来演示一下这种方法是怎样实现的:
    假设在Northwind数据库里有一个存储过程dbo.spSelectCustomersOrders
    CREATE PROCEDURE spSelectCustomersOrders
    AS
    BEGIN 
       SET NOCOUNT ON
       SELECT * FROM Customers 
       SELECT * FROM Orders
    END
   GO
     下面的代码调用了这个存储过程,并且把2组数据结果存储在Dataset里。
    Dim myConn As New System.Data.SqlClient.SqlConnection
    Dim myAdapter As New System.Data.SqlClient.SqlDataAdapter
    Dim mySelectCommand As New System.Data.SqlClient.SqlCommand
    Dim myDataset As New System.Data.DataSet

    myConn.ConnectionString = "Data Source=.\SQLExpress;Initial Catalog=Northwind;Integrated       Security=True"
    mySelectCommand.Connection = myConn
    mySelectCommand.CommandText = "dbo.spSelectCustomersOrders"
    myAdapter.SelectCommand = mySelectCommand
    myAdapter.Fill(myDataset)

    For Each table As System.Data.DataTable In myDataset.Tables
       Console.WriteLine("Table Name:" & table.TableName)
    Next

    代码的输出形式如下:
     Table Name: Table
     Table Name: Table1
    
     我们可以看到,DataAdapter.Fill()方法执行了存储过程,并且把2组数据结果分别存储在2个数据表里。
 
TableAdapter的解决方案
 
     然而,为什么TableAdapter.Fill()方法不能够正确地处理多组数据结果?那是因为TableAdapter.Fill()调用的DataAdapter.Fill()方法是以DataTable作为参数,而不是Dataset。这种情况,我们只需要在TableAdapter里创建一个新的Fill方法,令其调用以Dataset为参数的DataAdapter.Fill()方法。
     假设这里有一个包含CustomersOrdersNorthwindDataset.xsd文件。让我们用上面的存储过程来实现新的Fill方法。把下面的代码加到partial class文件里。(在Dataset Designer上,可以通过双击或者右键选择"View Code"来进入partial class,当然也可以手动创建一个空的class文件。)
    Namespace NorthwindDataSetTableAdapters
        Partial Public Class CustomersTableAdapter
            Public Function FillCustomersOrders(ByVal dataSet As NorthwindDataSet) As Integer 
                 Dim
multiSelectCommand As New System.Data.SqlClient.SqlCommand 
                 Dim returnValue As Integer 

                 multiSelectCommand.Connection = Me.Connection 
                 multiSelectCommand.CommandText = "dbo.spSelectCustomersOrders" 
                 Me
.Adapter.SelectCommand = multiSelectCommand 
                 ” Map auto-created Table1 that holds the second result-set (Orders rows) to 
                 ” Orders DataTable in our Dataset. 
                 Me.Adapter.TableMappings.Add("Table1", "Orders"
                returnValue = Me.Adapter.Fill(dataSet) 

                Return
returnValue 
            End Function 
         End
Class
     End
Namespace

      有两点需要特别注意:
      首先,新的FillCustomersOrders是以Dataset为参数,这样当我们调用DataAdapter.Fill()方法时,数据结果就会准确地存储到Dataset里。
      第二,注意我们是怎样应用TableMapping将自动生成的数据表映射到Dataset里的Orders表。当应用DataAdapter.Fill()来读取多组数据结果,每一组数据结果都被单独地存储在Dataset的数据表里。默认情况下,这些数据表被命名为Table, Table1, Table2…,为了将这些数据标与Dataset里定义的数据表相对应,我们应用TableMapping。如果你打开NorthwindDataset.xsd后面的代码,在TableAdapter classInitAdapter()方法,你就会看到类似的代码:
    tableMapping.SourceTable = "Table"
    tableMapping.DataSetTable = "Customers"
    ” Colum mapping code skipped
     …
     Me._adapter.TableMappings.Add(tableMapping)
 
      这段代码是为了保证DataAdapter.Fill方法返回的数据表与Dataset里的数据表相对应。在我们FillCustomersOrders示例里,第二组结果包含的是Orders信息,所以我们在Table1Orders之间创建了映射关系,确保数据FillOrders表中。
     把以上代码添加到partial class后,你就可以调用FillCustomersOrders方法来fill CustomersOrders
     CustomersTableAdapter.FillCustomersOrders(Me.NorthwindDataSet)
 
性能的考虑
 
      有些情况下,这种方法的确很有效。但是这也要看情况,也许你会想到这个方法可以避免多次访问数据库,从而提高性能,但如果仅仅只需要获取一小部分数据,却应用这种方法一次读取了大量的数据,这同样也会降低性能,倒不如一次读取小部分数据,需要其它数据时,与数据库建立另一个连接,再读取。ADO.NET在处理多个数据库连接方面性能优化得还是不错的,很多情况下,都不至于导致性能瓶颈。总之我们只需要遵循最基本的原则:只在需要的时候,才去读取数据。
     但有些情况下,读取多组数据结果还是很有帮助的,所以,应用我在这里所介绍的方法吧,但时刻也不要忘记性能的问题。
 
相关资源