Software Engineering, Project Management, and Effectiveness
Here's the next .NET Framework 2.0 performance guideline for review from Prashant Bansode, Bhavin Raichura, Girisha Gadikere and Claudio Caldato.
Use ExcludeSchema Serialization Mode while Exchanging Typed DataSet Over NetworkApplies to
What to DoSet the Typed DataSet property SchemaSerializationMode to ExcludeSchema while transferring the typed DataSet over network for better performance.
WhyThe serialization of a typed DataSet can be optimized by setting the SchemaSerializationMode property value to ExcludeSchema. When ExcludeSchema is used, the serialized payload does not contain schema information, tables, relations and constraints. This results in a smaller payload for network transfer, which provides better performance.
WhenIf it is required to send the typed DataSet over network, use ExcludeSchema Serialization Mode by setting the the typed DataSet property SchemaSerializationMode to ExcludeSchema.
ExcludeSchema is supported only for a typed DataSet. ExcludeSchema should only be used in cases where the schema information of the underlying typed DataTables, DataRelations and Constraints do not get modified.
HowThe typed DataSet has a new property called SchemaSerializationMode in .NET Framework 2.0. Set the SchemaSerializationMode property of typed DataSet to ExcludeSchema before returning for network transfer as follows:
... typedDataSet.EnforceConstraints = false; typedDataSet.SchemaSerializtionMode = SchemaSerializationMode.ExcludeSchema; ...
Problem ExampleA .NET 2.0 Windows Forms based application for Order Management, gets the list of Sales Orders by a Web Service call. The Web Service internally makes a business logic call to get the list of Sales Order in a Typed DataSet. The Web Service returns the Typed DataSet to the smart client application. The code implementation does not exclude schema information, while serializing the DataSet and therefore has a larger size. The larger content can take more time to transfer across network and have a negative impact on performance.
... //WebService Method public SalesOrdersTypedDataSet GetSalesDetailDataset() { ... SalesOrdersTypedDataSet salesOrdersDS = BusinessLogic.GetSalesOrders(); ... return salesOrdersDS; } ...
Solution ExampleA .NET 2.0 Windows Forms based application for Order Management, gets the list of Sales Orders by a Web Service call. The Web Service internally makes a business logic call to get the list of Sales Order in a Typed DataSet. The Web Service returns the Typed DataSet to the smart client applicaiton. The code implementation uses SchemaSerializationMode property of the Sales Order Typed DataSet to reduce the size of the serialized content. It gives performance benefit while transferring over network:
... //WebService Method public SalesOrdersTypedDataSet GetSalesOrdersDataset() { ... SalesOrdersTypedDataSet salesOrdersDS = BusinessLogic.GetSalesOrders(); salesOrdersDS.EnforceConstraints = false; salesOrdersDS.SchemaSerializationMode = SchemaSerializationMode.ExcludeSchema; return salesOrdersDS; } ...
Additional Resources
MadLibs can be an interesting approach to capturing or identifying principles, practices, values ... etc.
Here's an example attempting to encapsulate principles of agile development:
Here's one example:
Of course, you can also leave off the front and contrast with the behavior you'd like to change_________ ... over static and stale communication approaches.
Notice the ..x "over" ...y approach for the practices/principles above. Sometimes laws/principles/rules come across as common sense or "yeah, I already mostly do that" until you sharply contrast. It's a challenge to both the principle/practice author (am I striking the precise and accurate chord?) and the principle/practice follower (am I really changing behavior?)
This approach works for values too:
This is the approach used in the Agile Manifesto http://www.agilemanifesto.org/
So if you're responsible for identifying/documenting your organizations principles/practices/values, you might try using a MadLibs approach and a Wiki.
Use HandleCollector API when Managing Expensive Unmanaged Resource Handles
Applies to
What to DoUse HandleCollector API when managing expensive Unmanaged Resource Handles from Managed Code using COM Interop.
WhyThe HandleCollector API helps to optimize Garbage Collector efficiency while working with expensive unmanaged resource handles. The garbage collector cannot track the Memory allocated by Unmanaged Code, it can lead to un-optimized memory management by garbage collector. The HandleCollector can force garbage collection if the threshold number of handles are reached, thereby improves performance of the application.
WhenIf it is required to manage multiple Unmanaged Resource Handles in the application, it is recommended to use HandleCollector API to improve GC efficiency by ensuring the objects are destroyed seamlessly on-time.
HowCreate an instance of HandleCollector by providing three parameters Handle Name (string), Initial Threshold (int) and Maximum Threshold (int). The initial threshold is the the point at which GC can start performing garbage collection. The maximum threshold is the point at which GC must perform garbage collection.
... static readonly HandleCollector appGuiHandleCollector = new HandleCollector( “ApplicationGUIHandles”, 5, 30); ...
When application creates an expensive handle, application should increase the total handle count by invoking Add method:
... static IntPtr CreateBrush() { IntPtr handle = CreateSolidBrush(...); appGuiHandleCollector.Add(); return handle; } ...
When application destroys an expensive handle, application should decrese the total handle count by invoking Remove method:
... internal static void DeleteSolidBrush(IntPtr handle) { DeleteBrush(handle); appGuiHandleCollector.Remove(); } ...
Problem ExampleA .Net 2.0 Windows Forms based application needs to provide lot of GUI features to facilitate Paint functionality. The code internally uses many unmanaged GUI handles to manage various Bitmaps. If the developer misses out to destroy the handle by calling appropriate Dispose method at appropriate time, the object remains in memory till the time garbage collection is performed. Also, if memory allocation is performed within unmanaged resource, the GC may not be even aware of it to force collection. At runtime, it might be required to force garbage collection to optimize GC memory management for unmanaged allocations.
Solution ExampleA .Net 2.0 Windows Forms based application needs to provide lot of GUI features to facilitate Paint functionality. The code internally uses many unmanaged GUI handles to manage various Bitmaps. The application creates an instance of HandleCollector by providing Handle Name, Initial Threshold and Maximum Threshold. When application creates an expensive handle, it increases the total handle count by invoking the Add method. When handle count reaches to the maximum threshold limit, the GC will force garbage collection automatically. Also, if the handle is destroyed, it will automatically reduce the total handle count in the following code:
... class UnmanagedHandles { // Create a new HandleCollector static readonly HandleCollector appHandleCollector = new HandleCollector("ApplicationUnmanagedHandles", 5, 30); public UnmanagedHandles () { // Increment Handle Count myExpensiveHandleCollector.Add(); } ~ UnmanagedHandles () { // Decrement Handle Count myExpensiveHandleCollector.Remove(); } } ...
Use Promotable Transactions when Working with SQL Server 2005
What to DoUse new .Net 2.0 System.Transactions API for controlling transactions in managed code when working with SQL Server 2005
WhySystem.Transactions API gives flexibility to shift between local database server transactions and distributed database server transactions. The Systems.Transactions API, when used with SQL Server 2005, uses the Promotable Transactions feature through the Lightweight Transactions Manager. It does not create a distributed transaction when not required, resulting in improved performance.
Note If System.Transactions API is used to manage local transactions on SQL Server 2000, the local transaction is automatically promoted to a distributed transaction managed by MSDTC. SQL Server 2000 does not support Promotable Transactions.
WhenIf it is required to control transactions in managed code while working with SQL Server 2005, use Systems.Transactions API for improving performance and flexibility.
This guideline should not be used when working with SQL Server 2000.
HowThe following information is for using "Promotable Transactions".
While using the System.Transaction API is to define Transaction Scope using TransactionScope Class, it defines the boundary for the required transactions.
... using (TransactionScope scope = new TransactionScope()) { ... // Open Connection and Execute Statements ... scope.Complete(); } ...
Within Transaction Scope block use normal ADO.NET code for executing the statements using Connection, Command and Execute methods. If the transaction is successful, invoke TransactionScope.Complete method. If the transaction is unsuccessful, the transaction will be automatically rolled back as it will not execute TransactionScope.Complete in the program flow.
Problem ExampleA web application for Online Shopping, provides a user interface to purchase items. Once the items are purchased, the item entry should be added for billing in a Billing database table in a SQL server 2005 database. At the same time, the stock of the item should be reduced by the number of units sold in an Item Quantity database table. The entire operation needs to be performed in single transaction to maintain data integrity.
The application follows a traditional approach of using SqlTransaction API which enforces only local transactions. If distributed database transactions are required, the code has to be changed and compiled again. This breaks the principle of flexibility and agility in the design. The following code illustrates the problem, which forces local transactions and compromises flexibility to change it to distributed transactions:
... using (SqlConnection conn = new SqlConnection(dbConnStr)) { conn.Open();
SqlCommand cmd = conn.CreateCommand(); SqlTransaction trans;
// Start a local transaction. trans = conn.BeginTransaction("NewTransaction");
cmd.Connection = conn; cmd.Transaction = transaction;
try { cmd.CommandText = "Insert Statement..."; cmd.ExecuteNonQuery(); cmd.CommandText = "Update Statement..."; cmd.ExecuteNonQuery();
// Attempt to commit the transaction. trans.Commit(); } catch (Exception ex) { // Attempt to roll back the transaction. try { trans.Rollback(); } catch (Exception ex2) { // handle any errors that may have occurred } } } ...
Instead, if the new System.Transactions API is used, it supports distributed transactions also.
Solution ExampleA web application for Online Shopping, provides a user interface to purchase items. Once the items are purchased, the item entry should be added for billing in the Billing database table on SQL server 2005. At the same time, the stock of the item should be reduced by the number of units sold in the Item Quantity database table. The entire operation needs to be performed in single transaction to maintain data integrity. The new System.Transactions API is used to provide flexibility without compromise on performance. System.Transactions API has a feature of Promotable Transactions when used with SQL Server 2005. It determines the need of using distributed transactions or local transactions at runtime for improved performance:
... using (TransactionScope scope = new TransactionScope()) { using (SqlConnection conn = new SqlConnection(dbConnStr)) { SqlCommand cmd1 = conn.CreateCommand(); cmd1.CommandText = "Insert Statement....";
SqlCommand cmd2 = conn.CreateCommand(); cmd2.CommandText = "Update Statement....";
conn.Open(); cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery(): conn.Close(); } scope.Complete(); } ...
Use AddMemoryPressure while consuming Unmanaged Objects through COM InteropApplies to
What to DoUse .NET 2.0 CLR API AddMemoryPressure and RemoveMemoryPressure while consuming the Unmanaged Objects from Managed Code through COM Interop.
WhyThe garbage collector cannot track the Memory allocated by Unmanaged Code, it only tracks the Memory allocated by Managed Code.
If there is a large amount of memory allocation (such as images or video data) in the unmanaged code, the GC will be able to see only the reference of unmanaged objects, but not the size of the memory occupied by the unmanaged object references.
Since GC may be unaware of the large memory allocation within unmanaged code, the GC will not know that a collection should be executed, as it does not realize any "memory pressure" to cause a collection.
The AddMemoryPressure method can be used to inform GC about how much unmanaged memory a managed object will be referencing, when it consumes the Unmanaged Objects from Managed Code. The pro-active indication to GC improves the GC heuristics and collection algorithm, which improves memory management and performance.
WhenIf the Unmanaged Objects invoked from Managed Code allocates a large amount of unmanaged memory at runtime, the GC should be informed about the total memory consumed by the managed and unmanaged code, by invoking AddMemoryPressure method, to improve memory management of GC.
HowApplying memory pressure is the technique using which the GC can be informed about the memory allocation that might be performed by the unmanaged code within a managed code wrapper.
The AddMemoryPressure should be used to inform GC about probable memory allocation by the unmanaged code:
Bitmap (string path ) { _size = new FileInfo(path).Length; GC.AddMemoryPressure(_size); // other work }
The RemoveMemoryPressure should be used to inform GC to remove memory pressure while destroying the wrapper managed object which was consuming the unmanaged objects:
~Bitmap() { GC.RemoveMemoryPressure(_size); // other clean up code }
Note For every AddMemeoryPressure call, there must be a matching RemoveMemoryPressure call which will remove exactly the same amount of memory pressure as added earlier. Failing to do so can adversely affect the performance of the system in applications that run for long periods of time.
Problem ExampleA .NET 2.0 application uses unmanaged objects that allocate large amount of memory at runtime to load images and video data.
It actually uses unmanaged memory and occupies large amount of system memory. Since the garbage collector cannot track the memory allocated by unmanaged code, the application might run low on memory without triggering garbage collection, degrading the performance of the application.
class Bitmap { private long _size; Bitmap (string path ) { _size = new FileInfo(path).Length; // other work } ~Bitmap() { // other work } }
Solution ExampleA .NET 2.0 application uses unmanaged objects that allocate large amount of memory at runtime to load images and video data.
Use of AddMemoryPressure and RemoveMemoryPressure informs about the memory allocation and deallocation that might be performed by the unmanaged code within a managed code wrapper. The GC will know to perform a collection when there is a memory pressure and will work efficiently.
class Bitmap { private long _size; Bitmap (string path ) { _size = new FileInfo(path).Length; GC.AddMemoryPressure(_size); // other work } ~Bitmap() { GC.RemoveMemoryPressure(_size); // other work } }
Here's a quick reference for writing guidance modules. Guidance modules are simply what I call the prescriptive guidance documents we write when creating guides such as Improving Web Application Security, Improving .NET Application Performance and .NET 2.0 Security Guidance.
How To Write Prescriptive Guidance Modules
Summary of Steps
Step 1. Identify and prioritize the tasks and scenarios.Task-based content is "executable". You can take the actions prescribed in the guidance and produce a result. Scenarios bound the guidance to a context. This helps for relevancy and for evaluating the appropriateness of the recommendations.
Good candidates for tasks and scenarios include:
In many ways, the value of your prescriptive guidance is a measure of the value of the problem multiplied by how many people share the problem.
Step 2. Identify appropriate guidance module types.Choose the appropriate guidance module type for the job:
For a list of guidance module types, templates, and examples see Templates for Writing Guidance at http://www.guidancelibrary.com
Step 3. Create the guidance modules.Once you've identified the prioritized scenarios, tasks, and guidance types, you create the guidance. This involves the following:
Examples of Guidance Modules
These are some practices we learned in the guidance business to write more effective guidelines:
Follow the What to Do, Why and How PatternStart with the action to take -- the "what to do". This should be pinned against a context. Context includes which technologies and situations the guidance applies. Be sure to address "why" as well, which exposes the rationale. Rationale is key for the developer audience. It's easy to find many guidelines, missing context or rationale. Some of the worst guidelines leave you wondering what to actually do.
Keep It Brief and to the PointAvoid "blah, blah, blah". Say what needs to be said as succinctly as possible. Ask "why, why, why?" to everything you write - every paragraph and every sentence. Does it add value? Does it help the reader? Is it actionable? Often the answer is no. It's hard to do, but it keeps the content "lean and mean".
Start with Principle-Based RecommendationsA good principle-based recommendation addresses the question: "What are you trying to accomplish?". Expose guidance based on engineering versus implementation or technology of the day. This makes the guidance less volatile and arguably more useful. An example principle-based recommendation would be: Validate Input for Length, Range, Format, and Type. You can then build more specific guidelines for a technology or scenario from this baseline recommendation.
Provide Context for RecommendationsAvoid blanket recommendations. Recommendations should have enough context to be prescriptive. Sometimes this can be as simple as prefixing your guideline with an "if" condition.Make the Guidelines ActionableBe prescriptive, not descriptive. The guideline should be around actionable vs. just interesting information. Note that considerations are actions provided you tell the reader what to consider, when and why. As a general rule, avoid providing too much background or conceptual information. Point off to primer articles, books etc for background.
Choose Warm vs. Cold HandoffsIf you are sending the reader to another link for a specific piece of information, be explicit. It's as simple as adding "For more information on xyz, see ..." before your link. That's a warm hand off. A cold hand off is simply having a list of links and expecting the reader to follow the links and figure out why you sent them there. The worst is when the links are irrelevant and you simply added links because you could.
Create Thread KillersA "thread killer" is a great piece of information that when quoted or referred to can stop a technical discussion or alias question (a discussion thread) with no further comments. Look at the alias, understand the questions being asked, and tackle the root causes and underlying problems that cause these questions. (Think of the questions as symptoms). Make guidance that nails the problem. A great endorsement is when your "thread killer" is used to address FAQs on discussion aliases and forums.
Where to See This in PracticeThe following are examples of prescriptive guidelines based on these practices:
Use Generics To Eliminate the Cost Of Boxing, Casting and Virtual calls
What to Do
WhyGenerics can be used to improve the performance by avoiding runtime boxing, casting and virtual calls.
List<Object> class gives better performance over ArrayList. An example benchmark of a quick-sort of an array of one million integers may show the generic method is 3 times faster than the non-generic equivalent. This is because boxing of the values is avoided completely. In another example, the quick-sort of an array of one million string references with the generic method was 20 percent faster due to the absence of a need to perform type checking at run time. You results will depend on your scenario. Other benefits of using Generics are compile-time type checking, binary code reuse and clarity of the code.
WhenUse Generics feature for defining a code (class, structure, interface, method, or delegate) which has to be used by different consumers with different types.
Consider replacing a generic code (class, structure, interface, method, or delegate), which does an implicit casting of any type to System.Object and forces the consuming code to cast between Object references to actual data types.
Only if there is a considerable number (>500) to store consider having a special class, otherwise just use the default List<Object> class. List<Object> gives better performance over ArrayList as List<Object> has a better internal implementation for enumeration.
HowThe following steps show how to use generics for various types.
Define a generic class as follows. public class List<T>
Define methods and variables in generic class as follows. public class List<T> { private T[] elements; private int count; public void Add(T element) { if (count == elements.Length) Resize(count * 2); elements[count++] = element; } public T this[int index] { get { return elements[index]; } set { elements[index] = value; } } }
Access generic class with required type as follows List<int> intList = new List<int>(); intList.Add(1); ........ int i = intList[0];
Note The .NET Framework 2.0 provides a suite of generic collection classes in the class library. Your applications can further benefit from generics by defining your own generic code
Problem ExampleAn Order Management Application stores the domain data Item, Price, etc in Cache using ArrayList. ArrayList accepts any type of data and implicitly casts into objects. Numeric data like Order number, customer id, etc are wrapped to object type from primitive types (boxing) while storing in the ArrayList. Consumer code has to explicitly cast the data from Object type to specific data type while retrieving from the ArrayList. Boxing and un-boxing requires lot of operations like memory allocation, memory copy & garbage collection which in turn reduces the performance of the application.
Example code snippet to add items to ArrayList or to get/set items from the ArrayList
ArrayList intList = new ArrayList();
//Cache data in to array
intList.Add(45672); // Argument is boxed intList.Add(45673); // Argument is boxed
//Retrieve data from cache int orderId = (int)intList.Item[0]; // Explicit un-boxing & casting required
Solution ExampleAn Order Management Application stores the domain data Item, Price, etc in Cache. Using Generics feature avoids necessity of run time boxing and casting requirements and makes sure of compile time type checking
Implement the defining code for generic class. Generic class can be implemented only if required, else default List<T> class can be used //Use to allow consumer code to specify the required type
class OrderList{
//Consumer specific type array to hold the data T[] elements; int count;
//No implicit casting while adding to array public void Add(T element) {
//Add data to array as an object elements[count++] = element; }
// Method to set or get data public T this[int index] {
//Returns data as T type get { return elements[index]; }
//Set the data as T type set { elements[index] = value; } } }
Initiate the class with specifying as int data type OrderList intList = new OrderList();
//Cache data in to array intList.Add(45672); // No boxing required intList.Add(45673); // No boxing required
//Retrieve data from cache int orderId = intList[0]; // Un-boxing & casting not required
Here's the next .NET Framework 2.0 performance guideline in the series from Prashant Bansode, Bhavin Raichura, Girisha Gadikere and Claudio Caldato.
...
Use Token Handle Resolution API to get the Metadata for Reflection
What to DoUse the new .NET 2.0 token handle resolution API RuntimeMethodHandle to get the Metadata of members while using Reflection.
WhyThe RuntimeMethodHandle is a small, lightweight structure that defines the identity of a member. RuntimeMethodHandle is a trimmed down version of MemberInfos, which provides the metadata for the methods and data without consuming .NET 2.0 back-end cache.
The .NET Framework also provides GetXxx type of API methods for e.g.. GetMethod, GetProperty, GetEvent to determine the metadata of given Type at runtime. There are two forms of these APIs, the non-plural, which return one MemberInfo (such as GetMethod), and the plural APIs (such as GetMethods).
The .NET framework implements a back-end cache for the MemberInfo metadata to improve the performance of GetXxx API.
The caching policy is implemented irrespective of plural or non-plural GetXxx API call. Such eager caching policy degrades the performance on calls to or non-plural GetXxx API calls.
RuntimeMethodHandle works approximately twice faster than compared to equivalent GetXxx API call, if the MemberInfo is not present in the back-end .NET cache.
WhenIf it is required to get the metadata of a given Type at runtime, use new .NET 2.0 token handle resolution API RuntimeMethodHandle for better performance than traditional GetXxx API calls.
HowThe following code snippet shows how to get the RuntimeMethodHandle:
... // Obtaining a Handle from an MemberInfo RuntimeMethodHandle handle = typeof(D).GetMethod("MyMethod").MethodHandle; ...
The following code snippet shows how to get the MemeberInfo metadata from the handle:
... // Resolving the Handle back to the MemberInfo MethodBase mb = MethodInfo.GetMethodFromHandle(handle); ...
Problem ExampleA Windows Forms based application needs to dynamically load the plug-in Assemblies and available Types. The application also needs to determine the metadata of a given Type (methods, members etc) at runtime to execute Reflection calls.
The plug-in exposes a Type CustomToolBar, which is derived from Type BaseToolBar. The CustomToolBar Type has 2 methods - PrepareCommand, ExecuteCommand. The BaseToolBar Type has 3 methods - Initialize, ExecuteCommand and CleanUp. To execute the ExecuteCommand method of type CustomToolBar at runtime, it gets the metadata of that method using GetXxx API as shown in the following code snippet.
Since the .NET Framework implements eager caching policy, the call to get the metadata for a single ExecuteCommand method will also get the metadata of all the five methods of CustomToolBar and BaseToolBarTypes.
MethodInfo mi = typeof(CustomToolBar).GetMethod("ExecuteCommand");
The .NET framework implements a back-end cache for the MemberInfo metadata to improve the performance of GetXxx API. The implemented caching policy caches all members by default, irrespective of plural or non-plural API call. Such eager caching policy degrades the performance on calls to or non-plural GetXxx API calls.
Solution ExampleA Windows Forms based application needs to dynamically load the plug-in Assemblies and available Types. The application also needs to determine the metadata of a given Type (methods, members etc) at runtime to execute Reflection calls.
The plug-in exposes a Type CustomToolBar, which is derived from Type BaseToolBar. The CustomToolBar Type has 2 methods - PrepareCommand, ExecuteCommand. The BaseToolBar Type has 3 methods - Initialize, ExecuteCommand and CleanUp. To execute the ExecuteCommand method of type CustomToolBar at runtime, it gets the metadata of that method using RuntimeMethodHandle is used, as shown in the following code snippet. This can improve the performance of the application. :
... // Obtaining a Handle from an MemberInfo RuntimeMethodHandle handle = typeof(CustomToolBar).GetMethod("ExecuteComand").MethodHandle; // Resolving the Handle back to the MemberInfo MethodBase mb = MethodInfo.GetMethodFromHandle(handle); ...
If the appropriate MemberInfo is already in the back-end .NET cache, the cost of going from a handle to a MemberInfo is about the same as using one of the GetXxx API call. If the MemberInfo is not available in the cache RuntimeMethodHandle is approximately twice faster than the GetXxx API call.
Prashant Bansode, Bhavin Raichura, and Girisha Gadikere teamed up with Claudio Caldato (CLR team) to create some new performance guidelines for .NET Framework 2.0. The guidelines use our new guideline template.
Use TryParse Method to Avoid Unnecessary Exceptions
What to DoUse TryParse Method instead of Parse Method for converting string input to a valid .Net data type. For example, use TryParse method before converting a string Input to integer data type.
WhyThe Parse method will throw exception - ArgumentNullexception or FormatException or OverflowException, if the string representation cannot be converted to the respective data type.
Unnecessary Throwing Exceptions and Handling the same such as above has a negative impact on the performance of the application. The TryParse method does not throw an exception if the conversion fails instead it returns false, and hence saves exception handling related performance hit.
WhenIf it is required to convert a string representation of a data type to a valid .Net data type, use TryParse method instead of calling the Parse method to avoid unnecessary exception.
HowThe following code snippet illustrates how to use TryParse method :
... Int32 intResult; if (Int32.TryParse(strData, intResult)) { // process intResult result } else { //error handling } ...
Problem ExampleConsider a Windows Forms application for creating an Invoice. The application takes user inputs for multiple items as product name, quantity, price per unit and date of purchase. The user provides these inputs in the text boxes. The user can enter multiple items in an invoice at a given time and then finally submit the data for automatic billing calculation. The application internally needs to convert the string input data to integer (assume for simplicity). If the user enters invalid data in the text box, the system will throw an exception. This has adverse impact on performance of the application.
... private Int32 ConvertToInt(string strData) { try { return Int32.Parse(strData); } catch (exception ex) { return 0; //some default value } } ...
Solution ExampleConsider a Windows Forms application for creating an Invoice. The application takes user inputs for multiple items as product name, quantity, price per unit and date of purchase. The user provides these inputs in the text boxes. The user can enter multiple items in an invoice at a given time and then finally submit the data for automatic billing calculation. The application internally needs to convert the string input data to integer (assume for simplicity). If the user enters invalid data in the text box, the system will not throw unnecessary exceptions and hence improves the application performance.
... private Int32 ConvertToInt(string strData) { Int32 intResult; if (Int32.TryParse(strData, intResult)) { return intResult; } return o; //some default value } ...
Sometimes the guidelines in our guidance such as Improving Web Application Security, Improving .NET Application Performance and .NET 2.0 Security Guidance are missing some of the important details such as when, why or how. To correct that, we've created a template that explicitly captures the details. We use this template in Guidance Explorer. I'll also be posting .NET Framework 2.0 performance examples that use this new template.
Guideline Template
Test Cases
The test cases are simply questions we use to help improve the guidance. The guidelines author can use the test cases to check that they are putting the right information into the template. Reviewers use the test cases as a check against the content to make sure it's useful.
Title
Applies To
Why
When
How
Problem Example
Solution Example
Related Items
Additional Tests to Consider When Writing a Guideline
We released Guidance Explorer Beta 2 on CodePlex. Guidance Explorer is a patterns & practices R&D project to improve finding, sharing and creating prescriptive guidance. Guidance Explorer features modular, actionable guidance in the form of checklists, guidelines, how tos, patterns … etc.
What's New with This Release
Learn More
Resources
FeedbackSend your feedback to getool@microsoft.com.