10 Tips for Great .NET Programming
Whether you re interested in Windows Forms, ASP.NET, Web Services, or the .NET Framework, these tips help you exploit the still young .NET technology.
by Dino Esposito
The .NET Framework is larger than ever and filled with a huge number of classes and methods, but the developer community has yet to explore and understand most of this incredible volume of software features. What might appear to be a bug or a design flaw at first might be considered a significant strength after a second, more thoughtful look.
In light of this inevitable refinement process, sharing tips with other developers, although far from providing the definitive solution you might be looking for, is a way to steer you in the right direction when you build your the first .NET application. I ve compiled a list of 10 tips to make your .NET development more effective and productive. To help as many developers as possible, the tips span the technology s whole spectrum—from ADO.NET to ASP.NET, from the CLR to the Framework, and from Windows Forms to Web Services. Here they are, counting up to the ones I anticipate will have the most impact.
Tip 1: Shape Up Your DataGrid s Footer
The DataGrid control might feature a footer—that is, a row that summarizes part of the content shown in the page. The footer is hidden unless you enable it by setting the ShowFooter property to True. Once you do, the footer appears, but it has exactly the same number of columns as other rows. If this is fine with you, it isn t a problem. Otherwise, you ll need to add more fields to the footer or remove existing ones.
OnItemCreated is the key event to hook up. It s the event handler s signature:
void ItemCreated(Object s,DataGridItemEventArgs e)
This code lets you control the creation process for the footer and any other DataGrid item:
ListItemType itemType = e.Item.ItemType;if (itemType == ListItemType.Footer){ e.Item.Cells.RemoveAt(1); e.Item.Cells.RemoveAt(1); cellSummary.ColumnSpan = 3; e.Item.Cells[0].Text = "...";}
Make sure you intervene when the footer is being created, then grab the control instance representing the footer row. Then you can remove or add as many cells as you need. Don t forget to set the ColumnSpan property to the original number of columns to avoid rendering problems such as having the footer line look shorter or longer than the rest of the grid.
Tip 2: Use the Cache Object
In Active Server Pages (ASP), you were used to employing the Application object to detect global events and store application-wide data. Given many users from different pages can access the same data cell, you must use Lock and Unlock methods to serialize access and avoid conflicts and unpredictable results. In ASP.NET, together with the Application object, you have the Cache object, which has similar characteristics but ends up replacing the Application object in more than one instance.
Both the Cache and Application objects are collection data containers that make content visible across page and session boundaries. Both don t fully support Web farm and Web garden scenarios. A Web farm is a Web server architecture in which multiple servers host an application. In a Web garden scenario, multiple processes on the same server host an application.
Unlike the Application object, the Cache object isn t instantiated as soon as the first page of the application is loaded. The Cache object gets into the game only when you use it. In addition, the Cache object has features to minimize the amount of memory its data items occupy. You can set a relative or absolute expiration date for cached items as well as associate them with a priority and a decay factor. Then you have greater control over the status of your global data and can implement a special policy to degrade gracefully under low memory conditions. Additionally, the Cache object manages thread-safe objects and doesn t require Lock and Unlock.
Tip 3: Call a Service With GET or POST
A Web Service is primarily a URL that you connect to using HTTP. When you create a proxy class for a given Web Service, unless you specify otherwise, the command-line tool wsdl.exe serves you a class that uses the SOAP protocol to make remote calls to the Web Service.
Although the SOAP protocol is the expected standard for remote procedure calls, nothing prevents you from making calls to a .NET Web Service using plain GET or POST requests. Incidentally, POST commands transport SOAP payloads.
You can build this feature into the proxy class directly by setting the /protocol switch to httpget or httppost when calling the wsdl.exe utility. The source code for the wrapper class behaves accordingly. You can also call a Web Service using the XmlHttpRequest object (see Resources) and either plain script or compiled code:
Set http =CreateObject("Microsoft.XMLHTTP")http.open "GET",_ "http://server/service.asmx/MethodName",falsehttp.send ""MsgBox http.responseText
By using XmlHttpRequest, you have the chance to connect to Web Services from Win32, script, and, in general, non-. NET code.
Tip 4: Use Cookieless Sessions
In ASP.NET, the Session object refers to a configurable software entity you can map to an in-process or out-of-process module. The Session object reads a number of runtime settings you can store in the Web server s framework installation tree or in the application s BIN directory from the web.config file. This file determines the settings for a number of ASP.NET components. If you put a copy of config.web in your application s local subtree, the settings end up overriding the default ones set in the framework path.
One thing you can customize is whether the Session Manager should identify client sessions using cookies (the default) or not. This line in the application local config.web file sets the Session Manager to work without cookies:
<sessionState cookieless="true"/>
Tip 5: Use Custom Grid Pagination
The Web Form DataGrid control has built-in support for pagination. It displays a pager bar automatically and, with limited help from your code, it shows pages of records of the given size. However, by default, all the necessary records are cached in the DataGrid object s DataSource property. This means you must fetch the data all at once. Although this isn t such a big issue with a few dozen records, it becomes a problem with thousands of records. In this case, you might want to fetch records manually and cache them on disk, but off the DBMS.
You must inform the DataGrid control of this particular behavior. Fortunately, the DataGrid provides for an AllowCustomPagination property that, when set to True, changes the internal behavior of the control quite a bit. When you set this property to True, the grid always reads records found in the DataSource property from first to last. It s your responsibility to replenish this container with fresh data belonging to the current page. Normally, the grid itself fetches only the records that pertain to the page from the DataSource property.
Tip 6: Load Key Info From Database
The DataTable object lets you set in-memory primary keys on a given table. This action speeds up the search on both the DataTable and related DataView objects. Another good reason to have in-memory keys is to automatically prevent duplicate insertions that violate table integrity. Then you have plenty of time to fix the table, even properly warn the user, before the data is actually submitted to the database server for a batch update.
10 Tips for Great .NET Programming (Continued)
You can set this information manually in your fetching code:
//ds is an existing dataset
DataColumn[] keys == new DataColumn[1];
DataTable dt = ds.Tables["MyTable"];
keys[0] == dt.Columns["ID"];
dt.PrimaryKey = keys;
You can have the primary key information detected and set automatically while filling the data set. You automate the discovery of this information, and consequently the execution of your fetching code, by setting the MissingSchemaAction property of the data adapter that performs the query:
SqlDataAdapter da = new SqlDataAdapter(strCmd,strConn);DataSet ds = new DataSet();da.MissingSchemaAction = MissingSchemaAction.AddWithKey;da.Fill(ds,"MyTable");
Tip 7: Request Selected Checkboxes
In any ASP application, you have several checkboxes, all with the same name:
<input type="checkbox" name="foo" value="...">
You can get the corresponding values of the checkboxes that have been selected upon form posting with a single line of code:
<%a =split(Request.Form("foo"),",") %>
Request.Form("foo") returns a comma-separated string formed by the value strings of all checked items. You pass this string to the VBScript s split function and get an easily manageable structure, such as an array.
The same code won t work in ASP.NET if you use the <asp:checkbox> server control. To make it work, stick with the HtmlInputCheckBox control and write code such as this:
<input type="checkbox" runat="server" name="foo"value="...">
Even though the asp:checkbox and input type="checkbox" server tags evaluate to the same HTML code, ASP.NET ensures unique IDs and names for the tags output by asp:checkbox and any other control in the <asp> namespace.
Tip 8: Automate Master/Detail Views
If you have a Windows Forms application that uses the DataGrid control to display master/detail tables, chances are you can have the framework synchronize the views automatically for you.
The key is creating a data relation between the two tables and using a composed expression to define the data source for the detail table. Let s assume you have Customers and Orders tables with a CustID field in common. You create a relation in terms of an ADO.NET DataRelation object:
//ds is an existing DataSetDim dc1,dc2 As DataColumndc1 =ds.Tables("Customers").Columns("custid")dc2 =ds.Tables("Orders").Columns("custid")Dim r As DataRelationr =New DataRelation("OrdersByCustomer",dc1,dc2)ds.Relations.Add(r)
Whenever you have a DataRow object representing one row on the master table—Customers, in this case—you can obtain an array with the child rows according to the relation using the DataRow s GetChildRows method. For Windows Forms, this programmatic approach is buried in this code:
dGrid.DataSource =dsdGrid.DataMember = "Customers.OrdersByCustomer"
When you associate the detail grid with an expression such as MasterTable.Relation, its content is refreshed automatically and properly.
Tip 9: Handle File Change Notification
.NET integrates the functionality of Win32 file notification objects into the FileSystemWatcher class. Those kernel objects are responsible for notifying client applications about changes detected at the file system level. According to the specified parameters, a notification object signals the status of the applications whenever a file is created, modified, renamed, or deleted.
It s difficult for Win32 to know the name of the file being involved and the reason for its involvement with the event under Windows NT and Windows 2000. All this has been superseded in .NET thanks to the FileSystemWatcher class:
FileSystemWatcher watcher = new FileSystemWatcher();watcher.Path ="c:\\";watcher.Filter ="*.txt";watcher.NotifyFilter = NotifyFilters.LastWrite;
10 Tips for Great .NET Programming (Continued)
Once the object is configured, you begin watching:
watcher.EnableRaisingEvents =true;
Any detected event causes an application event to fire. You register for the events you need like this:
watcher.Changed += new FileSystemEventHandler(OnChanged);
The handler s event arguments supply all the file and event information you need.
Tip 10: Compile Code on the Fly
The .NET Framework exposes classes that let you compile code in a specified language. These classes live in the System.CodeDom.Compiler namespace. This code snippet shows how to obtain an in-memory running instance of the C# compiler:
CSharpCodeProvider csc = new CSharpCodeProvider();ICodeCompiler icc = csc.CreateCompiler();
Next, you ll set some input parameters through the CompilerParameters class:
CompilerParameters co = new CompilerParameters();co.OutputAssembly ="foo.exe";co.ReferencedAssemblies.Add("system.dll");
You must specify at least the name of the executable, set GenerateExecutable to False if you want a DLL, and add the list of assemblies to reference:
icc.CompileAssemblyFromFile(co, csfile);
To run the compiling process, use CompileAssemblyFromFile() and pass it the parameters and the source file name. You can use the class CompilerResults to learn more about the newly generated assembly.