Architecture and Design Solutions
If you are an architect, this guide provides the following solutions to help you design Microsoft® .NET applications to meet your performance objectives:
How to balance performance with quality-of-service (QoS) requirements
Do not consider performance in isolation. Balance your performance requirements with other QoS attributes such as security and maintainability.
How to identify and evaluate performance issues
Use performance modeling early in the design process to help evaluate your design decisions against your objectives before you commit time and resources. Identify your performance objectives, your workload, and your budgets. Budgets are your constraints. These include maximum execution time and resource utilization such as CPU, memory, disk I/O, and network I/O.
How to perform architecture and design reviews
Review the design of your application in relation to your target deployment environment, any constraints that might be imposed, and your defined performance goals. Use the categories that are defined by the performance and scalability frame promoted by this guide to help partition the analysis of your application and to analyze the approach taken for each area. The categories represent key areas that frequently affect application performance and scalability. Use the categories to organize and prioritize areas for review.
How to choose a deployment topology
When you design your application architecture, you must take into account corporate policies and procedures together with the infrastructure that you plan to deploy your application on. If the target environment is rigid, your application design must reflect the restrictions that exist in that rigid environment. Your application design must also take into account QoS attributes such as security and maintainability. Sometimes you must make design tradeoffs because of protocol restrictions, and network topologies.
Identify the requirements and constraints that exist between application architecture and infrastructure architecture early in the development process. This helps you choose appropriate architectures and helps you resolve conflicts between application and infrastructure architecture early in the process.
Use a layered design that includes presentation, business, and data access logic. A well-layered design generally makes it easier to scale your application and improves maintainability. A well-layered design also creates predictable points in your application where it makes sense (or not) to make remote calls.
To avoid remote calls and additional network latency, stay in the same process where possible and adopt a non-distributed architecture, where layers are located inside your Web application process on the Web server.
If you do need a distributed architecture, consider the implications of remote communication when you design your interfaces. For example, you might need a distributed architecture because security policy prevents you from running business logic on your Web server, or you might need a distributed architecture because you need to share business logic with other applications, Try to reduce round trips and the amount of traffic that you send over the network.
How to design for required performance and scalability
Use tried and tested design principles. Focus on the critical areas where the correct approach is essential and where mistakes are often made. Use the categories described by the performance frame that is defined in this guide to help organize and prioritize performance issues. Categories include data structures and algorithms, communication, concurrency, resource management, coupling and cohesion, and caching and state management.
How to pass data across the tiers
Prioritize performance, maintenance, and ease of development when you select an approach. Custom classes allow you to implement efficient serialization. Use structures if you can to avoid implementing your own serialization. You can use XML for interoperability and flexibility. However, XML is verbose and can require considerable parsing effort. Applications that use XML may pass large amounts of data over the network. Use a DataReader object to render data as quickly as possible, but do not pass DataReader objects between layers because they require an open connection. The DataSet option provides great flexibility; you can use it to cache data across requests. DataSet objects are expensive to create and serialize. Typed DataSet objects permit clients to access fields by name and to avoid the collection lookup overhead.
How to choose between Web services, remoting, and Enterprise Services
Web services are the preferred communication mechanism for crossing application boundaries, including platform, deployment, and trust boundaries. The Microsoft product team recommendations for working with ASP.NET Web services, Enterprise Services, and .NET remoting are summarized in the following list:
Build services by using ASP.NET Web services.
Enhance your ASP.NET Web services with Web Services Enhancements (WSE) if you need the WSE feature set and if you can accept the support policy.
Use object technology, such as Enterprise Services or .NET remoting, within the implementation of a service.
Use Enterprise Services inside your service boundaries when the following conditions are true:
You need the Enterprise Services feature set. This feature set includes object pooling, declarative transactions, distributed transactions, role-based security, and queued components.
You are communicating between components on a local server, and you have performance issues with ASP.NET Web services or WSE.
Use .NET remoting inside your service boundaries when the following conditions are true:
You need in-process, cross-application domain communication. Remoting has been optimized to pass calls between application domains extremely efficiently.
You need to support custom wire protocols. Understand, however, that this customization will not port cleanly to future Microsoft implementations.
When you work with ASP.NET Web services, Enterprise Services, or .NET remoting, you should consider the following caveats:
If you use ASP.NET Web services, avoid using low-level extensibility features such as the HTTP Context object. If you do use the HttpContext object, abstract your access to it.
If you use .NET remoting, avoid or abstract using low-level extensibility such as .NET remoting sinks and custom channels.
If you use Enterprise Services, avoid passing object references inside Enterprise Services. Also, do not use COM+ APIs. Instead, use types from the System.EnterpriseServices namespace.
How to design remote interfaces
When you create interfaces that are designed for remote access, consider the level of chatty communication, the intended unit of work, and the need to maintain state on either side of the conversation.
As a general rule, you should avoid property-based interfaces. You should also avoid any chatty interface that requires the client to call multiple methods to perform a single logical unit of work. Provide sufficiently granular methods. To reduce network round trips, pass data through parameters as described by the data transfer object pattern instead of forcing property access. Also try to reduce the amount of data that is sent over the remote method calls to reduce serialization overhead and network latency.
If you have existing objects that expose chatty interfaces, you can use a data facade pattern to provide a coarse-grained wrapper. The wrapper object would have a coarse-grained interface that encapsulates and coordinates the functionality of one or more objects that have not been designed for efficient remote access.
Alternatively, consider the remote transfer object pattern where you wrap and return the data you need. Instead of making a remote call to fetch individual data items, you fetch a data object by value in a single remote call. You then operate locally against the locally cached data. In some scenarios where you may need to ultimately update the data on the server, the wrapper object exposes a single method that you call to send the data back to the server.
How to choose between service orientation and object orientation
When you are designing distributed applications, services are the preferred approach. While object-orientation provides a pure view of what a system should look like and is good for producing logical models, a pure object-based approach often does not take into account real-world aspects such as physical distribution, trust boundaries, and network communication. A pure object-based approach also does not take into account nonfunctional requirements such as performance and security.
Table 1 summarizes some key differences between object orientation and service orientation.
Table 1: Object Orientation vs. Service Orientation
Object orientation Service orientation
Assumes homogeneous platform and execution environment. Assumes heterogeneous platform and execution environment.
Share types, not schemas. Share schemas, not types.
Assumes cheap, transparent communication. Assumes variable cost, explicit communication.
Objects are linked: Object identity and lifetime are maintained by the infrastructure. Services are autonomous: security and failure isolation are a must.
Typically requires synchronized deployment of both client and server. Allows continuous separate deployment of client and server.
Is easy to conceptualize and thus provides a natural path to follow. Builds on ideas from component software and distributed objects. Dominant theme is to manage/reduce sharing between services.
Provides no explicit guidelines for state management and ownership. Owns and maintains state or uses reference state.
Assumes a predictable sequence, timeframe, and outcome of invocations. Assumes message-oriented, potentially asynchronous and long-running communications.
Goal is to transparently use functions and types remotely. Goal is to provide inter-service isolation and wire interoperability based on standards.
Common application boundaries include platform, deployment, trust, and evolution. Evolution refers to whether or not you develop and upgrade applications together. When you evaluate architecture and design decisions around your application boundaries, consider the following:
Objects and remote procedure calls (RPC) are appropriate within boundaries.
Services are appropriate across and within boundaries.
Development Solutions
If you are a developer, this guide provides the following solutions:
Improving Managed Code Performance
How to conduct performance reviews of managed code
Use analysis tools such as FxCop.exe to analyze binary assemblies and to ensure that they conform to the Microsoft .NET Framework design guidelines. To evaluate specific features including garbage collection overheads, threading, and asynchronous processing.
Use the CLR Profiler tool to look inside the managed heap to analyze problems that include excessive garbage collection activity and memory leaks. For more information, see "How To: Use CLR Profiler" in the "How To" section of this guide.
How to design efficient types
Should your classes be thread safe? What performance issues are associated with using properties? What are the performance implications of supporting inheritance?
How to manage memory efficiently
Write code to help the garbage collector do its job efficiently. Minimize hidden allocations, and avoid promoting short-lived objects, preallocating memory, chunking memory, and forcing garbage collections. Understand how pinning memory can fragment the managed heap.
Identify and analyze the allocation profile of your application by using CLR Profiler.
How to use multithreading in .NET applications
Minimize thread creation, and use the self-tuning thread pool for multithreaded work. Avoid creating threads on a per-request basis. Also avoid using Thread.Abort or Thread.Suspend.
Make sure that you appropriately tune the thread pool for ASP.NET applications and for Web services. For more information, see "How to tune the ASP.NET thread pool" later in this document.
How to use asynchronous calls
Asynchronous calls may benefit client-side applications where you need to maintain user interface responsiveness. Asynchronous calls may also be appropriate on the server, particularly for I/O bound operations. However, you should avoid asynchronous calls that do not add parallelism and that block the calling thread immediately after initiating the asynchronous call. In these situations, there is no benefit to making asynchronous calls.
How to clean up resources
Release resources as soon as you have finished with them. Use finally blocks or the C# using statement to make sure that resources are released even if an exception occurs. Make sure that you call Dispose (or Close) on any disposable object that implements the IDisposable interface. Use finalizers on classes that hold on to unmanaged resources across client calls. Use the Dispose pattern to help ensure that you implement Dispose functionality and finalizers (if they are required) correctly and efficiently.
How to avoid unnecessary boxing
Excessive boxing can lead to garbage collection and performance issues. Avoid treating value types as reference types where possible. Consider using arrays or custom collection classes to hold value types. To identify boxing, examine your Microsoft intermediate language (MSIL) code and search for the box and unbox instructions.
How to handle exceptions
Exceptions can be expensive. You should not use exceptions for regular application logic. However, use structured exception handling to build robust code, and use exceptions instead of error codes where possible. While exceptions do carry a performance penalty, they are more expressive and less error prone than error codes.
Write code that avoids unnecessary exceptions. Use finally blocks to guarantee resources are cleaned up when exceptions occur. For example, close your database connections in a finally block. You do not need a catch block with a finally block. Finally blocks that are not related to exceptions are inexpensive.
How to work with strings efficiently
Excessive string concatenation results in many unnecessary allocations that create extra work for the garbage collector. Use StringBuilder when you need to create complex string manipulations and when you need to concatenate strings multiple times. If you know the number of appends and concatenate strings in a single statement or operation, prefer the + operator. Use Response.Write in ASP.NET applications to benefit from string buffering when a concatenated string is to be displayed on a Web page.
How to choose between arrays and collections
Arrays are the fastest of all collection types, so unless you need special functionalities like dynamic extension of the collection, sorting, and searching, you should use arrays. If you need a collection type, choose the most appropriate type based on your functionality requirements to avoid performance penalties.
Use ArrayList to store custom object types and particularly when the data changes frequently and you perform frequent insert and delete operations. Avoid using ArrayList for storing strings.
Use a StringCollection to store strings.
Use a Hashtable to store a large number of records and to store data that may or may not change frequently. Use Hashtable for frequently queried data such as product catalogs where a product ID is the key.
Use a HybridDictionary to store frequently queried data when you expect the number of records to be low most of the time with occasional increases in size.
Use a ListDictionary to store small amounts of data (fewer than 10 items).
Use a NameValueCollection to store strings of key-value pairs in a presorted order. Use this type for data that changes frequently where you need to insert and delete items regularly and where you need to cache items for fast retrieval.
Use a Queue when you need to access data sequentially (first in is first out) based on priority.
Use a Stack in scenarios where you need to process items in a last–in, first-out manner.
Use a SortedList for fast object retrieval using an index or key. However, avoid using a SortedList for large data changes because the cost of inserting the large amount of data is high. For large data changes, use an ArrayList and then sort it by calling the Sort method.
How to improve serialization performance
Reduce the amount of data that is serialized by using the XmlIgnore or NonSerialized attributes. XmlIgnore applies to XML serialization that is performed by the XmlSerializer. The XmlSerializer is used by Web services. The NonSerialized applies to .NET Framework serialization used in conjunction with the BinaryFormatter and SoapFormatter. The BinaryFormatter produces the most compact data stream, although for interoperability reasons you often need to use XML or SOAP serialization.
You can also implement ISerializable to explicitly control serialization and to determine the exact fields to be serialized from a type. However, using ISerializable to explicitly control serialization is not recommended because it prevents you from using new and enhanced formatters provided by future versions of the .NET Framework.
If versioning is a key consideration for you, consider using a SerializationInfoEnumerator to enumerate through the set of serialized fields before you try to deserialize them.
To improve DataSet serialization, you can use column name aliasing, you can avoid serializing both the original and the updated data values, and you can reduce the number of DataTable instances that you serialize.
How to improve code access security performance
Code access security ensures that your code and the code that calls your code are authorized to perform specific privileged operations and to access privileged resources like the file system, the registry, the network, databases, and other resources. The permission asserts and permission demands in the code you write and call directly affects the number and the cost of the security stack walks that you need.
How to reduce working set size
A smaller working set produces better system performance. Fewer larger assemblies rather than many smaller assemblies help reduce working set size. Using the Native Image Generator (Ngen.exe) to precompile code may also help. For more information, see "Working Set Considerations" in Chapter 5, "Improving Managed Code Performance."
How to develop SMP friendly code
To write managed code that works well with symmetric multiprocessor (SMP) servers, avoid contentious locks and do not create lots of threads. Instead, favor the ASP.NET thread pool and allow it to decide the number of threads to release.
If you run your application on a multiprocessor computer, use the server GC) instead of the workstation GC. The server GC is optimized for throughput, memory consumption, and multiprocessor scalability. ASP.NET automatically loads the server GC. If you do not use ASP.NET, you have to load the server GC programmatically. The next version of the .NET Framework provides a configurable switch.
How to time managed code in nanoseconds
Use the Microsoft Win32® functions QueryPerformanceCounter and QueryPerformanceFrequency to measure performance. To create a managed wrapper for these functions, see "How To: Time Managed Code Using QueryPerformanceCounter and QueryPerformanceFrequency" in the "How To" section of this guide.
Note At the time of this writing, the .NET Framework 2.0 (code-named "Whidbey") provides a wrapper to simplify using QueryPerformanceCounter and QueryPerformanceFrequency.
How to instrument managed code
Instrument your application to measure your processing steps for your key performance scenarios. You may need to measure resource utilization, latency, and throughput. Instrumentation helps you identify where bottlenecks exist in your application. Make your instrumentation configurable; be able to control event types and to switch your instrumentation off completely. Options for instrumentation include the following:
Event Tracing for Windows (ETW). Event Tracing for Windows is the recommended approach because it is the least expensive to use in terms of execution time and resource utilization.
Trace and Debug classes. The Trace class lets you instrument your release and debug code. You can use the Debug class to output debug information and to check logic for assertions in code. These classes are in the System.Diagnostics namespace.
Custom performance counters. You can use custom counters to time key scenarios within your application. For example, you might use a custom counter to time how long it takes to place an order. For implementation details, see "How To: Use Custom Performance Counters from ASP.NET" in the "How To" section of this guide.
Windows Management Instrumentation (WMI). WMI is the core instrumentation technology built into the Microsoft Windows® operating system. Logging to a WMI sink is more expensive compared to other sinks.
Enterprise Instrumentation Framework (EIF). EIF provides a framework for instrumentation. It provides a unified API. You can configure the events that you generate, and you can configure the way the events are logged. For example, you can configure the events to be logged in the Windows event log or in Microsoft SQL Server™. The levels of granularity of tracing are also configurable. EIF is available as a free download at http://www.microsoft.com/downloads/details.aspx?FamilyId=80DF04BC-267D-4919-8BB4-1F84B7EB1368&displaylang=en.
For more information, see "How To: Use EIF" In the "How To" section of this guide.
How to decide when to use the Native Image Generator (Ngen.exe)
The Native Image Generator (Ngen.exe) allows you to run the just-in-time (JIT) compiler on your assembly's MSIL to generate native machine code that is cached to disk. Ngen.exe for the .NET Framework version 1.0 and version 1.1 was primarily designed for the common language runtime (CLR), where it has produced significant performance improvements. To identify whether or not Ngen.exe provides any benefit for your particular application, you need to measure performance with and without using Ngen.exe. Before you use Ngen.exe, consider the following:
Ngen.exe is most appropriate for any scenario that benefits from better page sharing and working set reduction. For example, it is most appropriate for client scenarios that require fast startup to be responsive, for shared libraries, and for multiple-instance applications.
Ngen.exe is not recommended for ASP.NET version 1.0 and 1.1 because the assemblies that Ngen.exe produces cannot be shared between application domains. At the time of this writing, the .NET Framework 2.0 (code-named "Whidbey") includes a version of Ngen.exe that produces images that can be shared between application domains.
If you do decide to use Ngen.exe:
Measure your performance with and without Ngen.exe.
Make sure that you regenerate your native image when you ship new versions of your assemblies for bug fixes or for updates, or when something your assembly depends on changes.
Improving Data Access Performance
How to improve data access performance
Your goal is to minimize processing on the server and at the client and to minimize the amount of data passed over the network. Use database connection pooling to share connections across requests. Keep transactions as short as possible to minimize lock durations and to improve concurrency. However, do not make transactions so short that access to the database becomes too chatty.
How to page records
You should allow the user to page through large result sets when you deliver large result sets to the user one page at a time. When you choose a paging solution, considerations include server-side processing, data volumes and network bandwidth restrictions, and client-side processing.
The built-in paging solutions provided by the ADO.NET DataAdapter and DataGrid are only appropriate for small amounts of data. For larger result sets, you can use the SQL Server SELECT TOP statement to restrict the size of the result set. For tables that do not have a strictly-increasing key column, you can use a nested SELECT TOP query. You can also use temporary tables when data is retrieved from complex queries and is prohibitively large to be transmitted and stored on the Web layer and when the data is application wide and applicable to all users.
How to serialize DataSets efficiency
Default DataSet serialization is not the most efficient. For information about how to improve this, see "How To: Improve Serialization Performance" in the "How To" section of this guide.
How to choose between dynamic SQL and stored procedures
Stored procedures generally provide improved performance in comparison to dynamic SQL statements. From a security standpoint, you need to consider the potential for SQL injection and authorization. Both approaches are susceptible to SQL injection if they are poorly written. Database authorization is often easier to manage when you use stored procedures because you can restrict your application's service accounts to only run specific stored procedures and to prevent them from accessing tables directly.
If you use stored procedures, follow these guidelines:
Try to avoid recompiles.
Use the Parameters collection to help prevent SQL injection.
Avoid building dynamic SQL within the stored procedure.
Avoid mixing business logic in your stored procedures.
If you use dynamic SQL, follow these guidelines:
Use the Parameters collection to help prevent SQL injection.
Batch statements if possible.
Consider maintainability. For example, you have to decide if it is easier for you to update resource files or to update compiled statements in code.
How to choose between a DataSet and a DataReader
Do not use a DataSet object for scenarios where you can use a DataReader object. Use a DataReader if you need forward-only, read-only access to data and if you do not need to cache the data. Do not pass DataReader objects across physical server boundaries because they require open connections. Use the DataSet when you need the added flexibility or when you need to cache data between requests.
How to perform transactions in .NET
You can perform transactions using T-SQL commands, ADO.NET, or Enterprise Services. T-SQL transactions are most efficient for server-controlled transactions on a single data store. If you need to have multiple calls to a single data store participate in a transaction, use ADO.NET manual transactions. Use Enterprise Services declarative transactions for transactions that span multiple data stores.
When you choose a transaction approach, you also have to consider ease of development. Although Enterprise Services transactions are not as quick as manual transactions, they are easier to develop and lead to middle tier solutions that are flexible and easy to maintain.
Regardless of your choice of transaction type, keep transactions as short as possible, consider your isolation level, and keep read operations to a minimum inside a transaction.
How to optimize queries
Start by isolating long-running queries by using SQL Profiler. Next, identify the root cause of the long-running query by using SQL Analyzer. By using SQL Analyzer, you may identify missing or inefficient indexes. Use the Index Tuning Wizard for help selecting the correct indexes to build. For large databases, defragment your indexes at regular intervals.
Improving ASP.NET Performance
How to build efficient Web pages
Start by trimming your page size and by minimizing the number and the size of graphics, particularly in low network bandwidth scenarios. Partition your pages to benefit from improved caching efficiency. Disable view state for pages that do not need it. For example, you should disable view state for pages that do not post back to the server or for pages that use server controls. Ensure pages are batch-compiled. Enable buffering so that ASP.NET batches work on the server and avoids chatty communication with the client. You should also know the cost of using server controls.
How to tune the ASP.NET thread pool
If your application queues requests with idle CPU, you should tune the thread pool.
For applications that serve requests quickly, consider the following settings in the Machine.config file:
Set maxconnection to 12 times the number of CPUs.
Set maxIoThreads and maxWorkerThreads to 100.
Set minFreeThreads to 88 times the number of CPUs.
Set minLocalRequestFreeThreads to 76 times the number of CPUs.
For applications that experience burst loads (unusually high loads) between lengthy periods of idle time, consider testing your application by increasing the minWorkerThreads and minIOThreads settings.
For applications that make long-running calls, consider the following settings in the Machine.config file:
Set maxconnection to 12 times the number of CPUs.
Set maxIoThreads and maxWorkerThreads to 100.
Now test the application without changing the default setting for minFreeThreads. If you see high CPU utilization and context switching, test by reducing maxWorkerThreads or increasing minFreeThreads.
For ASP.NET applications that use the ASPCOMPAT flag, you should ensure that the total thread count for the worker process does not exceed the following value:
75 + ((maxWorkerThread + maxIoThreads) * #CPUs * 2)
For more information and implementation details, see "Formula for Reducing Contention" in Chapter 6, "Improving ASP.NET Performance." Also see "Tuning Options" in the "ASP.NET Tuning" section in Chapter 17, "Tuning .NET Application Performance."
How to handle long-running calls
Long-running calls from ASP.NET applications block the calling thread. Under load, this may quickly cause thread starvation, where your application uses all available threads and stops responding because there are not enough threads available. It may also quickly cause queuing and rejected requests. An ASP.NET application that calls a long-running Web service is an application that blocks the calling thread. In this common scenario, you can call the Web service asynchronously and then display a busy page or a progress page on the client. By retaining the Web service proxy in server-side state by polling from the browser by using the > meta < refresh tag, you can detect when the Web service call completes and then return the data to the client.
If design changes are not an alternative, consider tuning the thread pool as described earlier.
How to cache data
ASP.NET can cache data by using the Cache API, by using output caching, or by using partial page fragment caching. Regardless of the implementation approach, you need to consider an appropriate caching policy that identifies the data you want to cache, the place you want to cache the data in, and how frequently you want to update the cache.
To use effective fragment caching, separate the static and the dynamic areas of your page, and use user controls.
How to call STA components from ASP.NET
STA components must be called by the thread that creates them. This thread affinity can create a significant bottleneck. Rewrite the STA component by using managed code if you can. Otherwise, make sure you use the ASPCOMPAT attribute on the pages that call the component to avoid thread switching overhead. Do not put STA components in session state to avoid limiting access to a single thread. Avoid STA components entirely if you can.
How to handle session state
If you do not need session state, disable it. If you do need session state, you have three options:
The in-process state store
The out-of-process state service
SQL Server
The in-process state store offers the best performance, but it introduces process affinity, which prevents you from scaling out your solution in a Web farm. For Web farm scenarios, you need one of the out-of-process stores. However, the out-of-process stores incur the overhead of serialization and network latency. Be aware that any object that you want to store in out-of-process session state must be serializable.
Other optimizations include using primitive types where you can to minimize serialization overhead and using the ReadOnly attribute on pages that only read session state.
Improving Web Services Performance
The solutions in this section show how to improve Web service performance. The majority of the solutions are detailed in Chapter 10, "Improving Web Services Performance."
How to improve Web service performance
Start by tuning the thread pool. If you have sufficient CPU and if you have queued work, apply the tuning formula specified in Chapter 10. Make sure that you pool Web service connections. Make sure that you send only the data you need to send, and ensure that you design for chunky interfaces. Also consider using asynchronous server-side processing if your Web service performs extensive I/O operations. Consider caching for reference data and for any internal data that your Web service relies upon.
How to handle large data transfer
To perform large data transfers, start by checking that the maxRequestLength parameter in the < httpRuntime > element of your configuration file is large enough. This parameter limits the maximum SOAP message size for a Web service. Next, check your timeout settings. Set an appropriate timeout on the Web service proxy, and make sure that your ASP.NET timeout is larger than your Web service timeout.
You can handle large data transfer in a number of ways:
Use a byte array parameter. Using a byte array parameter is a simple approach, but if a failure occurs midway through the transfer, the failure forces you to start again from the beginning. When you are uploading data, this approach can also make your Web service subject to denial-of-service attacks.
Return a URL. Return a URL to a file, and then use HTTP to download the file.
Use streaming. If you need to transfer large amounts of data (such as several megabytes) from a Web method, consider streaming to avoid having to buffer large amounts of data in memory at the server and client. You can stream data from a Web service either by implementing IList or by implementing IXmlSerializable.
For more information, see "Bulk Data Transfer" in Chapter 10, "Improving Web Services Performance."
How to handle attachments
You have various options when you are handling attachments by using Web services. When you are choosing an option, consider the following:
WS-Attachments. Web Services Enhancements (WSE) version 1.0 and 2.0 support Web services attachments (WS-Attachments). WS-Attachments use Direct Internet Message Encapsulation (DIME) as an encoding format. While DIME is a supported part of WSE, Microsoft is not investing in this approach long term. DIME is limited because the attachments are outside the SOAP envelope.
Base64 encoding. For today, you should use Base64 encoding in place of WS-Attachments when you have advanced Web services requirements such as security. Base64 encoding creates a larger message payload that may be up to two times the original size. For messages that have large attachments, you can implement a WSE filter to compress the message by using tools like GZIP before you send the message over the network. If you cannot afford the message size that Base64 introduces and if you can rely on the transport for security (for example, Secure Sockets Layer [SSL] or Internet Protocol Security [IPSec]), consider the WS-Attachments implementation in WSE. Securing the message is preferred to securing the transport so that messages can be routed securely. Transport security only addresses point-to-point communication.
SOAP Message Transmission Optimization Mechanism (MTOM). MTOM, which is a derivative work of SOAP Messages with Attachments (SwA), is the likely future interop technology. MTOM is being standardized by the World Wide Web Consortium (W3C) and is easier to compose than SwA.
SwA, also known as WS-I Attachments Profile 1.0, is not supported by Microsoft.
How to improve .NET remoting performance
Remoting is for local, in-process, cross-application domain communication or for integration with legacy systems. If you use remoting, reduce round trips by using chunky interfaces. Improve serialization performance by serializing only the data you need. Use the NonSerialized attribute to prevent unnecessary fields from being serialized.
How to serialize DataSet instances efficiently over remoting
Try to improve serialization efficiency in the following ways:
Use column name aliasing to reduce the size of column names.
Avoid serializing the original and new values for DataSet fields if you do not need to.
Serialize only those DataTable instances in the DataSet that you require. DataSet instances serialize as XML.
To implement binary serialization, see Knowledge Base article 829740, "Improving DataSet Serialization and Remoting Performance," at http://support.microsoft.com/default.aspx?scid=kb;en-us;829740.
Improving Enterprise Services Performance
The solutions in this section show how to improve the performance of your Enterprise Services applications and serviced components. The majority of the solutions are detailed in Chapter 8, "Improving Enterprise Services Performance."
How to improve Enterprise Services performance
Only use Enterprise Services if you need a service. If you need a service, prefer library applications for in-process performance. Use Enterprise Services transactions if you need distributed transactions, but be aware that manual transactions that use ADO.NET or T-SQL offer superior performance for transactions against a single resource manager. Remember to balance performance with ease of development. Declarative Enterprise Services transactions offer the easiest programming model. Also consider your transaction isolation level.
Use object pooling for objects that take a long time to initialize. Make sure that you release objects back to the pool promptly. A good way to do this is to annotate your method with the AutoComplete attribute. Also, clients should call Dispose promptly on the service component. Avoid using packet privacy authentication if you call your serviced components over an IPSec encrypted link. Avoid impersonation, and use a single service identity to access your downstream database to benefit from connection pooling.
Wednesday, July 11, 2007
Wednesday, June 06, 2007
Architecture and Classes ( Microsoft Synchronization Services)
Microsoft Synchronization Services for ADO.NET is a set of DLLs that provides a composable application programming interface (API). Depending on the architecture and needs of your application, you can use all or only some of the components provided. Synchronization Services enables synchronization between a SQL Server Compact Edition client database and a server database or any other data source, such as a service that provides stock quotes in XML. For synchronizing two databases, Synchronization Services supports two-tier and N-tier architectures using any server database for which an ADO.NET provider is available. For synchronizing between a client database and other types of data sources, Synchronization Services supports a service-based architecture. This architecture requires more application code than two-tier and N-tier architectures, but it does not require a developer to take a different approach to synchronization. Rather, it provides a great deal of flexibility for extending the architecture of your application to include synchronization.
The following diagrams show the components involved in two-tier, N-tier, and service-based architectures. The diagrams each show a single client, but there are often multiple clients synchronizing with a single server. Synchronization Services uses a hub-and-spoke model. All changes from each client are synchronized with the server before the changes are sent from the server to other clients (clients do not exchange changes directly with each other). Synchronization Services provides snapshot, download-only, upload-only, and bidirectional synchronization:
Snapshot and download-only synchronization are typically used to store and update reference data, such as a product list, on a client. Data changes that are made at the server are downloaded to the client database during synchronization. Snapshot synchronization completely refreshes data every time the client is synchronized; it is appropriate when you don't want to track incremental changes, or the server does not have the ability to do so. Download-only synchronization downloads only the incremental changes that have occurred since the previous synchronization.
Upload-only synchronization is typically used to insert data, such as a sales order, on a client. Inserts and other data changes made in the client database are uploaded to the server during synchronization.
Bidirectional synchronization is typically used for data, such as customer contact information, that can be updated at the client and server. Any conflicting changes must be handled during synchronization.
Components in the Architecture Diagrams
The components in the architecture diagrams include the client and server databases and a set of classes from the Synchronization Services API. The N-tier and service-based architectures also includes Proxy, Web Service, and Transport components that you must write.
The first diagram shows a two-tier architecture with a client database and a server database.

With the exception of the two databases, all items in the diagram correspond to Synchronization Services classes, which are contained in the following DLLs:
Microsoft.Synchronization.Data.dll contains Synchronization Agent, Synchronization Tables, and Synchronization Groups.
Microsoft. Synchronization.Data.Client.dll contains the Client Synchronization Provider.
Microsoft. Synchronization.Data.Server.dll contains the Server Synchronization Provider and Synchronization Adapters.
All the DLLs depend on System.dll and System.Data.dll from .NET Frameworks 2.0 or later. Microsoft.Synchronization.Data.Client.dll also depends on System.Data.SqlServerCe.dll from SQL Server Compact Edition. For 2-tier applications, all Synchronization Services DLLs reside on the client; for N-tier applications, Microsoft.Synchronization.Data.dll and Microsoft.Synchronization.Data.Server.dll reside on a separate computer that provides a synchronization service.

The second diagram shows an N-tier architecture, which requires a proxy, a service, and a transport mechanism to communicate between the client database and the server database. This architecture is more common than a two-tier architecture, because an N-tier architecture does not require a direct connection between the client and server databases.

The third diagram shows a service-based architecture. This architecture includes a client database, but does not include a server database or the corresponding Server Synchronization Provider and Synchronization Adapters. To use this type of architecture, your application must be able to communicate to the Synchronization Agent through a custom proxy and custom service that provides the same functionality that the Server Synchronization Provider and Synchronization Adapters usually provide, such as retrieving changes to synchronize.
Client Database
The client database for Synchronization Services applications is SQL Server Compact Edition. Synchronization Services provides an infrastructure to track incremental changes in the client database. This infrastructure is enabled the first time any table is synchronized using a method other than snapshot synchronization. Synchronization Services adds the following objects to the client database:
The system table __sysOCSDeletedRows is used to track deletes for all tables that are involved in synchronization.
The system table __sysTxCommitSequence is used to ensure transactions are tracked in the proper sequence.
The columns __sysChangeTxBsn and __sysInsertTxBsn are used to track inserts and updates. The columns are added to each table involved in synchronization.
Server Database
The server database can be any database for which an ADO.NET provider is available. For this Community Technology Preview (CTP) release, the Synchronization Services API has been tested primarily using SQL Server 2005, but we encourage you to try the API with SQL Server 2000 and recent versions of Oracle, IBM, and Sybase databases. If you want to track incremental changes in the server database, you must prepare the database to do so. For more information, see How to: Prepare a Server Database for Synchronization.
Synchronization Services Classes
The following classes are represented in the diagram: SyncAgent, ClientSyncProvider, ServerSyncProvider, SyncTables, SyncGroup, and SyncAdapter.
Synchronization Agent
The synchronization agent is the engine that drives synchronization. It loops through each of the tables to be synchronized; calls the client synchronization provider to retrieve and apply changes at the client database; and calls the server synchronization provider to retrieve and apply changes at the server database. The synchronization agent also maintains session level information for the synchronization and bubbles up success messages, errors, and statistics to the application on the client. For more information, see SyncAgent.
Client Synchronization Provider
The client synchronization provider communicates with the client and shields the synchronization agent from the specific implementation of the client database. The Synchronization Services includes a provider for the SQL Server Compact Edition database. The principal activities of the client synchronization provider are:
Storing information about tables on the client that are enabled for synchronization.
Retrieving changes that occurred in the client database since the last synchronization.
Applying incremental changes to the client database.
Detecting conflicting updates.
For more information, see SqlCeClientSyncProvider.
Server Synchronization Provider
The server synchronization provider communicates with the server and shields the synchronization agent from the specific implementation of the server database. The principal activities of the server synchronization provider are:
Storing information about tables on the server that are enabled for synchronization.
Enabling applications to retrieve changes that occurred in the server database since the last synchronization.
Applying incremental changes to the server database.
Detecting conflicting updates.
For more information, see DbServerSyncProvider.
Synchronization Table and Synchronization Group
A synchronization table is defined for each table that is synchronized. It stores settings, such as the direction of synchronization. Each client can request only the tables it needs, which might not include all the tables that the Server Synchronization Provider makes available. For example, there might be twenty tables, ten of which are configured for bidirectional synchronization in the Server Synchronization Provider. A client might request only twelve of the tables as download-only. Although the server supports upload, the client isn't required to make changes or to synchronize all tables. For more information, see SyncTable.
After a synchronization table is defined it can be added to a synchronization group. A synchronization group is a mechanism to ensure consistent application of changes for a set of tables. If tables are included in a synchronization group, changes to those tables are transferred as a unit and applied at the server in a single transaction. If any change in the group fails, changes for the entire group are retried on the next synchronization. For more information, see SyncGroup.
Synchronization Adapter
Modeled after the data adapter in ADO.NET, the synchronization adapter is defined for each table that is synchronized. It makes available to the server synchronization provider the specific commands needed to interact with the server database, such as the InsertCommand that propagates inserts on the client database to the server database. Because Synchronization Adapters use the ADO.NET DbCommand object, the developer can use any command structure supported by ADO.NET, including inline Transact-SQL, stored procedures, views, functions, and so on. The commands simply require a single result that defines the structure and data to be transferred and applied. For more information, see SyncAdapter.
Proxy, Service, and Transport
Proxy, Service, and Transport are used in N-tier and service-based architectures:
In N-tier applications, Microsoft.Synchronization.Data.Server.dll is used but it does not reside on the client. It typically resides on a middle tier that has a direct connection to the server database. In this case, a proxy and service are required to communicate between the client and the middle tier:
On the client, application code references a proxy for the server synchronization provider rather than directly referencing the provider. The proxy communicates with a service on the middle tier.
On the middle tier, the service inherits from and exposes the same methods as ServerSyncProvider (the abstract class from which DbServerSyncProvider inherits). The server synchronization provider methods are then executed over a direct connection to the server database. Results are routed through the middle tier and back to the client.
In service-based applications, Microsoft.Synchronization.Data.Server.dll is not used on the client. The application code must provide the same functionality that the Server Synchronization Provider and Synchronization Adapters normally provide:
On the client, application code references a proxy for the application code that handles server synchronization provider tasks such as retrieving changes from the data source. The proxy communicates with a service on the middle tier.
On the middle tier, the service inherits from and exposes the same methods as ServerSyncProvider (the abstract class from which DbServerSyncProvider inherits). The methods are then executed by your application code over a direct connection to the server database. Results are routed through the middle tier and back to the client.
Additional Classes in the API
The diagrams in this topic show the major classes in the API, but there are a large number of classes that are not shown. To read information about all of the available classes, see Microsoft.Synchronization.Data, Microsoft.Synchronization.Data.Client,and Microsoft.Synchronization.Data.Server. In the meantime, the following descriptions provide an introduction to four other important classes that you should be familiar with.
Synchronization Anchor
A reference point in time for a set of tables synchronized from the server. Synchronization anchors enable an application to determine which changes should be synchronized during a given session. During synchronization, the client synchronization provider stores in the client database: a last received anchor, which identifies the last change downloaded from the server; and a last sent anchor, which identifies the last change uploaded from the client. On the next synchronization, an application can use these anchors to identify the starting point for synchronizing the next set of changes. For more information, see SyncAnchor.
Synchronization Session Statistics
A set of statistics that the Synchronization Agent provides for each synchronization session. The statistics include information about synchronization times, the number of changes processed, and any conflicts or exceptions that occurred. For more information, see SyncStatistics.
Synchronization Session Variables
Variables that are made available to the developer to use as parameters for the select, insert, update, and delete commands executed at the server. The variables can be used in a number of ways, including: to provide support for conflict detection; to avoid downloading changes to a client more than once; to provide different table schema based on the client database version. The Server Synchronization Provider includes built-in session variables and allows you to create additional variables to pass information to the server. For more information, see SyncSession.
SQL Server Synchronization Adapter Builder
Modeled after the command builder in ADO.NET, the synchronization adapter builder is an aid to developing code for the synchronization commands that are executed by the server synchronization provider. The synchronization adapter builder produces select, insert, update, and delete commands for SQL Server databases based on information that you provide about the tables involved in synchronization. For more information, see SqlSyncAdapterBuilder and Tools to Aid Development (Synchronization Services).
The following diagrams show the components involved in two-tier, N-tier, and service-based architectures. The diagrams each show a single client, but there are often multiple clients synchronizing with a single server. Synchronization Services uses a hub-and-spoke model. All changes from each client are synchronized with the server before the changes are sent from the server to other clients (clients do not exchange changes directly with each other). Synchronization Services provides snapshot, download-only, upload-only, and bidirectional synchronization:
Snapshot and download-only synchronization are typically used to store and update reference data, such as a product list, on a client. Data changes that are made at the server are downloaded to the client database during synchronization. Snapshot synchronization completely refreshes data every time the client is synchronized; it is appropriate when you don't want to track incremental changes, or the server does not have the ability to do so. Download-only synchronization downloads only the incremental changes that have occurred since the previous synchronization.
Upload-only synchronization is typically used to insert data, such as a sales order, on a client. Inserts and other data changes made in the client database are uploaded to the server during synchronization.
Bidirectional synchronization is typically used for data, such as customer contact information, that can be updated at the client and server. Any conflicting changes must be handled during synchronization.
Components in the Architecture Diagrams
The components in the architecture diagrams include the client and server databases and a set of classes from the Synchronization Services API. The N-tier and service-based architectures also includes Proxy, Web Service, and Transport components that you must write.
The first diagram shows a two-tier architecture with a client database and a server database.

With the exception of the two databases, all items in the diagram correspond to Synchronization Services classes, which are contained in the following DLLs:
Microsoft.Synchronization.Data.dll contains Synchronization Agent, Synchronization Tables, and Synchronization Groups.
Microsoft. Synchronization.Data.Client.dll contains the Client Synchronization Provider.
Microsoft. Synchronization.Data.Server.dll contains the Server Synchronization Provider and Synchronization Adapters.
All the DLLs depend on System.dll and System.Data.dll from .NET Frameworks 2.0 or later. Microsoft.Synchronization.Data.Client.dll also depends on System.Data.SqlServerCe.dll from SQL Server Compact Edition. For 2-tier applications, all Synchronization Services DLLs reside on the client; for N-tier applications, Microsoft.Synchronization.Data.dll and Microsoft.Synchronization.Data.Server.dll reside on a separate computer that provides a synchronization service.

The second diagram shows an N-tier architecture, which requires a proxy, a service, and a transport mechanism to communicate between the client database and the server database. This architecture is more common than a two-tier architecture, because an N-tier architecture does not require a direct connection between the client and server databases.

The third diagram shows a service-based architecture. This architecture includes a client database, but does not include a server database or the corresponding Server Synchronization Provider and Synchronization Adapters. To use this type of architecture, your application must be able to communicate to the Synchronization Agent through a custom proxy and custom service that provides the same functionality that the Server Synchronization Provider and Synchronization Adapters usually provide, such as retrieving changes to synchronize.
Client Database
The client database for Synchronization Services applications is SQL Server Compact Edition. Synchronization Services provides an infrastructure to track incremental changes in the client database. This infrastructure is enabled the first time any table is synchronized using a method other than snapshot synchronization. Synchronization Services adds the following objects to the client database:
The system table __sysOCSDeletedRows is used to track deletes for all tables that are involved in synchronization.
The system table __sysTxCommitSequence is used to ensure transactions are tracked in the proper sequence.
The columns __sysChangeTxBsn and __sysInsertTxBsn are used to track inserts and updates. The columns are added to each table involved in synchronization.
Server Database
The server database can be any database for which an ADO.NET provider is available. For this Community Technology Preview (CTP) release, the Synchronization Services API has been tested primarily using SQL Server 2005, but we encourage you to try the API with SQL Server 2000 and recent versions of Oracle, IBM, and Sybase databases. If you want to track incremental changes in the server database, you must prepare the database to do so. For more information, see How to: Prepare a Server Database for Synchronization.
Synchronization Services Classes
The following classes are represented in the diagram: SyncAgent, ClientSyncProvider, ServerSyncProvider, SyncTables, SyncGroup, and SyncAdapter.
Synchronization Agent
The synchronization agent is the engine that drives synchronization. It loops through each of the tables to be synchronized; calls the client synchronization provider to retrieve and apply changes at the client database; and calls the server synchronization provider to retrieve and apply changes at the server database. The synchronization agent also maintains session level information for the synchronization and bubbles up success messages, errors, and statistics to the application on the client. For more information, see SyncAgent.
Client Synchronization Provider
The client synchronization provider communicates with the client and shields the synchronization agent from the specific implementation of the client database. The Synchronization Services includes a provider for the SQL Server Compact Edition database. The principal activities of the client synchronization provider are:
Storing information about tables on the client that are enabled for synchronization.
Retrieving changes that occurred in the client database since the last synchronization.
Applying incremental changes to the client database.
Detecting conflicting updates.
For more information, see SqlCeClientSyncProvider.
Server Synchronization Provider
The server synchronization provider communicates with the server and shields the synchronization agent from the specific implementation of the server database. The principal activities of the server synchronization provider are:
Storing information about tables on the server that are enabled for synchronization.
Enabling applications to retrieve changes that occurred in the server database since the last synchronization.
Applying incremental changes to the server database.
Detecting conflicting updates.
For more information, see DbServerSyncProvider.
Synchronization Table and Synchronization Group
A synchronization table is defined for each table that is synchronized. It stores settings, such as the direction of synchronization. Each client can request only the tables it needs, which might not include all the tables that the Server Synchronization Provider makes available. For example, there might be twenty tables, ten of which are configured for bidirectional synchronization in the Server Synchronization Provider. A client might request only twelve of the tables as download-only. Although the server supports upload, the client isn't required to make changes or to synchronize all tables. For more information, see SyncTable.
After a synchronization table is defined it can be added to a synchronization group. A synchronization group is a mechanism to ensure consistent application of changes for a set of tables. If tables are included in a synchronization group, changes to those tables are transferred as a unit and applied at the server in a single transaction. If any change in the group fails, changes for the entire group are retried on the next synchronization. For more information, see SyncGroup.
Synchronization Adapter
Modeled after the data adapter in ADO.NET, the synchronization adapter is defined for each table that is synchronized. It makes available to the server synchronization provider the specific commands needed to interact with the server database, such as the InsertCommand that propagates inserts on the client database to the server database. Because Synchronization Adapters use the ADO.NET DbCommand object, the developer can use any command structure supported by ADO.NET, including inline Transact-SQL, stored procedures, views, functions, and so on. The commands simply require a single result that defines the structure and data to be transferred and applied. For more information, see SyncAdapter.
Proxy, Service, and Transport
Proxy, Service, and Transport are used in N-tier and service-based architectures:
In N-tier applications, Microsoft.Synchronization.Data.Server.dll is used but it does not reside on the client. It typically resides on a middle tier that has a direct connection to the server database. In this case, a proxy and service are required to communicate between the client and the middle tier:
On the client, application code references a proxy for the server synchronization provider rather than directly referencing the provider. The proxy communicates with a service on the middle tier.
On the middle tier, the service inherits from and exposes the same methods as ServerSyncProvider (the abstract class from which DbServerSyncProvider inherits). The server synchronization provider methods are then executed over a direct connection to the server database. Results are routed through the middle tier and back to the client.
In service-based applications, Microsoft.Synchronization.Data.Server.dll is not used on the client. The application code must provide the same functionality that the Server Synchronization Provider and Synchronization Adapters normally provide:
On the client, application code references a proxy for the application code that handles server synchronization provider tasks such as retrieving changes from the data source. The proxy communicates with a service on the middle tier.
On the middle tier, the service inherits from and exposes the same methods as ServerSyncProvider (the abstract class from which DbServerSyncProvider inherits). The methods are then executed by your application code over a direct connection to the server database. Results are routed through the middle tier and back to the client.
Additional Classes in the API
The diagrams in this topic show the major classes in the API, but there are a large number of classes that are not shown. To read information about all of the available classes, see Microsoft.Synchronization.Data, Microsoft.Synchronization.Data.Client,and Microsoft.Synchronization.Data.Server. In the meantime, the following descriptions provide an introduction to four other important classes that you should be familiar with.
Synchronization Anchor
A reference point in time for a set of tables synchronized from the server. Synchronization anchors enable an application to determine which changes should be synchronized during a given session. During synchronization, the client synchronization provider stores in the client database: a last received anchor, which identifies the last change downloaded from the server; and a last sent anchor, which identifies the last change uploaded from the client. On the next synchronization, an application can use these anchors to identify the starting point for synchronizing the next set of changes. For more information, see SyncAnchor.
Synchronization Session Statistics
A set of statistics that the Synchronization Agent provides for each synchronization session. The statistics include information about synchronization times, the number of changes processed, and any conflicts or exceptions that occurred. For more information, see SyncStatistics.
Synchronization Session Variables
Variables that are made available to the developer to use as parameters for the select, insert, update, and delete commands executed at the server. The variables can be used in a number of ways, including: to provide support for conflict detection; to avoid downloading changes to a client more than once; to provide different table schema based on the client database version. The Server Synchronization Provider includes built-in session variables and allows you to create additional variables to pass information to the server. For more information, see SyncSession.
SQL Server Synchronization Adapter Builder
Modeled after the command builder in ADO.NET, the synchronization adapter builder is an aid to developing code for the synchronization commands that are executed by the server synchronization provider. The synchronization adapter builder produces select, insert, update, and delete commands for SQL Server databases based on information that you provide about the tables involved in synchronization. For more information, see SqlSyncAdapterBuilder and Tools to Aid Development (Synchronization Services).
Thursday, May 03, 2007
Viewstate-less ASP.NET Pages
Theoretically speaking, the viewstate of an ASP.NET page is a chunk of data that represents the last known good state of the page. Upon page rendering, each server control stores in the viewstate the current value of each tracked property. The viewstate is persisted for each page instance and retrieved when the same page instance posts back. The goal of the viewstate is supporting the ASP.NET runtime when it comes to distinguishing postback requests from first-time requests and recreating the state the page had last time it served markup to the client. Put another way, the viewstate allows the ASP.NET runtime to start processing the page from where it left off last time. We all know that HTTP requests are stateless and, in light of this, we're not surprised to find out that two successive requests for the same page in the same session are completely independent and decoupled. This is clearly a problem for Web applications—an old problem indeed. If the user types in some text and submits its work to the server, it is a reasonable expectation that the same HTML element used to contain the text doesn't lose its contents upon postback. This is exactly what would happen, until some countermeasures are applied. In classic ASP, countermeasures were more of tricks and made large use of code blocks and the Request object. In ASP.NET, the same pattern was engineered in the framework and took the form of the viewstate.
Practically speaking, the viewstate is a hash table that contains a tree of hash tables, one for each control in the page tree. Each control's hash table contains a collection of name/value entries where name equals the name of a control's property and value is the current value of that property. A control doesn't have to persist to the viewstate all of its properties. Only properties explicitly implemented as trackable go to the viewstate. What makes a control property trackable? Its implementation:
public string Text
{
get { (string) return ViewState["Text"]; }
set { ViewState["Text"] = value; }
}
In this way, whenever you assign a value to the Text property, the value is not stored within the current instance of the control but in a global container—the viewstate. When an ASP.NET page is done with rendering, it asks all controls to save their viewstate to the page's viewstate. The page's viewstate is then serialized to a binary stream and then encoded to Base64. The resulting string is attached to the client markup for the page as a hidden field. In this way, when the page posts back the viewstate is automatically uploaded and used to recreate the last known good state for that instance of the page.
The use of the viewstate is controversial among ASP.NET developers. The most common snag is that it takes up too much bandwidth in download and upload. In effect, the viewstate can easily reach 10 or even 20 KB. In general, it takes much more than the actual markup. And it's completely ignored and useless for client browsers.
Can you disable it programmatically for ASP.NET pages? You bet. You can do that on a per-page and/or per-control basis. And what's the final effect?
You simply lose the last-known good state. Which is not necessarily a bad thing. For example, imagine a page with a TextBox control. By default, the TextBox saves at least 10 properties to the viewstate, including Text, ReadOnly, MaxLength and visual properties. What's the property you really update across postbacks? 99 percent of the time, it is the sole Text property. This means that ReadOnly, MaxLength, and all visual properties most of the time retain the value you originally hard-coded in the ASPX page markup. Those values are automatically restored on each request because a new page instance is created. Unless you change some of these properties programmatically, you don't need viewstate for a text box. Nonetheless, thousands of developers do that. What about the Text property? It still works as expected, even with a disabled viewstate.
Why? It's because the value typed by the user is posted back along with the viewstate and used to overwrite the last-known good state. So as far as the Text property is concerned, losing the last-known good state is no big deal.
The viewstate is a key feature of ASP.NET, and it's on by default for all properties of all controls (that support it). Using it only when strictly needed will reduce the size of the page significantly and with the download speed of the page
Practically speaking, the viewstate is a hash table that contains a tree of hash tables, one for each control in the page tree. Each control's hash table contains a collection of name/value entries where name equals the name of a control's property and value is the current value of that property. A control doesn't have to persist to the viewstate all of its properties. Only properties explicitly implemented as trackable go to the viewstate. What makes a control property trackable? Its implementation:
public string Text
{
get { (string) return ViewState["Text"]; }
set { ViewState["Text"] = value; }
}
In this way, whenever you assign a value to the Text property, the value is not stored within the current instance of the control but in a global container—the viewstate. When an ASP.NET page is done with rendering, it asks all controls to save their viewstate to the page's viewstate. The page's viewstate is then serialized to a binary stream and then encoded to Base64. The resulting string is attached to the client markup for the page as a hidden field. In this way, when the page posts back the viewstate is automatically uploaded and used to recreate the last known good state for that instance of the page.
The use of the viewstate is controversial among ASP.NET developers. The most common snag is that it takes up too much bandwidth in download and upload. In effect, the viewstate can easily reach 10 or even 20 KB. In general, it takes much more than the actual markup. And it's completely ignored and useless for client browsers.
Can you disable it programmatically for ASP.NET pages? You bet. You can do that on a per-page and/or per-control basis. And what's the final effect?
You simply lose the last-known good state. Which is not necessarily a bad thing. For example, imagine a page with a TextBox control. By default, the TextBox saves at least 10 properties to the viewstate, including Text, ReadOnly, MaxLength and visual properties. What's the property you really update across postbacks? 99 percent of the time, it is the sole Text property. This means that ReadOnly, MaxLength, and all visual properties most of the time retain the value you originally hard-coded in the ASPX page markup. Those values are automatically restored on each request because a new page instance is created. Unless you change some of these properties programmatically, you don't need viewstate for a text box. Nonetheless, thousands of developers do that. What about the Text property? It still works as expected, even with a disabled viewstate.
Why? It's because the value typed by the user is posted back along with the viewstate and used to overwrite the last-known good state. So as far as the Text property is concerned, losing the last-known good state is no big deal.
The viewstate is a key feature of ASP.NET, and it's on by default for all properties of all controls (that support it). Using it only when strictly needed will reduce the size of the page significantly and with the download speed of the page
Friday, February 16, 2007
Static Methods, Factories
What Is a Static Field or Method?
Let's change the question. When is a field or method not part of an object? Answer: when it is part of the class! Remember, an object is an instance of a class and each object exists in a separate space in memory. It is possible to access class fields and class methods without creating an instance of a class using the "static" key word. Declaring a field or method with the static key word, tells the compiler that the field or method is associated with the class itself, not with instances of the class. In a sense, static or "class" fields and methods are global variables and methods that you can touch using the class name. If you think of a class as a blueprint used to create objects, then you can think of static fields and methods are being part of the blueprint itself. There is only one copy of the static fields and methods in memory, shared by all instances of the class.
Static fields are useful when you want to store state related to all instances of a class. A counter is a good example of a static field. The classic use of a static counter is to generate a unique ID or serial number for each instance of a class.
Static methods are useful when you have behavior that is global to the class and not specific to an instance of a class. In contrast, instance methods are useful when the method needs to know about the state of an object. Since data and behavior are intertwined in an object, instance methods have access to the instance fields and can exhibit behavior that is specific to the state of an object.
Using Static Factory Methods Instead of Multiple Constructors
You might wonder why I have chosen to combine the topics of static methods and constructors into a single chapter. The answer is "static factory methods." Instead of writing multiple public constructors, you can write multiple static factory methods and private constructors that return objects. First, here is an example of a static factory method. The method simply constructs an object with default values and then returns a reference to the object.
public static Toaster GetInstance()
{
return new Toaster(ColorType.Black, DEFAULT_NAME);
}
In a sense, this static method is analogous to the no-arg constructor.
public Toaster() {}
Here is the version of the Toaster class that uses static methods and a single private constructor to return toaster objects:
///
/// Summary description for Toaster
///
class Toaster
{
// static factory methods
public static Toaster GetInstance()
{
return new Toaster(ColorType.Black, DEFAULT_NAME);
}
public static Toaster GetInstance(string modelName)
{
return new Toaster(ColorType.Black, modelName);
}
public static Toaster GetInstance(ColorType color)
{
return new Toaster(color, DEFAULT_NAME);
}
public static Toaster GetInstance(ColorType color, string modelName)
{
return new Toaster(color, modelName);
}
public const string DEFAULT_NAME= "Generic";
public enum ColorType {Black, Red, Yellow, White}; // black is the enum default value!
private static ColorType DEFAULT_COLOR= ColorType.Black;
private ColorType color= DEFAULT_COLOR;
private string modelName= DEFAULT_NAME;
// the single private constructor
private Toaster(ColorType color, string modelName)
{
this.color= color; // ColorType cannot be null --> compile time error or defaults to ColorType.Black!
if (modelName != null)
{
this.modelName= modelName;
}
}
// the getters
public string Color
{
get
{
return Enum.Format(typeof(ColorType), color,"G");
}
}
public string ModelName
{
get
{
return modelName; // danger, return ModelName --> stack overflow!
}
}
}
Declaring the only constructor private, prevents any outside caller from directly instantiating the class. The only path to a Toaster object is through a static factory method. So, you can use multiple overloaded public constructors or multiple static factory methods and private constructors to create toaster objects. If you are interested in learning more about using static factory methods instead of multiple constructers check out Effective Java Programming Language Guide by Joshua Bloch, Addison-Wessley, 2001, 252 pp.
Note: The behavior of enum is quite complicated. You cannot set an enum to null and if you fail to explicitly initialize an enum variable, it defaults to the first member of the enumeration. For example:
public static ColorType c; // c --> ColorType.Black
Creational Patterns -- Class Factory and Singleton
I am going to finish off this chapter by introducing two common design patterns: the "Class Factory" and "Singleton" patterns. The class factory is useful when you want to return concrete objects that share a base class, at runtime. The singleton pattern is useful when you only want to allow the creation of one instance of an object in a application. These patterns are both considered "Creational" patterns since they abstract the creation of objects.
Using a Static Method to Return Concrete Classes -- The Class Factory.
The concept of using static methods to return objects is a useful one. In the previous code, you learned how to replace multiple constructors with multiple static factory methods. Another useful design pattern is the "Class Factory." A class factory can be used to return concrete implementations of a common base type.
In Chapter 2, you learned about polymorphism using the Drawable abstract class. Concrete implementations of the Drawable class such as square or circle provide a concrete implementation of the abstract "DrawYourself" method. Let's resurrect our Drawable class.
abstract class Drawable
{
public abstract String DrawYourself();
}
class Circle : Drawable
{
public override String DrawYourself()
{
return "Circle";
}
}
class Square : Drawable
{
public override String DrawYourself()
{
return "Square";
}
}
In this example of the class factory pattern, you pass a parameter to a static factory method that then returns the appropriate concrete implementation of the Drawable abstract class. To insure that the method is passed valid parameters at compile time, you can define a type safe enum "DrawableType":
public enum DrawableType {CIRCLE,SQUARE};
Here is our class factory:
///
/// Summary description for class Factory.
///
class Factory
{
public enum DrawableType {CIRCLE,SQUARE};
public static Drawable GetInstance(DrawableEnum e)
{
if (e == DrawableType.CIRCLE)
{
return new Circle();
}
else if (e == DrawableType.SQUARE)
{
return new Square();
}
else
{
throw new IndexOutOfRangeException(); // should never get here
}
}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Drawable d1= Factory.GetInstance(Factory.DrawableType.CIRCLE);
Console.WriteLine(d1.DrawYourself());
Drawable d2= Factory.GetInstance(Factory.DrawableType.SQUARE);
Console.WriteLine(d2.DrawYourself());Console.ReadLine();
}
}
Note that d1 and d1 are reference variables of the Type Drawable, yet the code outputs: Circle, Square. Polymorphism at work! The class factory design pattern allows your application to create concrete implementations of a base class or interface dynamically at runtime in response to user or system input.
The Singleton Pattern
The "Singleton" pattern is a special version of the class factory that only returns a single instance of a class. The singleton pattern is useful when there should only be one instance of an object. As an example, there may be many soldiers that derive from person, but there should only be one reigning King of England that derives from person! Here is a sample that uses a static factory method to insure that only one instance is created. The factory method "GetInstance" returns a reference to the single instance of the class. Note that you must declare any constructors private, so that the constructors are not visible outside of the class. This insures that. there will be one and only one instance of MyClass.
///
/// Summary description for MyClass.
///
class MyClass
{
private static MyClass theOnlyOne= new MyClass(); // create one and only one instance of the class
public static MyClass GetInstance()
{
return theOnlyOne;
}
public readonly string description= "The One and Only.";
private MyClass(){}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
MyClass mc= MyClass.GetInstance();
Console.WriteLine(mc.description);
Console.ReadLine();
}
}
One of the advantages of the factory method is that you can modify the singleton behavior of the class without affecting the caller of the class. If you decide that your application should now support Kings present and past, then you can modify MyClass to return a new instance for each call to GetInstance. Here is the modified multi-instance version of MyClass:
///
/// Summary description for MyClass
///
class MyClass
{
public static MyClass GetInstance()
{
return new MyClass();
}
public readonly string description= "OneOfMany";
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
MyClass mc= MyClass.GetInstance();
Console.WriteLine(mc.description);
Console.ReadLine();
}
}
Let's change the question. When is a field or method not part of an object? Answer: when it is part of the class! Remember, an object is an instance of a class and each object exists in a separate space in memory. It is possible to access class fields and class methods without creating an instance of a class using the "static" key word. Declaring a field or method with the static key word, tells the compiler that the field or method is associated with the class itself, not with instances of the class. In a sense, static or "class" fields and methods are global variables and methods that you can touch using the class name. If you think of a class as a blueprint used to create objects, then you can think of static fields and methods are being part of the blueprint itself. There is only one copy of the static fields and methods in memory, shared by all instances of the class.
Static fields are useful when you want to store state related to all instances of a class. A counter is a good example of a static field. The classic use of a static counter is to generate a unique ID or serial number for each instance of a class.
Static methods are useful when you have behavior that is global to the class and not specific to an instance of a class. In contrast, instance methods are useful when the method needs to know about the state of an object. Since data and behavior are intertwined in an object, instance methods have access to the instance fields and can exhibit behavior that is specific to the state of an object.
Using Static Factory Methods Instead of Multiple Constructors
You might wonder why I have chosen to combine the topics of static methods and constructors into a single chapter. The answer is "static factory methods." Instead of writing multiple public constructors, you can write multiple static factory methods and private constructors that return objects. First, here is an example of a static factory method. The method simply constructs an object with default values and then returns a reference to the object.
public static Toaster GetInstance()
{
return new Toaster(ColorType.Black, DEFAULT_NAME);
}
In a sense, this static method is analogous to the no-arg constructor.
public Toaster() {}
Here is the version of the Toaster class that uses static methods and a single private constructor to return toaster objects:
///
/// Summary description for Toaster
///
class Toaster
{
// static factory methods
public static Toaster GetInstance()
{
return new Toaster(ColorType.Black, DEFAULT_NAME);
}
public static Toaster GetInstance(string modelName)
{
return new Toaster(ColorType.Black, modelName);
}
public static Toaster GetInstance(ColorType color)
{
return new Toaster(color, DEFAULT_NAME);
}
public static Toaster GetInstance(ColorType color, string modelName)
{
return new Toaster(color, modelName);
}
public const string DEFAULT_NAME= "Generic";
public enum ColorType {Black, Red, Yellow, White}; // black is the enum default value!
private static ColorType DEFAULT_COLOR= ColorType.Black;
private ColorType color= DEFAULT_COLOR;
private string modelName= DEFAULT_NAME;
// the single private constructor
private Toaster(ColorType color, string modelName)
{
this.color= color; // ColorType cannot be null --> compile time error or defaults to ColorType.Black!
if (modelName != null)
{
this.modelName= modelName;
}
}
// the getters
public string Color
{
get
{
return Enum.Format(typeof(ColorType), color,"G");
}
}
public string ModelName
{
get
{
return modelName; // danger, return ModelName --> stack overflow!
}
}
}
Declaring the only constructor private, prevents any outside caller from directly instantiating the class. The only path to a Toaster object is through a static factory method. So, you can use multiple overloaded public constructors or multiple static factory methods and private constructors to create toaster objects. If you are interested in learning more about using static factory methods instead of multiple constructers check out Effective Java Programming Language Guide by Joshua Bloch, Addison-Wessley, 2001, 252 pp.
Note: The behavior of enum is quite complicated. You cannot set an enum to null and if you fail to explicitly initialize an enum variable, it defaults to the first member of the enumeration. For example:
public static ColorType c; // c --> ColorType.Black
Creational Patterns -- Class Factory and Singleton
I am going to finish off this chapter by introducing two common design patterns: the "Class Factory" and "Singleton" patterns. The class factory is useful when you want to return concrete objects that share a base class, at runtime. The singleton pattern is useful when you only want to allow the creation of one instance of an object in a application. These patterns are both considered "Creational" patterns since they abstract the creation of objects.
Using a Static Method to Return Concrete Classes -- The Class Factory.
The concept of using static methods to return objects is a useful one. In the previous code, you learned how to replace multiple constructors with multiple static factory methods. Another useful design pattern is the "Class Factory." A class factory can be used to return concrete implementations of a common base type.
In Chapter 2, you learned about polymorphism using the Drawable abstract class. Concrete implementations of the Drawable class such as square or circle provide a concrete implementation of the abstract "DrawYourself" method. Let's resurrect our Drawable class.
abstract class Drawable
{
public abstract String DrawYourself();
}
class Circle : Drawable
{
public override String DrawYourself()
{
return "Circle";
}
}
class Square : Drawable
{
public override String DrawYourself()
{
return "Square";
}
}
In this example of the class factory pattern, you pass a parameter to a static factory method that then returns the appropriate concrete implementation of the Drawable abstract class. To insure that the method is passed valid parameters at compile time, you can define a type safe enum "DrawableType":
public enum DrawableType {CIRCLE,SQUARE};
Here is our class factory:
///
/// Summary description for class Factory.
///
class Factory
{
public enum DrawableType {CIRCLE,SQUARE};
public static Drawable GetInstance(DrawableEnum e)
{
if (e == DrawableType.CIRCLE)
{
return new Circle();
}
else if (e == DrawableType.SQUARE)
{
return new Square();
}
else
{
throw new IndexOutOfRangeException(); // should never get here
}
}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Drawable d1= Factory.GetInstance(Factory.DrawableType.CIRCLE);
Console.WriteLine(d1.DrawYourself());
Drawable d2= Factory.GetInstance(Factory.DrawableType.SQUARE);
Console.WriteLine(d2.DrawYourself());Console.ReadLine();
}
}
Note that d1 and d1 are reference variables of the Type Drawable, yet the code outputs: Circle, Square. Polymorphism at work! The class factory design pattern allows your application to create concrete implementations of a base class or interface dynamically at runtime in response to user or system input.
The Singleton Pattern
The "Singleton" pattern is a special version of the class factory that only returns a single instance of a class. The singleton pattern is useful when there should only be one instance of an object. As an example, there may be many soldiers that derive from person, but there should only be one reigning King of England that derives from person! Here is a sample that uses a static factory method to insure that only one instance is created. The factory method "GetInstance" returns a reference to the single instance of the class. Note that you must declare any constructors private, so that the constructors are not visible outside of the class. This insures that. there will be one and only one instance of MyClass.
///
/// Summary description for MyClass.
///
class MyClass
{
private static MyClass theOnlyOne= new MyClass(); // create one and only one instance of the class
public static MyClass GetInstance()
{
return theOnlyOne;
}
public readonly string description= "The One and Only.";
private MyClass(){}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
MyClass mc= MyClass.GetInstance();
Console.WriteLine(mc.description);
Console.ReadLine();
}
}
One of the advantages of the factory method is that you can modify the singleton behavior of the class without affecting the caller of the class. If you decide that your application should now support Kings present and past, then you can modify MyClass to return a new instance for each call to GetInstance. Here is the modified multi-instance version of MyClass:
///
/// Summary description for MyClass
///
class MyClass
{
public static MyClass GetInstance()
{
return new MyClass();
}
public readonly string description= "OneOfMany";
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
MyClass mc= MyClass.GetInstance();
Console.WriteLine(mc.description);
Console.ReadLine();
}
}
Tuesday, January 23, 2007
Updater Application Block
Introduction
Automatically deploying your windows forms applications is one of most discussed features in .NET Development. There are many different solutions to this problem: Running applications from the Web (See http://www.theserverside.net/articles/showarticle.tss?id=RunFromWeb), the Windows form App Updater Component (See http://www.theserverside.net/articles/showarticle.tss?id=AppUpdater), and VS.NET 2005 adds support for deploying applications from the web.
In this article, I’ll discuss one other option: the Application Updater Starter Block from the Practices and Guidance Group at Microsoft. The Application Updater block provides one implementation you can use to automatically update your applications. Unlike some other solutions, the AppUpdater block was designed so that you can extend and modify it to suit your purposes. Several .NET developers have written and posted extensions or modifications to it. I would choose to base my no touch deployment implementation on the AppUpdater when two conditions are true:
First, you can’t wait for VS.NET 2005. No Touch deployment is built in to VS.NET 2005. It’s easier to use than any of the other options.
Second, you need to extend the features in the AppUpdater component. The design of the Updater Application Block provides quite a few more opportunities for extension than the AppUpdater Component.
I’ll give an overview of how to use the Updater Application Block. I’ll show up the possible extension points for creating your own implementations of some of the components. I’ll finish by discussing the reasons for picking one component over another. This article does not include its own sample, I’m relying on the samples that come with the Updater Block. To get a clear idea of the tasks that are involved in building an application that automatically updates itself, see the App Updater article. The steps are the same, even though the component is different. This article focuses on the architecture and design of the Updater Application Block.
Download and Installation
The Updater Application block installation is a two step installation. You must install the application block, then run the quick start scripts to configure both the server and the client for updates. Of course, you’d know this if you read the readme.txt file, but no one does that. The point is you don’t just load the solutions, build them, and try to run the updater. It won’t work. The quick start scripts create virtual directories on the server, install all the updates to those directories and configure the client applications to search for updates on the local host web server.
Using the samples
The samples that come with the Updater show the same basic tasks needed to auto-update an application:
Use a shim to load the application. (This facilitates updating a running application)
Code on the client machines periodically polls a known server for updates.
When a new update is available, it is downloaded and verified.
After verification, the new version is installed.
The application restarts and the new version runs.
Given these four basic tasks, it’s no surprise that most of the implementations you’ll find are reasonably similar in their design and implementation. If anything, such similarities shows that these different implementations are on the right track. The one item that separates the Updater Application Block is that its design is extensible: All the key tasks are defined by interfaces and you can create new classes to implement any of them. Before we get that far, let’s go over the solutions already provided in the Updater.
The AppStart assembly contains the shim that loads the target application. The Updater starter block includes support for running only one version of the target application. In its main routine, it creates a Mutex to ensure that the target application is not currently running:
//Check to see if AppStart is already running FOR the
// particular versioned folder of the target application
bool isOwned = false;
_appStartMutex = new Mutex( true, _config.ExePath +
_mutexGuid.ToString(), out isOwned );
if ( !isOwned )
{
MessageBox.Show(
String.Format(
CultureInfo.CurrentCulture,
"There is already a copy of the application '{0}' running. Please close that application before starting a new one.",
_config.ExePath ) );
Environment.Exit( 1 );
}
StartApp_Process();
The code snippet above uses a system mutex to determine if another copy of the AppStarter, targeting this same application, is running. The StartApp_Process method launches the target application, after setting the working directory correctly:
_exePath = Path.Combine( _config.FolderName , _config.ExePath );
//Start the app
try
{
ProcessStartInfo p = new ProcessStartInfo (_exePath);
p.WorkingDirectory = Path.GetDirectoryName(_exePath);
_process = Process.Start (p);
Debug.WriteLine("APPLICATION STARTER: Started app: " + _exePath);
}
catch (Exception e)
{
Debug.WriteLine("APPLICATION STARTER: Failed to start process at: "
+ _exePath);
HandleTerminalError(e);
}
As you’ll see a little further on, the config object returns the current version of the application to shim. When the downloader retrieves new versions, it writes new entries to determine which version should be launched.
Find and Download New versions
Finding and downloading content is accomplished by a class that implements the IDownloader interface. The IDownloader interface contains four methods, to support both synchronous and asynchronous downloading:
public enum JobStatus
{
Ready,
Downloading,
Error,
Cancelled,
Validating
}
public interface IDownloader : IDisposable
{
void Init( XmlNode config );
void Download( string sourceFile, string destFile,
TimeSpan maxTimeWait );
Guid BeginDownload( string[] sourceFile, string[] destFile );
JobStatus GetJobStatus( Guid jobId );
}
The Updater Application block contains one class that implements the IDownloader interface: The BITS Downloader. BITS is a service available on Windows 2000 and above. It is used by the Windows Update service to retrieve its updates. It is secure, handles interruption in service, and works quietly in the background. The downside is that it won’t work if you need to support older Windows versions, like Windows 98. In that case, you’ll need to create your own downloader for those aging clients. The best part about this design in the Updater Application Block is that it is configurable at runtime. The ApplicationUpdater reads a configuration file to determine which downloader to create. The config section looks like this (formatted for your screen, not for your config reader):
type=
"Microsoft.ApplicationBlocks.ApplicationUpdater.
Downloaders.BITSDownloader"
assembly=
"Microsoft.ApplicationBlocks.ApplicationUpdater,
Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
You could create a different downloader for other clients. You could then write a different key in the config file when you install the system to launch a different downloader on outdated systems. As I said earlier though, while I like the architecture and design of the Update block, I would use the AppUpdater control before I would write my own custom downloader.
Validating what you download
Downloading and installing new stuff is great, but you need to guard against replacing your application with some other arbitrary executable. The Updater application block uses signatures to determine if the file has been tampered with. If there is any doubt about the file’s authenticity, the installation is aborted.
The validation contract is defined by the IValidator interface. Like the downloader, this means that you can create your own validator using your own algorithm, as long as you implement the interface:
public interface IValidator : IDisposable
{
void Init( XmlNode config );
bool Validate( string filePath, string signature );
bool Validate( XmlNode xml, string signature );
string Sign( string filePath, string key );
string Sign( XmlNode xml, string key );
}
The Init method is a hook to let you initialize your validators. The Validate and Sign methods are pairs: when you create an update for automatic download, you generate a signature for each file in the download. The signature gets generated by the Sign() method and then must be written into the server manifest file. The download client attempts to match the signatures using the Validate( ) method. The Updater Application block includes a utility application that uses the configured validator to generate the manifest (See Figure 1).
Validation is very important for any system that supports auto-updating; it’s worth going over in more detail. Remember that any assembly loaded from your computer is fully trusted. It was loaded from the “My Computer” zone. That means version 1.0.0.0 of your application is fully trusted. Your fully trusted application can go to the web, and download new assemblies without raising warnings from the system. After downloading these new assemblies, they will be loaded from the “My Computer” zone as well. That means any future versions are also fully trusted. That’s good, because that’s what you want: You automatically update your fully trusted application, and the new version should also enjoy the privileges of a fully trusted application. However, should someone sneak into your server and put a set of assemblies there, you would unwittingly deliver them to your entire install base. Worse, you’d give them that full trust by putting those assemblies on the local computer. That must be prevented. Validation prevents that.
Validation ensures that the files have come from a trusted source. Before you deploy your first version to any client computer, you generate a key that will be used to create the signature. That key is mixed in with the hash code generated from each downloadable file. That’s the important key point: The signature depends on the key, and the contents of the downloaded files. Should the server security be violated, the attackers will not have the key, and will not be able to generate the correct signature. In the same vein, if the attackers modify a file after you’ve installed it at the server, the signature will change. That will cause the validation to fail as well.
The Updater contains two possible validators you can use: a symmetric validator, and a public / private key pair (or asymmetric) validator. The asymmetric validator is more secure, because the client computers have a different key than the server. But you must guard your private key carefully. With either validator, any attacker that gains access to the key can fool you into downloading malicious content. That is a bit easier with the symmetric key because it is stored on client machines. It is encrypted, but it’s still not as safe as the asymmetric key. My recommendation is that you should use the asymmetric validator on any projects where security is a serious concern (and aren’t they all like that?)
Post Update Processing
The final addition to the Update Application block is the ability to perform post-update installation tasks. You will use this feature if you are updating a component that needs specific client-side processing to occur when an update is downloaded (like COM registration. In fact, the Updater block contains one class that implements IPostProcessor, to update the registry). To use the Post Processing facility, you create a class that implements the IPostProcessor interface, which has only one method: Run. That method should perform whatever processing you need. Include that assembly in the server manifest, with the instructions to load and execute the post-processing code:
type="Microsoft.ApplicationBlocks.
ApplicationUpdater.Quickstarts.RegPostProcessor"
assembly="FlexPoints, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null"
name="PostProcessor\Flexpoints.exe" />
The download manager will get the specified file, and after downloading and verifying the entire package, the post – processing instructions will be executed.
Which to choose?
I started this series with the App Updater component because it is the simple, straightforward way to deploy updates to a client application simply. I’m finishing with the Updater Application Block because it shows a combination of best practices for creating an extensible solution to this problem. All the extensible components are defined by interfaces that can be implemented in a variety of ways: IDownloader, IValidator, and IPostProcessor. An application configuration file defines which assemblies, and which types in those assemblies, should be created to do each of the tasks specified by these interfaces. You can add new capabilities to the Updater facility by creating new assemblies and referencing them in the config files. It’s rare that you will need to modify the core libraries.
Putting these facts together, the Updater block provides a much better framework for you to extend over time, should you need. It will also be applicable to a much wider array of applications. If you are an experienced .NET developer, use the Updater as your starting point. When you need features not already in the updater, you’ll find ways to extend it. On the other hand, the AppUpdater component is much simpler to understand and work with. If you’ve never worked with no-touch deployment, start there. Understand that component, build one or two self-updating applications, then move on to the Updater Application Block.
Automatically deploying your windows forms applications is one of most discussed features in .NET Development. There are many different solutions to this problem: Running applications from the Web (See http://www.theserverside.net/articles/showarticle.tss?id=RunFromWeb), the Windows form App Updater Component (See http://www.theserverside.net/articles/showarticle.tss?id=AppUpdater), and VS.NET 2005 adds support for deploying applications from the web.
In this article, I’ll discuss one other option: the Application Updater Starter Block from the Practices and Guidance Group at Microsoft. The Application Updater block provides one implementation you can use to automatically update your applications. Unlike some other solutions, the AppUpdater block was designed so that you can extend and modify it to suit your purposes. Several .NET developers have written and posted extensions or modifications to it. I would choose to base my no touch deployment implementation on the AppUpdater when two conditions are true:
First, you can’t wait for VS.NET 2005. No Touch deployment is built in to VS.NET 2005. It’s easier to use than any of the other options.
Second, you need to extend the features in the AppUpdater component. The design of the Updater Application Block provides quite a few more opportunities for extension than the AppUpdater Component.
I’ll give an overview of how to use the Updater Application Block. I’ll show up the possible extension points for creating your own implementations of some of the components. I’ll finish by discussing the reasons for picking one component over another. This article does not include its own sample, I’m relying on the samples that come with the Updater Block. To get a clear idea of the tasks that are involved in building an application that automatically updates itself, see the App Updater article. The steps are the same, even though the component is different. This article focuses on the architecture and design of the Updater Application Block.
Download and Installation
The Updater Application block installation is a two step installation. You must install the application block, then run the quick start scripts to configure both the server and the client for updates. Of course, you’d know this if you read the readme.txt file, but no one does that. The point is you don’t just load the solutions, build them, and try to run the updater. It won’t work. The quick start scripts create virtual directories on the server, install all the updates to those directories and configure the client applications to search for updates on the local host web server.
Using the samples
The samples that come with the Updater show the same basic tasks needed to auto-update an application:
Use a shim to load the application. (This facilitates updating a running application)
Code on the client machines periodically polls a known server for updates.
When a new update is available, it is downloaded and verified.
After verification, the new version is installed.
The application restarts and the new version runs.
Given these four basic tasks, it’s no surprise that most of the implementations you’ll find are reasonably similar in their design and implementation. If anything, such similarities shows that these different implementations are on the right track. The one item that separates the Updater Application Block is that its design is extensible: All the key tasks are defined by interfaces and you can create new classes to implement any of them. Before we get that far, let’s go over the solutions already provided in the Updater.
The AppStart assembly contains the shim that loads the target application. The Updater starter block includes support for running only one version of the target application. In its main routine, it creates a Mutex to ensure that the target application is not currently running:
//Check to see if AppStart is already running FOR the
// particular versioned folder of the target application
bool isOwned = false;
_appStartMutex = new Mutex( true, _config.ExePath +
_mutexGuid.ToString(), out isOwned );
if ( !isOwned )
{
MessageBox.Show(
String.Format(
CultureInfo.CurrentCulture,
"There is already a copy of the application '{0}' running. Please close that application before starting a new one.",
_config.ExePath ) );
Environment.Exit( 1 );
}
StartApp_Process();
The code snippet above uses a system mutex to determine if another copy of the AppStarter, targeting this same application, is running. The StartApp_Process method launches the target application, after setting the working directory correctly:
_exePath = Path.Combine( _config.FolderName , _config.ExePath );
//Start the app
try
{
ProcessStartInfo p = new ProcessStartInfo (_exePath);
p.WorkingDirectory = Path.GetDirectoryName(_exePath);
_process = Process.Start (p);
Debug.WriteLine("APPLICATION STARTER: Started app: " + _exePath);
}
catch (Exception e)
{
Debug.WriteLine("APPLICATION STARTER: Failed to start process at: "
+ _exePath);
HandleTerminalError(e);
}
As you’ll see a little further on, the config object returns the current version of the application to shim. When the downloader retrieves new versions, it writes new entries to determine which version should be launched.
Find and Download New versions
Finding and downloading content is accomplished by a class that implements the IDownloader interface. The IDownloader interface contains four methods, to support both synchronous and asynchronous downloading:
public enum JobStatus
{
Ready,
Downloading,
Error,
Cancelled,
Validating
}
public interface IDownloader : IDisposable
{
void Init( XmlNode config );
void Download( string sourceFile, string destFile,
TimeSpan maxTimeWait );
Guid BeginDownload( string[] sourceFile, string[] destFile );
JobStatus GetJobStatus( Guid jobId );
}
The Updater Application block contains one class that implements the IDownloader interface: The BITS Downloader. BITS is a service available on Windows 2000 and above. It is used by the Windows Update service to retrieve its updates. It is secure, handles interruption in service, and works quietly in the background. The downside is that it won’t work if you need to support older Windows versions, like Windows 98. In that case, you’ll need to create your own downloader for those aging clients. The best part about this design in the Updater Application Block is that it is configurable at runtime. The ApplicationUpdater reads a configuration file to determine which downloader to create. The config section looks like this (formatted for your screen, not for your config reader):
"Microsoft.ApplicationBlocks.ApplicationUpdater.
Downloaders.BITSDownloader"
assembly=
"Microsoft.ApplicationBlocks.ApplicationUpdater,
Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
You could create a different downloader for other clients. You could then write a different key in the config file when you install the system to launch a different downloader on outdated systems. As I said earlier though, while I like the architecture and design of the Update block, I would use the AppUpdater control before I would write my own custom downloader.
Validating what you download
Downloading and installing new stuff is great, but you need to guard against replacing your application with some other arbitrary executable. The Updater application block uses signatures to determine if the file has been tampered with. If there is any doubt about the file’s authenticity, the installation is aborted.
The validation contract is defined by the IValidator interface. Like the downloader, this means that you can create your own validator using your own algorithm, as long as you implement the interface:
public interface IValidator : IDisposable
{
void Init( XmlNode config );
bool Validate( string filePath, string signature );
bool Validate( XmlNode xml, string signature );
string Sign( string filePath, string key );
string Sign( XmlNode xml, string key );
}
The Init method is a hook to let you initialize your validators. The Validate and Sign methods are pairs: when you create an update for automatic download, you generate a signature for each file in the download. The signature gets generated by the Sign() method and then must be written into the server manifest file. The download client attempts to match the signatures using the Validate( ) method. The Updater Application block includes a utility application that uses the configured validator to generate the manifest (See Figure 1).
Validation is very important for any system that supports auto-updating; it’s worth going over in more detail. Remember that any assembly loaded from your computer is fully trusted. It was loaded from the “My Computer” zone. That means version 1.0.0.0 of your application is fully trusted. Your fully trusted application can go to the web, and download new assemblies without raising warnings from the system. After downloading these new assemblies, they will be loaded from the “My Computer” zone as well. That means any future versions are also fully trusted. That’s good, because that’s what you want: You automatically update your fully trusted application, and the new version should also enjoy the privileges of a fully trusted application. However, should someone sneak into your server and put a set of assemblies there, you would unwittingly deliver them to your entire install base. Worse, you’d give them that full trust by putting those assemblies on the local computer. That must be prevented. Validation prevents that.
Validation ensures that the files have come from a trusted source. Before you deploy your first version to any client computer, you generate a key that will be used to create the signature. That key is mixed in with the hash code generated from each downloadable file. That’s the important key point: The signature depends on the key, and the contents of the downloaded files. Should the server security be violated, the attackers will not have the key, and will not be able to generate the correct signature. In the same vein, if the attackers modify a file after you’ve installed it at the server, the signature will change. That will cause the validation to fail as well.
The Updater contains two possible validators you can use: a symmetric validator, and a public / private key pair (or asymmetric) validator. The asymmetric validator is more secure, because the client computers have a different key than the server. But you must guard your private key carefully. With either validator, any attacker that gains access to the key can fool you into downloading malicious content. That is a bit easier with the symmetric key because it is stored on client machines. It is encrypted, but it’s still not as safe as the asymmetric key. My recommendation is that you should use the asymmetric validator on any projects where security is a serious concern (and aren’t they all like that?)
Post Update Processing
The final addition to the Update Application block is the ability to perform post-update installation tasks. You will use this feature if you are updating a component that needs specific client-side processing to occur when an update is downloaded (like COM registration. In fact, the Updater block contains one class that implements IPostProcessor, to update the registry). To use the Post Processing facility, you create a class that implements the IPostProcessor interface, which has only one method: Run. That method should perform whatever processing you need. Include that assembly in the server manifest, with the instructions to load and execute the post-processing code:
ApplicationUpdater.Quickstarts.RegPostProcessor"
assembly="FlexPoints, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null"
name="PostProcessor\Flexpoints.exe" />
The download manager will get the specified file, and after downloading and verifying the entire package, the post – processing instructions will be executed.
Which to choose?
I started this series with the App Updater component because it is the simple, straightforward way to deploy updates to a client application simply. I’m finishing with the Updater Application Block because it shows a combination of best practices for creating an extensible solution to this problem. All the extensible components are defined by interfaces that can be implemented in a variety of ways: IDownloader, IValidator, and IPostProcessor. An application configuration file defines which assemblies, and which types in those assemblies, should be created to do each of the tasks specified by these interfaces. You can add new capabilities to the Updater facility by creating new assemblies and referencing them in the config files. It’s rare that you will need to modify the core libraries.
Putting these facts together, the Updater block provides a much better framework for you to extend over time, should you need. It will also be applicable to a much wider array of applications. If you are an experienced .NET developer, use the Updater as your starting point. When you need features not already in the updater, you’ll find ways to extend it. On the other hand, the AppUpdater component is much simpler to understand and work with. If you’ve never worked with no-touch deployment, start there. Understand that component, build one or two self-updating applications, then move on to the Updater Application Block.
Thursday, October 26, 2006
ApplicationSettingsBase in .NET 2.0
The new namespace called System.Configuration and a class in that namespace called ApplicationSettingsBase that will be your new friend when dealing with this issue.
Before we dive into this topic more, a word here from experience with this class - it's remarkably easy and convienent to use as long as you take a few extra steps yourself. I don't give the issue of creating settings a moments thought anymore since this class was released in .NET 2.0.
Now, let's see why...
The first conundrum that a developer faces (and has always faced) when dealing with settings is where to put the data that needs persisting. In the Win32 world and even .NET 1.x, it was common to resort to the in-famous Registry to store application data. You basically have a choice - store it in the per-user area (Current User), or the per-machine area (Local Machine). The latter has additional restrictions in that you need read/write access to the registry keys that you want to edit there and a low rights user account may not have such access. It also can be problematic since there is one set of data there for all users which can complicate data that needs to be per-user.
The Registry also has another glaring deficiency (besides being easily corrupted and hard to edit) - there is no support for format changes to the data being stored. Imagine v1.0 of your glorious application shipped and had several setting entries. A few months go by and you ready v1.1 to ship to customers. You found the need to add some entries to your set and remove a few. You also decide to store these entries in a different registry subfolder. It's pretty much left to you to spend time writing a conversion/migration routine. Even worse, as versions of your application proliferate, you have to continue to update that conversion code to handle everything back to v1.0 so that any stragglers in your user base and get up to your latest version.
What a pain.
This is a place the Microsoft thought long and hard about when they devised ApplicationSettingsBase (which rumor has it derived from some excellent work done in the Microsoft Patterns & Practices group in the famous code "blocks"). ApplicationSettingsBase is one of those swiss army knives that allows a developer to handle how settings are stored, or leave it to the class to handle it on behalf of the developer. The latter is by far the most common and likely approach for most teams. Why spend time writing plumbing code when you can leave it to Microsoft? By default, ApplicationSettingsBase generates a .config file containing the settings stored in a XML format and stored deep down in the MyDocuments area. It's a known safe place to store application and user data and all users will have access to their own MyDocuments area.
Round three on managing settings in a .NET 2.0 application using System.Configuration. ApplicationSettingsBase. We're now at the point of talking about how this class supports migration of settings within your project.
The challenge with any implementation of application settings is how to manage the evolution of those settings over time. You may find the need to add, remove, update and adjust settings as time passes and your application evolves. Using the default storage provider in .NET 2.0, ApplicationSettingsBase writes out the settings and their values into an XML file similiar to app.config but called user.config. Your next question undoubtably is "where does .NET put this file?".
The answer is fascinating.
First, you have to remember that since settings are likely to be per-user based items, they need to go someplace guaranteed to be accessible to even a user with a low rights account (ie. User security group). The stipulated location for this kind of information is the "Documents and Settings" folder typically found in the root of your C: drive. This is also where the MyDocuments folder is located. However, ApplicationSettingsBase doesn't store (by default) its data in MyDocuments. It chose instead to place this information in Local Settings\Application Data. These is a sibling folder to MyDocuments and is intended for per-user data that is non-roaming (ie that is computer dependent).
As an aside, if you regularly deal with issues such as where to place files, what folders you should use for specific purposes, how to respond to environmental changes in the operating system, etc, I highly recommend getting a copy of Microsoft's Designed for Windows XP spec v2.3 which is in Word document format. This handy little tome will help you resolve and dispense with the interminable discussions that sometimes take place among developers trying to decide these issues.
Back to ApplicationSettingsBase. Although the Local Settings\Application Data folder is used, ApplicationSettingsBase doesn't stop there. It proceeds to create a sub folder named with the name of the company assigned to your application EXE file properties. If the name has spaces, those are substituted with underscores. Not sure why Microsoft does this, but they do. Next, the class creates a sub folder under the company name folder with the name of your EXE plus a bunch of ugly looking characters you can think of as a unique identifier of sorts. In this folder, it creates yet another sub folder with the version of your EXE. In that folder goes the user.config with your settings. It's important to note that the version number includes the full version number down to the build and revision octets.
Each time you build your application (by default) Visual Studio .NET 2005 helpfully updates your version number (actually build number)incrementing it each time. When this happens, ApplicationSettingsBase will automatically create a new sub folder in that location I described earlier with the new version number. Over time as you develop an application on a machine, you can end up with hundreds or thousands of these little sub folders each containing a version of the user.config file that existed for that build.
Now for the really interesting (and helpful) part.
When ApplicationSettingsBase is created when your application runs, it offers (but doesn't require) the ability to migrate older settings values to the new structure using ApplicationSettingsBase.Upgrade(). This is one of those auto-magical methods that makes your life as a .NET developer really sweet. This method will upgrade and copy values from an older build/version of your application into the new user.config. So you don't lose settings between builds and neither do your users. Which will make them very happy indeed.
Before we dive into this topic more, a word here from experience with this class - it's remarkably easy and convienent to use as long as you take a few extra steps yourself. I don't give the issue of creating settings a moments thought anymore since this class was released in .NET 2.0.
Now, let's see why...
The first conundrum that a developer faces (and has always faced) when dealing with settings is where to put the data that needs persisting. In the Win32 world and even .NET 1.x, it was common to resort to the in-famous Registry to store application data. You basically have a choice - store it in the per-user area (Current User), or the per-machine area (Local Machine). The latter has additional restrictions in that you need read/write access to the registry keys that you want to edit there and a low rights user account may not have such access. It also can be problematic since there is one set of data there for all users which can complicate data that needs to be per-user.
The Registry also has another glaring deficiency (besides being easily corrupted and hard to edit) - there is no support for format changes to the data being stored. Imagine v1.0 of your glorious application shipped and had several setting entries. A few months go by and you ready v1.1 to ship to customers. You found the need to add some entries to your set and remove a few. You also decide to store these entries in a different registry subfolder. It's pretty much left to you to spend time writing a conversion/migration routine. Even worse, as versions of your application proliferate, you have to continue to update that conversion code to handle everything back to v1.0 so that any stragglers in your user base and get up to your latest version.
What a pain.
This is a place the Microsoft thought long and hard about when they devised ApplicationSettingsBase (which rumor has it derived from some excellent work done in the Microsoft Patterns & Practices group in the famous code "blocks"). ApplicationSettingsBase is one of those swiss army knives that allows a developer to handle how settings are stored, or leave it to the class to handle it on behalf of the developer. The latter is by far the most common and likely approach for most teams. Why spend time writing plumbing code when you can leave it to Microsoft? By default, ApplicationSettingsBase generates a .config file containing the settings stored in a XML format and stored deep down in the MyDocuments area. It's a known safe place to store application and user data and all users will have access to their own MyDocuments area.
Round three on managing settings in a .NET 2.0 application using System.Configuration. ApplicationSettingsBase. We're now at the point of talking about how this class supports migration of settings within your project.
The challenge with any implementation of application settings is how to manage the evolution of those settings over time. You may find the need to add, remove, update and adjust settings as time passes and your application evolves. Using the default storage provider in .NET 2.0, ApplicationSettingsBase writes out the settings and their values into an XML file similiar to app.config but called user.config. Your next question undoubtably is "where does .NET put this file?".
The answer is fascinating.
First, you have to remember that since settings are likely to be per-user based items, they need to go someplace guaranteed to be accessible to even a user with a low rights account (ie. User security group). The stipulated location for this kind of information is the "Documents and Settings" folder typically found in the root of your C: drive. This is also where the MyDocuments folder is located. However, ApplicationSettingsBase doesn't store (by default) its data in MyDocuments. It chose instead to place this information in Local Settings\Application Data. These is a sibling folder to MyDocuments and is intended for per-user data that is non-roaming (ie that is computer dependent).
As an aside, if you regularly deal with issues such as where to place files, what folders you should use for specific purposes, how to respond to environmental changes in the operating system, etc, I highly recommend getting a copy of Microsoft's Designed for Windows XP spec v2.3 which is in Word document format. This handy little tome will help you resolve and dispense with the interminable discussions that sometimes take place among developers trying to decide these issues.
Back to ApplicationSettingsBase. Although the Local Settings\Application Data folder is used, ApplicationSettingsBase doesn't stop there. It proceeds to create a sub folder named with the name of the company assigned to your application EXE file properties. If the name has spaces, those are substituted with underscores. Not sure why Microsoft does this, but they do. Next, the class creates a sub folder under the company name folder with the name of your EXE plus a bunch of ugly looking characters you can think of as a unique identifier of sorts. In this folder, it creates yet another sub folder with the version of your EXE. In that folder goes the user.config with your settings. It's important to note that the version number includes the full version number down to the build and revision octets.
Each time you build your application (by default) Visual Studio .NET 2005 helpfully updates your version number (actually build number)incrementing it each time. When this happens, ApplicationSettingsBase will automatically create a new sub folder in that location I described earlier with the new version number. Over time as you develop an application on a machine, you can end up with hundreds or thousands of these little sub folders each containing a version of the user.config file that existed for that build.
Now for the really interesting (and helpful) part.
When ApplicationSettingsBase is created when your application runs, it offers (but doesn't require) the ability to migrate older settings values to the new structure using ApplicationSettingsBase.Upgrade(). This is one of those auto-magical methods that makes your life as a .NET developer really sweet. This method will upgrade and copy values from an older build/version of your application into the new user.config. So you don't lose settings between builds and neither do your users. Which will make them very happy indeed.
Tuesday, October 24, 2006
Generic List
Generic List Class is a new in the .Net Framework version 2.0 .It represent strongly typed list of the object that can be accessed by index.It also provide methode for search,sort and manipulate list.
Namespace: System.Collections.Generic
Assembly: mscorlib (in mscorlib.dll)
[SerializableAttribute]
public class List : IList, ICollection,IEnumerable, IList, ICollection, IEnumerable
The List class is the generic equivalent of the ArrayList class. It implements the IList generic interface using the array whose size is dynamically increased as required.
The List class uses both an equality comparer and an ordering comparer.
Methods such as Contains, IndexOf, LastIndexOf, and Remove use an equality comparer for the list elements. The default equality comparer for type T is determined as follows. If type T implements the IEquatable generic interface, then the equality comparer is the Equals method of that interface; otherwise, the default equality comparer is Object.Equals(Object).
Methods such as BinarySearch and Sort use an ordering comparer for the list elements. The default comparer for type T is determined as follows. If type T implements the IComparable generic interface, then the default comparer is the CompareTo method of that interface; otherwise, if type T implements the nongeneric IComparable interface, then the default comparer is the CompareTo method of that interface. If type T implements neither interface, then there is no default comparer, and a comparer or comparison delegate must be provided explicitly.
The List is not guaranteed to be sorted. You must sort the List before performing operations (such as BinarySearch) that require the List to be sorted.
Elements in this collection can be accessed using an integer index. Indexes in this collection are zero-based.
List accepts a null reference (Nothing in Visual Basic) as a valid value for reference types and allows duplicate elements
Performance Considerations
In deciding whether to use the List or ArrayList class, both of which have similar functionality, remember that the List class performs better in most cases and is type safe. If a reference type is used for type T of the List class, the behavior of the two classes is identical. However, if a value type is used for type T, you need to consider implementation and boxing issues.
If a value type is used for type T, the compiler generates an implementation of the List class specifically for that value type. That means a list element of a List object does not have to be boxed before the element can be used, and after about 500 list elements are created the memory saved not boxing list elements is greater than the memory used to generate the class implementation.
Example
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
List names = new List();
Console.WriteLine("\nCapacity: {0}", names.Capacity);
names.Add("Vishal");
names.Add("Joe");
names.Add("Prashant");
names.Add("Deepak");
Console.WriteLine();
foreach(string name in names)
{
Console.WriteLine(name);
}
Console.WriteLine("\nCapacity: {0}", names.Capacity);
Console.WriteLine("Count: {0}", names.Count);
Console.WriteLine("\nContains(\"Vishal\"): {0}",
names.Contains("Vishal"));
Console.WriteLine("\nInsert(2, \"Shobhit\")");
names.Insert(2, "Shobhit");
Console.WriteLine();
foreach(string name in names)
{
Console.WriteLine(name);
}
Console.WriteLine("\nnames[3]: {0}", names[3]);
Console.WriteLine("\nRemove(\"Deepak\")");
names.Remove("Deepak");
Console.WriteLine();
foreach(string name in names)
{
Console.WriteLine(name);
}
names.Clear();
Console.WriteLine("\nClear()");
Console.WriteLine("Capacity: {0}", names.Capacity);
Console.WriteLine("Count: {0}", names.Count);
}
}
Namespace: System.Collections.Generic
Assembly: mscorlib (in mscorlib.dll)
[SerializableAttribute]
public class List
The List class is the generic equivalent of the ArrayList class. It implements the IList generic interface using the array whose size is dynamically increased as required.
The List class uses both an equality comparer and an ordering comparer.
Methods such as Contains, IndexOf, LastIndexOf, and Remove use an equality comparer for the list elements. The default equality comparer for type T is determined as follows. If type T implements the IEquatable generic interface, then the equality comparer is the Equals method of that interface; otherwise, the default equality comparer is Object.Equals(Object).
Methods such as BinarySearch and Sort use an ordering comparer for the list elements. The default comparer for type T is determined as follows. If type T implements the IComparable generic interface, then the default comparer is the CompareTo method of that interface; otherwise, if type T implements the nongeneric IComparable interface, then the default comparer is the CompareTo method of that interface. If type T implements neither interface, then there is no default comparer, and a comparer or comparison delegate must be provided explicitly.
The List is not guaranteed to be sorted. You must sort the List before performing operations (such as BinarySearch) that require the List to be sorted.
Elements in this collection can be accessed using an integer index. Indexes in this collection are zero-based.
List accepts a null reference (Nothing in Visual Basic) as a valid value for reference types and allows duplicate elements
Performance Considerations
In deciding whether to use the List or ArrayList class, both of which have similar functionality, remember that the List class performs better in most cases and is type safe. If a reference type is used for type T of the List class, the behavior of the two classes is identical. However, if a value type is used for type T, you need to consider implementation and boxing issues.
If a value type is used for type T, the compiler generates an implementation of the List class specifically for that value type. That means a list element of a List object does not have to be boxed before the element can be used, and after about 500 list elements are created the memory saved not boxing list elements is greater than the memory used to generate the class implementation.
Example
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
List
Console.WriteLine("\nCapacity: {0}", names.Capacity);
names.Add("Vishal");
names.Add("Joe");
names.Add("Prashant");
names.Add("Deepak");
Console.WriteLine();
foreach(string name in names)
{
Console.WriteLine(name);
}
Console.WriteLine("\nCapacity: {0}", names.Capacity);
Console.WriteLine("Count: {0}", names.Count);
Console.WriteLine("\nContains(\"Vishal\"): {0}",
names.Contains("Vishal"));
Console.WriteLine("\nInsert(2, \"Shobhit\")");
names.Insert(2, "Shobhit");
Console.WriteLine();
foreach(string name in names)
{
Console.WriteLine(name);
}
Console.WriteLine("\nnames[3]: {0}", names[3]);
Console.WriteLine("\nRemove(\"Deepak\")");
names.Remove("Deepak");
Console.WriteLine();
foreach(string name in names)
{
Console.WriteLine(name);
}
names.Clear();
Console.WriteLine("\nClear()");
Console.WriteLine("Capacity: {0}", names.Capacity);
Console.WriteLine("Count: {0}", names.Count);
}
}
Monday, October 23, 2006
Settings in VS 2005
Settings can represent user preferences, or valuable information the application needs to use. For example, you might create a series of settings that store user preferences for the color scheme of an application. Or you might store the connection string that specifies a database that your application uses. Settings allow you to both persist information that is critical to the application outside of the code, and to create profiles that store the preferences of individual users.
In C# use settings by accessing the Properties namespace. In the course of this article, you will learn the difference between application and user settings, how to create new settings at design time, how to access settings at run time, and even how to incorporate multiple sets of settings into your application.
Application and User Settings
Settings have four properties:
Name: The Name property of settings is the name that is used to access the value of the setting at run time.
Type: The Type of the setting is the .NET Framework type that the setting represents. A setting can be of any type. For example, a setting that holds a user preference of color would be a System.Color type.
Scope: The Scope property represents how a setting can be accessed at run time. There are two possible values for the Scope property: Application and User. These will be discussed more in this section.
Value: The Value property represents the value returned when the setting is accessed. The value will be of the type represented by the Type property.
The crucial distinction between application-scope and user-scope settings is that user-scope settings are read/write at run time, and their values can be changed and saved in code. Application-scope settings are read only at run time. While they can be read, they cannot be written to. Settings with application scope can only be changed at design time, or by altering the settings file manually
Creating a New Setting at Design Time
You can create a new setting at design time by using the Settings designer. The Settings designer is a familiar grid-style interface that allows you to create new settings and specify properties for those settings. You must specify Name, Type, Scope, and Value for each new setting. Once a setting is created, it can be assessed in code using the mechanisms described later in this article.
To Create a New Setting at Design Time
1. In Solution Explorer, expand the Properties node of your project.
2. In Solution Explorer, double-click the .settings file in which you want to add a new setting. The default name for this file is Settings.settings.
3. In the Settings designer, set the Name, Type, Scope, and Value for your setting. Each row represents a single setting. Figure 1 shows an example of the Settings designer.
To Change the Value of a Setting Between Application Sessions
Using Microsoft Notepad or some other text or XML editor, open the.exe.config file associated with your application.
Locate the entry for the setting you want to change. It should look similar to the following example:
This is the setting value
Type a new value for your setting and save the file.
In C# use settings by accessing the Properties namespace. In the course of this article, you will learn the difference between application and user settings, how to create new settings at design time, how to access settings at run time, and even how to incorporate multiple sets of settings into your application.
Application and User Settings
Settings have four properties:
Name: The Name property of settings is the name that is used to access the value of the setting at run time.
Type: The Type of the setting is the .NET Framework type that the setting represents. A setting can be of any type. For example, a setting that holds a user preference of color would be a System.Color type.
Scope: The Scope property represents how a setting can be accessed at run time. There are two possible values for the Scope property: Application and User. These will be discussed more in this section.
Value: The Value property represents the value returned when the setting is accessed. The value will be of the type represented by the Type property.
The crucial distinction between application-scope and user-scope settings is that user-scope settings are read/write at run time, and their values can be changed and saved in code. Application-scope settings are read only at run time. While they can be read, they cannot be written to. Settings with application scope can only be changed at design time, or by altering the settings file manually
Creating a New Setting at Design Time
You can create a new setting at design time by using the Settings designer. The Settings designer is a familiar grid-style interface that allows you to create new settings and specify properties for those settings. You must specify Name, Type, Scope, and Value for each new setting. Once a setting is created, it can be assessed in code using the mechanisms described later in this article.
To Create a New Setting at Design Time
1. In Solution Explorer, expand the Properties node of your project.
2. In Solution Explorer, double-click the .settings file in which you want to add a new setting. The default name for this file is Settings.settings.
3. In the Settings designer, set the Name, Type, Scope, and Value for your setting. Each row represents a single setting. Figure 1 shows an example of the Settings designer.
To Change the Value of a Setting Between Application Sessions
Using Microsoft Notepad or some other text or XML editor, open the
Locate the entry for the setting you want to change. It should look similar to the following example:
Type a new value for your setting and save the file.
Subscribe to:
Posts (Atom)