Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 52 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
52
Dung lượng
1,61 MB
Nội dung
sWhere += " AND RTRIM(FunctionName) = '" & sFunction & "'" Else sWhere = "WHERE RTRIM(FunctionName) = '" & sFunction & "'" End If End If ' Tack on the where clause. sSQL += sWhere ' Load the data. Try Dim da As New SqlDataAdapter(sSQL, CONNSTR) conn.Open() da.Fill(ds, "perfdata") conn.Close() Catch ex As SqlException conn.Close() Throw ex End Try Return (ds) End Function If a beginning date is supplied, we will only retrieve data later than that date. If an end date is supplied, we only retrieve data earlier than that date. If a username is sup- plied, only records that match that username will be retrieved. The same goes for the FunctionName. The bulk of the GetPerfData method is dedicated to building the WHERE clause based on these parameters. Once the WHERE clause is constructed, we simply retrieve the data using a DataAdapter. Lastly, we return the DataSet to the caller. Remember that DataSets are derived from MarshalByValueObject, and this can be serialized for Remoting. The code for the Analysis is more complex but works well. We will be calculating Minimum, Maximum, and Average values for all the data by FunctionName. There- fore, we need to get data back from the database by function name for all the function names. There are two steps in this process. First, we load a list of unique function names that currently reside in the database. Second, this list is used to load perfor- mance data for each function name that we just loaded. Take a look at the code, then we’ll talk. Public Function BasicStats() As DataSet Dim conn As New SqlConnection(CONNSTR) Dim dsFunctions As New DataSet() ' Holds returned functions Dim dsData As New DataSet() ' Holds all data for a function Dim dsStats As New DataSet() ' Our return DataSet Dim sSQL As String 134 Project 4 Dim sWhere As String Dim cmd As New SqlCommand() ' Get the list of functions we need to process. sSQL = "SELECT DISTINCT FunctionName FROM PerfData" Dim da As New SqlDataAdapter(sSQL, CONNSTR) Try conn.Open() da.Fill(dsFunctions, "functions") Catch ex As SqlException conn.Close() Throw ex End Try ' Get our return DataSet ready to be filled with stats. Dim dt As New DataTable("stats") dt.Columns.Add("FunctionName", _ System.Type.GetType("System.String")) dt.Columns.Add("Avg", System.Type.GetType("System.Double")) dt.Columns.Add("Min", System.Type.GetType("System.Double")) dt.Columns.Add("Max", System.Type.GetType("System.Double")) dsStats.Tables.Add(dt) ' For each row in the Functions list, load all records ' that match its name. Calculate stats for each name. Dim FunctionRow As DataRow Dim aRow As DataRow Dim avg As Double Dim min As Double Dim max As Double Dim iElapsed As Int32 ' Loop through each function, getting all records for that ' function and calculating stats. Try Dim tempRow As DataRow For Each FunctionRow In dsFunctions.Tables("functions").Rows ' Get the SQL statement ready and load performance data ' for the specified function name. sSQL = "SELECT * FROM PerfData " & _ "WHERE RTRIM(FunctionName)='" & _ "RTrim(FunctionRow("FunctionName")) & "'" Try da.SelectCommand.CommandText = sSQL da.Fill(dsData, "perfdata") Catch ex As SqlException conn.Close() Throw (ex) End Try ' Clear our statistics accumulators. Performance Testing with Remoting 135 avg = 0 min = 999999999999 max = 0 ' Go through each row in the dataset and ' accumulate statistical information. For Each aRow In dsData.Tables("perfdata").Rows iElapsed = CInt(aRow("ElapsedMillisecs")) avg += CDbl(iElapsed) If iElapsed > max Then max = iElapsed End If If iElapsed < min Then min = iElapsed End If Next ' Final average calc. avg /= dsData.Tables("perfdata").Rows.Count ' Load the data for this function into the dataset. dsData.Tables(0).Clear() ' Get ready for the next tempRow = dsStats.Tables(0).NewRow tempRow("FunctionName") = _ Trim(FunctionRow("FunctionName")) tempRow("Avg") = avg tempRow("Min") = min tempRow("Max") = max dsStats.Tables(0).Rows.Add(tempRow) Next Catch ex As Exception conn.Close() Throw (ex) End Try conn.Close() Return (dsStats) End Function As mentioned, we first need a list of function names from the database. However, we need a unique list with no repeats. The database can take care of this for us, using the DISTINCT clause, as shown: SELECT DISTINCT FunctionName FROM PerfData This will not load the same name twice, so we’ll only get a list of unique names. Once the list is loaded, we need to loop through it, processing each name and loading all performance data for each name. We create a DataTable object and then create four new columns for it: FunctionName, Avg, Min, and Max. We will be adding rows of data to it later. 136 Project 4 After the performance data for a given name is loaded, we have to deal with it. This means that we have to pack it into a DataSet for return to the caller. We are creating a DataSet manually, setting up a table with columns and stuffing data into it. The For Each loop, which iterates through the FunctionNames, starts. We construct a SQL statement based on the current FunctionName and load the data. Once the rows for that function name are loaded, we have to extract the data for each row and process it. We use another For Each loop to navigate the date and deal with it. This is what we do for the elapsed time (duration) of each row: ■■ For the Average calculation, we simply accumulate the elapsed time value. When the For Each loop is complete, we can use it to calculate the average for that FunctionName. ■■ If the value is lower than our Min value, we assign the new value to the Min variable. Each value is checked so that we find the lowest one and save it. ■■ If the value is higher than our currently stored Max value, we assign the new value to the Max variable. The processing and calculating of a given function name is done. Now we have to store the information in our manually created DataSet. The code shows how to do this using a temporary DataRow object. Once that’s complete, we’re done and ready for the next function name. When all the function names are done, we can return the DataSet to the caller. If we ever need to modify the code here, or decide to add to its functionality, it will be easy to redeploy because we’re hosting it on a single server. Clients need only to call it. Component Configuration We need a web.config file in the project directory (not the BIN directory) for the com- ponent. Our config file should look like this: <configuration> <system.runtime.remoting> <application> <service> <wellknown mode="SingleCall" type="prj04remote.prj04svc,prj04remote" objectUri="prj04remote.soap" /> </service> </application> </system.runtime.remoting> </configuration> Lastly, we must configure the component in IIS, just like we did for the walkthrough example. Open Internet Services Manager, select the Default Web Site, and select New Virtual Directory from the Actions menu. Name the virtual directory prj04remote, and point it to the directory where the project is, up to but not including the BIN directory. The component is now ready for remote use. Performance Testing with Remoting 137 TEAMFLY Team-Fly ® The Client Program Now that we can store, retrieve, and analyze performance data, we need to do some- thing with it. Presumably we would like to look at it in order to improve the perfor- mance of our software. To make this easier, as well as illustrate how to use the remote component, we will create a data viewer that shows the data and the analysis results. We’ll even graph it for you. Start by creating a new WinForms project in Visual Studio. Name it prj04client. Rename the main form to frmMain. Now add a reference to our remote component so that we can use it. Right-click on the References section in the Solution Explorer, and from the context menu, select Add Reference. Browse to the location where you created the remote component and its BIN directory, and add the reference. Drop a few controls onto the form, specifically those listed in Table 4.2. Once they are in place, we’ll start adding some really interesting code. Start the code by importing the SqlClient and Remoting namespaces, as follows: Imports System.Runtime.Remoting Imports System.Data.SqlClient The Remoting namespace will give us easy access to the RemoteConfiguration object. We’ll also be doing some data work, so the SqlClient namespace is useful. When the form is loaded, we take care of a couple details. We center the form in the window and set the caption on the DataGrid: Private Sub frmMain_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load dgPerfData.CaptionText = "Click Get Data button " Me.CenterToScreen() End Sub Table 4.2 The Controls for the Main Form CONTROL NAME PROPERTIES Label LblTitle Text=”Performance Data and Statistics”, AutoSize=True, Font=Arial 12pt Bold Button btnData Text=”Get Data”, Anchor=Bottom, Left, FlatStyle=Popup Button btnStats Text=”Get Stats”, Anchor=Bottom, Left, FlatStyle=Popup Button btnDone Text=”Done”, Anchor=Bottom, Right, FlatStyle=Popup DataGrid dgPerfData Anchor=Top, Bottom, Left, Right 138 Project 4 When the Get Data button is clicked, we want to load all the performance data from the database into the DataGrid. The remote component will take care of retrieving the data and sending it to our client. We’ll handle putting it into the grid as follows: Private Sub btnData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnData.Click Dim rc As New prj04remote.prj04svc() ' Our remote component Dim ds As DataSet Try ' Get the data ds = rc.GetPerfData() ' Bind it to the grid dgPerfData.SetDataBinding(ds, "perfdata") ' Title the grid dgPerfData.CaptionText = "Performance Data" Catch ex As Exception ' Oops MsgBox(ex.Message()) End Try End Sub Notice that we create an instance of our remote component just like we would cre- ate any other local component. The GetPerfData method in the remote component is called, which returns a DataSet that contains all the raw performance data. We then bind that DataSet to the DataGrid using a single statement. The DataGrid supplies a method called SetDataBindings that takes a DataSet and the name of a table to use within that DataSet. Once the data is loaded, we change the caption of the Data- Grid to indicate what data was being displayed. Lastly, we show our second form, for statistics display, when the Get Stats button is clicked. The form is loaded and displayed and then it takes over dealing with the sta- tistical display. Figure 4.2 shows what the form looks like when running and loaded with data. Figure 4.2 The Executing prj04client Program. Performance Testing with Remoting 139 The Statistics Form The real fun in this program is the statistics form. It shows the statistics by function name in text form as well as in a cute little graph that helps you visualize the data at a glance. It does most of its work in the form load event. Start by adding a new form to the project, naming it frmStats, and dropping controls into it as listed in Table 4.3. You will have to add the chart control to the toolbox by right-clicking on the toolbox and from the context menu, select Customize Toolbox. From the dialog, illustrated in Figure 4.3, and click the browse button. Locate the file mschrt20.ocx, which should be hiding out in your Windows\System32 directory. If the control is already listed in the dialog, just check it on. Table 4.3 The frmStats Controls CONTROL NAME PROPERTIES Button btnDone Text=”Done”, Anchor=Bottom, Right Label Label1 Text=”Performance Chart:” Label lblTextTitle Text=”Performance Data:” Label lblTitle Text=”Performance Statistics”, Font=Arial 12pt Bold MSChart chtStats Anchor=Top, Bottom, Left, Right RadioButton rb2D Text=”2D”, Anchor=Top, Right RadioButton rb3D Text=”3D”, Anchor=Top, Right TextBox tbStats Text=””, Anchor=Top, Bottom, Left, MultiLine=True, ScrollBars=Vertical Figure 4.3 The Customize Toolbox Dialog. 140 Project 4 Once the form is set up, you can start entering code behind it. Start by doing a little groundwork with namespaces and class variable: Imports System.Data.SqlClient Public Class frmStats Inherits System.Windows.Forms.Form Private bLoadDone As Boolean = False The local class variable bLoadDone is used to indicate that all the work in the form load event is done. This is important because if we try to change the chart type before the load is done (which occurs automatically once before the form load event is com- plete), then we’ll get a nice NULL error and a crash. One of the features that our form supports is the ability to switch between a 2D and a 3D chart. We use radio buttons to do this, and we need events to handle it and make the change. The following code takes care of this. Private Sub rb3D_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles rb3D.CheckedChanged If bLoadDone Then SetChartType() End If End Sub Private Sub rb2D_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles rb2D.CheckedChanged If bLoadDone Then SetChartType() End If End Sub Private Sub SetChartType() If rb3D.Checked Then chtStats.chartType = _ MSChart20Lib.VtChChartType.VtChChartType3dBar Else chtStats.chartType = _ MSChart20Lib.VtChChartType.VtChChartType2dBar End If End Sub The most interesting part of the code is the load event, where all the data retrieval and processing takes place. Here’s the code you should add to the load event: Performance Testing with Remoting 141 Private Sub frmStats_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim ds As DataSet ' Hold returned perf data. Dim rc As New prj04remote.prj04svc() ' Our remote component. Dim s As String RemotingConfiguration.Configure("prj04client.exe.config") Me.CenterToScreen() ' Load the statistics from the remote component. Try ds = rc.BasicStats() Catch ex As Exception MsgBox(ex.Message()) Me.Close() End Try ' Fill in the text data. As long as we're looping through ' the data, build the data array for the chart. Dim arrChartData(ds.Tables(0).Rows.Count, 5) As String Dim i As Int32 = 0 Dim aRow As DataRow ' This loop processes a row of data at a time. Each row ' equates to a single function for which performance ' statistics are being reported. For Each aRow In ds.Tables(0).Rows ' Set up the text display string for this function. s &= "Function: " & aRow("FunctionName") & vbCrLf s &= " • Average time (ms): " & CStr(aRow("Avg")) & vbCrLf s &= " • Min time (ms): " & CStr(aRow("Min")) & vbCrLf s &= " • Max time (ms): " & CStr(aRow("Max")) & - vbCrLf & vbCrLf ' Collect chart data for this function. arrChartData(i, 0) = aRow("FunctionName") arrChartData(i, 1) = CStr(aRow("Min")) arrChartData(i, 2) = CStr(aRow("Avg")) arrChartData(i, 3) = CStr(aRow("Max")) i += 1 ' Next element in chart data array Next tbStats.Text = s ' Set up and fill in the chart. chtStats.ChartData = arrChartData chtStats.RowCount = ds.Tables(0).Rows.Count chtStats.ColumnCount = 3 chtStats.ShowLegend = True ' Set up the column labels for the chart For i = 1 To chtStats.ColumnCount chtStats.Column = i 142 Project 4 Select Case i Case 1 : chtStats.ColumnLabel = "Min" Case 2 : chtStats.ColumnLabel = "Avg" Case 3 : chtStats.ColumnLabel = "Max" End Select Next i ' Now that we're done loading the form, it's OK to ' change the chart type. bLoadDone = True End Sub The first and all-important step is to set up the Remoting environment. Our call to RemotingConfiguration.Configure passes in the name of our configuration file, which is loaded and used to prepare our program for remote operations. Then we create an instance of the remote component and call its BasicStats method to get the analyzed data. Once that’s done, we need to extract it from the DataSet and do two things with it: Fill it into the text box and stuff it into the chart control. We iter- ate through each row in the DataSet and format a string that will go into the textbox. We keep adding to that string for each row in the DataSet. Once we have finished mov- ing through the data, we’ll pass it off to the control. We also fill the data into a two- dimensional array that we will hand off to the control when we’re done. After the loop is complete, we need to set up the chart control. We hand off the data array that we built to the control’s ChartData property. We set the row count and turn on the legend. Lastly, we add some text labels to the chart columns so that they display correctly. Set our done flag and we’re finished. Figure 4.4 shows what the form looks like when it is running and loaded with performance data. You can resize this form and enlarge the graph to see it better. Switch it to 2D graph mode for a more analytical view of the data, as shown in Figure 4.5. Figure 4.4 The frmStats Form during Execution. Performance Testing with Remoting 143 [...]... Generates random medium-duration performance data for a function called Calculate.” Label Label3 Text=” Generates random longduration performance data for a function called CreateReport.” Label lblTitle Text=”Remoting Test Application”, Font=Arial 12pt Bold Label lblStatus Text=”Ready.”, Font=Bold 145 146 Project 4 Private Class PerfLogger Private Private Private Private Private gSessionID As Guid iStart... instance void MyName() cil managed { // Code size 16 (0x10) maxstack 8 IL_0000: nop IL_0001: ldstr “NameSpace2.Class1” IL_0006: ldc.i4.0 IL_0007: ldnull IL_0008: call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox (object, valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle, object) IL_000d: pop IL_000e: nop... installation and create it from scratch, or you can add a deployment project to your application solution 163 1 64 Project 5 Starting a new installation from scratch is a good option if you don’t have access to the installation target’s source code or if you want to keep the deployment project separate (for example, so that different people can work on the application and the installation) You create a... 144 Project 4 Figure 4. 5 The frmStats Form with a 2D Graph The Configuration File The last detail is the configuration file for the client The following listing for the file prj04client.exe.config shows what it should be for this application Make sure it goes in the project’s BIN subdirectory ... in Figure 4. 6 149 150 Project 4 Figure 4. 6 The Test Application executing The Configuration File We need a configuration file for this application, as we did for our other client program This one is called prj04app.exe.config, goes in the project’s BIN directory, and looks like this: ... be located anywhere So when an assembly is needed, how does it find the code? The complete assembly location process is a little complicated; however, there are two primary locations for assemblies that make them easy to locate at runtime: s s The application directory s s The Global Assembly Cache The application directory is pretty obvious If the assemblies that make up an application are located... logging of data Let’s look at the code and then discuss it Table 4. 4 The Controls on the Test Program Form CONTROL NAME PROPERTIES Button btnDone Text=”Done” Button btnF1 Text=”GetData Function” Button btnF2 Text=”Calculate Function” Button btnF3 Text=”CreateReport Function” Label Label1 Text=” Generates random shortduration performance data for a function called GetData.” Label Label2 Text=” Generates random... Installation Installations have many parts that you can use to create installers, customize them, and make them do what you want All of them are accessible through the Visual Studio tools, and we’ll get to that shortly So what are they? Assemblies and files You know what assemblies are, the components of your application You might also want to add some supporting files, such as graphics or a database... platforms, information is entered into the database to track the files that are installed and where they are, along with other information It also tracks some installed common files that are shared among applications, much like registered COM components 155 156 Project 5 The Windows Installer naturally takes advantage of the Application Database It also provides functionality to uninstall applications,... create installation packages, or deployment packages, for the WinForms class library we built in Project 1, the bug-tracking system we built in Project 2, and the Web service from Project 3 ✄ You Will Need ✔ Visual Studio NET ✔ A basic knowledge of Visual Basic NET ✔ Compiled results of Projects 1, 2, and 3 The Project We’ll be creating several installation deployment packages for our previous projects . date is supplied, we will only retrieve data later than that date. If an end date is supplied, we only retrieve data earlier than that date. If a username is sup- plied, only records that match. data. We then bind that DataSet to the DataGrid using a single statement. The DataGrid supplies a method called SetDataBindings that takes a DataSet and the name of a table to use within that. view of the data, as shown in Figure 4. 5. Figure 4. 4 The frmStats Form during Execution. Performance Testing with Remoting 143 Figure 4. 5 The frmStats Form with a 2D Graph. The Configuration File The