The .NET Framework is a development framework that provides a new programming interface to Windows services and APIs, and integrates a number of technologies that emerged from Microsoft during the late 1990s. Microsoft announced the .NET initiative in July 2000. In April 2003, Version 1.1 of the integral .NET Framework was released. This book describes this updated version of the .NET Framework.
The .NET platform consists of four separate product groups:
A set of languages, including C#, J#, and VB.NET; a set of development tools, including Visual Studio .NET; a comprehensive class library for building web services and web and Windows applications; as well as the Common Language Runtime (CLR). These components collectively form the largest part of the .NET Framework.
An offering of commercial web services, specifically the .NET Services initiative; for a fee, developers can use these services in building applications that require them.
A set of .NET-enabled enterprise servers, including SQL Server, Exchange Server, BizTalk Server, and so on. These provide specialized functionality for relational data storage, email, and B2B commerce. Future versions of these products will increasingly support the .NET Framework.
New .NET-enabled, non-PC devices, from cell phones to game boxes.
While the main strategy of .NET is to enable software as a service, .NET is much more than that. In addition to embracing the Web, Microsoft .NET acknowledges and responds to the following trends within the software industry:
Simplifies the development of robust client/server and multi-tier (n-tier) applications. Traditional distributed technologies require high vendor-affinity and are unable to interoperate with the Web. Microsoft .NET provides remoting and web services architectures that exploit open Internet standards, including the Hypertext Transfer Protocol (HTTP), Extensible Markup Language (XML), and Simple Object Access Protocol (SOAP) and WSOL.
Simplifies the integration of software components developed by different vendors and supports development of distributed applications. The Component Object Model (COM) has brought reality to software plug-and-play, but COM component development and deployment are too complex. Microsoft .NET provides a simpler way to build and deploy components.
Allow the development of scalable enterprise applications without writing code to manage transactions, security, or pooling. Microsoft .NET continues to support COM and component services, since these services greatly reduce the development time and effort required to build large-scale applications.
Over the past decade, web application development has shifted from connectivity (TCP/IP), to presentation (HTML), to programmability (XML and SOAP). A key goal of Microsoft .NET is to enable the sharing of functionality across the Web among different platforms, devices, and programming languages.
Lessons that the software industry has learned from developing large-scale enterprise and web applications. A commercial web application must support interoperability, scalability, availability, security, and manageability. Microsoft .NET facilitates all these goals.
Although these are the sources of many ideas embodied by Microsoft .NET, what’s most notable about the platform is its use of open Internet standards (HTTP, XML, and SOAP) at its core to transmit information from one machine to another across the Internet. In fact, .NET provides bidirectional mapping between XML and objects. For example, a class can be expressed as an XML Schema Definition (XSD); an object can be converted to and from an XML buffer; a method can be specified using an XML format called Web Services Description Language (WSDL); and a method call can be expressed using an XML format called SOAP.
The Microsoft .NET platform consists of five main components, as shown in Figure 1-1. At the lowest layer lies the operating system (OS), which can be one of a variety of Windows platforms, including Windows XP, Windows 2000, Windows Server 2003, Windows ME, and Windows CE. As part of the .NET strategy, Microsoft has promised to deliver more .NET device software to facilitate a new generation of smart devices.
On top of the operating system is a collection of specialized server products that shortens the time required to develop large-scale business systems. These server products include Application Center, BizTalk Server, Commerce Server, Exchange Server, Host Integration Server, Internet Security and Acceleration Server, and SQL Server.
Since web services are highly reusable across the Web, Microsoft provides a number of building-block services (officially called .NET Services) that applications developers can use, for a fee. Two examples of .NET Services that Microsoft offers include .NET Passport and .NET Alerts. .NET Passport allows you to use a single username and password at all web sites that support Passport authentication. .NET Alerts allow .NET Alert providers, such as a business, to alert their consumers with important or up-to-the-minute information. Microsoft plans to add newer services, such as calendar, directory, and search services. Third-party vendors are also creating new web services of their own.
The top layer of the .NET architecture is a development tool called Visual Studio .NET (VS.NET), which makes possible the rapid development of web services and other applications. A successor to Microsoft Visual Studio 6.0, VS.NET is an Integrated Development Environment (IDE) that supports four different languages and features such as cross-language debugging and the XML Schema Editor.
And at the center of .NET is the Microsoft .NET Framework—the main focus of this book. The .NET Framework is a development and runtime infrastructure that changes the development of business applications on the Windows platform. The .NET Framework includes the CLR and a common framework of classes that can be used by all .NET languages.
The Microsoft .NET Framework embodies design goals that are both practical and ambitious. In this section, we discuss the main design goals of the Microsoft .NET Framework, including support for components, language integration, application interoperation across the Web, simplified development and deployment, improved reliability, and greater security.
Prior to the introduction of COM technology, developers had no standard way to integrate binary libraries without referring to or altering their source code. With the advent of COM, programmers were able to integrate binary components into their applications, similar to the way we can plug-and-play hardware components into our desktop PCs. Although COM was great, the grungy details of COM gave developers and administrators many headaches.
Although COM permits you to integrate binary components developed using any language, it does require you to obey the COM identity, lifetime, and binary layout rules. You must also write the plumbing code that is required to create a COM component, such as DllGetClassObject, CoRegisterClassObject, and others.
Realizing that these requirements result in frequent rewrites of similar code, .NET sets out to remove them. In the .NET world, all classes are ready to be reused at the binary level. You don’t have to write extra plumbing code to support componentization in the .NET Framework. You simply write a .NET class, which then becomes a part of an assembly (to be discussed in Chapter 2) that inherently supports plug-and-play.
In addition to providing a framework to make development easier, .NET removes the pain of developing COM components. Specifically, .NET removes the use of the registry for component registration and eliminates the requirements for extraneous plumbing code found in all COM components, including code to support IUnknown, class factories, component lifetime, registration, dynamic binding, and others.
COM supports language independence, which means that you can develop a COM component in any language you want. As long as your component meets all the rules spelled out in the COM specification, it can be instantiated and used by your applications. Although this supports binary reuse, it doesn’t support language integration. In other words, you can’t reuse the code in the COM components written by someone else; you can’t extend a class hosted in the COM component; you can’t catch exceptions thrown by code in the COM component; and so forth.
Microsoft .NET supports not only language independence, but also language integration. This means that you can inherit from classes, catch exceptions, and take advantage of polymorphism across different languages. The .NET Framework makes this possible with a specification called the Common Type System (CTS), which all .NET components must support. For example, everything in .NET is an object of a specific class that derives from the root class called System.Object. The CTS supports the general concepts of classes, interfaces, delegates (which support callbacks), reference types, and value types. The .NET base classes provide most of the base system types, such as those that support integer, string, and file manipulation. Because every language compiler must meet a minimum set of rules stipulated by the Common Language Specification (CLS) and generate code to conform to the CTS, different .NET languages can be used in the same application. We will examine the CTS and CLS in Chapter 2.
COM supports distributed computing through its Distributed COM (DCOM) wire protocol. A problem with DCOM is that it embeds the host TCP/IP address inside the Network Data Representation (NDR) buffer, such that it will not work through firewalls and Network Address Translation (NAT) software. In addition, the DCOM dynamic activation, protocol negotiation, and garbage collection facilities are proprietary, complex, and expensive. The solution is an open, simple, and lightweight protocol for distributed computing. The .NET Framework uses the industry-supported SOAP protocol, which is based on the widely accepted XML standards.
If you have developed software for the Windows platforms since their inception, you have seen everything from the Windows APIs to the Microsoft Foundation Classes (MFC), the Active Template Library (ATL), the system COM interfaces, and countless other environments, such as Visual Interdev, Visual Basic, JScript, and other scripting languages. Each time you set out to develop something in a different compiler, you had to learn a new API or a class library, because there is no consistency or commonality among these different libraries or interfaces.
.NET solves this problem by providing a set of framework classes that every language uses. Such a framework removes the need for learning a new API each time you switch languages.
Imagine this scenario: your Windows application, which uses three shared Dynamic Link Libraries (DLLs), works just fine for months, but stops working one day after you’ve installed another software package that overwrites the first DLL, does nothing to the second DLL, and adds an additional copy of the third DLL into a different directory. If you have ever encountered such a brutal—yet entirely possible—problem, you have entered DLL Hell. And if you ask a group of seasoned developers whether they have experienced DLL Hell, they will grimace at you in disgust, not because of the question you’ve posed, but because they have indeed experienced the pain and suffering.
To avoid DLL Hell on Windows 2000 and subsequent Windows operating systems (at least for system DLLs), Windows 2000 stores system DLLs in a cache. If you install an application that overwrites system DLLs, Windows 2000 will overwrite the added system DLLs with the original versions from the cache.
Microsoft .NET further diminishes DLL Hell. In the .NET environment, your executable will use the shared DLL with which it was built. This is guaranteed, because a shared DLL must be registered against something similar to the Windows 2000 cache, called the Global Assembly Cache (GAC). In addition to this requirement, a shared DLL must have a unique hash value, public key, locale, and version number. Once you’ve met these requirements and registered your shared DLL in the GAC, its physical filename is no longer important. In other words, if you have two versions of a DLL that are both called MyDll.dll, both of them can live and execute on the same system without causing DLL Hell. This is possible because the executable that uses one of these DLLs is tightly bound to a specific version of the DLL during compilation.
In addition to eradicating DLL Hell, .NET also removes the need for component-related registry settings. A COM developer will tell you that half the challenge of learning COM is understanding the COM-specific registry entries for which the developer is responsible. Microsoft .NET stores all references and dependencies of .NET assemblies within a special section called a manifest (see Chapter 2). In addition, assemblies can be either private or shared. Private assemblies are found using logical paths or XML-based application configuration files, and public assemblies are registered in the GAC; in both cases, the system will find your dependencies at runtime. If they are missing, you get an exception telling you exactly what happened.
Finally, .NET brings back the concept of zero-impact installation and removal. This concept is the opposite of what you have to deal with in the world of COM. To set up a COM application, you have to register all your components after you have copied them over to your machine. If you fail to perform this step correctly, nothing will work and you’ll end up pulling your hair out. Likewise, to uninstall the application, you should unregister your components (to remove the registry entries) prior to deleting your files. Again, if you fail to perform this step correctly, you will leave remnants in the registry that will be forever extant.
Unlike COM, but like DOS, to set up an application in .NET, you
xcopy your files from one directory on a CD
to another directory on your machine, and the application will run
automatically. Similarly, you can just delete the
directory to uninstall the application from your machine.
There are many programming languages and platforms in the commercial software industry, but few of them attempt to provide both a reliable language and a robust runtime or infrastructure. The most successful language that we have seen in the commercial software industry is the Java™ language and the Java Virtual Machine™, which have brought the software-development community much satisfaction. Microsoft is positioning .NET as the next big thing.
Microsoft .NET requires type safety. Unlike C++, every class in .NET is derived from the mother of all classes, Object, which supports basic features such as returning a string representation of the object, indicating whether the object is equal to another, and so on. The CLR must recognize and verify types before they can be loaded and executed. This decreases the chances for rudimentary programming errors and prevents buffer overruns, which can be a security weakness.
Traditional programming languages don’t provide a common error-handling mechanism. C++ and Java support exception handling, but many others leave you in the dust, forcing to invent your own error-handling facilities. Microsoft .NET supports exceptions in the CLR, providing a consistent error-handling mechanism. Put another way: exceptions work across all .NET-compatible languages.
When you program in C++, you must deallocate all heap-based objects that you have previously allocated. If you fail to do this, the allocated resources on your system will never be reclaimed even though they are no longer needed. And if this is a server application, it won’t be robust because the accumulation of unused resources in memory will eventually bring down the system. Similar to Java, the .NET runtime tracks and garbage-collects all allocated objects that are no longer needed.
When developing applications in the old days of DOS, Microsoft developers cared little about security because their applications ran on a single desktop with a single thread of execution. As soon as developers started developing client and server applications, things got a bit complicated: multiple users might then have accessed the servers, and sensitive data might be exchanged between the client and the server. The problem became even more complex in the web environment, since you could unknowingly download and execute malicious applets on your machine.
To mitigate these problems, .NET provides a number of security features. Windows NT and Windows 2000 protect resources using access-control lists and security identities, but don’t provide a security infrastructure to verify access to parts of an executable’s code. Unlike traditional security support in which only access to the executable is protected, .NET goes further to protect access to specific parts of the executable code—this is known as code access security. For example, to take advantage of declarative security checks, you can prefix your method implementations with security attributes without having to write any code. To take advantage of imperative security checks, you write the code in your method to explicitly cause a security check. .NET provides other security features to make it harder to penetrate your applications and system.
Now that you are familiar with the major goals of the .NET Framework, let’s briefly examine its architecture. As you can see in Figure 1-2, the .NET Framework sits on top of the operating system, which can be a few different flavors of Windows and consists of a number of components (each of these components is discussed in greater detail starting with Chapter 4, as described in the Preface). .NET is essentially a system application that runs on Windows.
The most important component of the framework is the CLR. If you are a Java programmer, think of the CLR as the .NET equivalent of the Java Virtual Machine (JVM). If you don’t know Java, think of the CLR as the heart and soul of the .NET architecture. At a high level, the CLR activates objects, performs security checks on them, lays them out in memory, executes them, and garbage-collects them.
Conceptually, the CLR and the JVM are similar in that they are both runtime infrastructures that abstract the underlying platform differences. However, while the JVM officially supports only the Java language, the CLR supports any language that can be represented in its Common Intermediate Language (CIL). The JVM executes bytecode, so it can, in principle, support many languages, too. Unlike Java’s bytecode, though, CIL is never interpreted. Another conceptual difference between the two infrastructures is that Java code runs on any platform with a JVM, whereas .NET code runs only on platforms that support the CLR. In April, 2003, the International Organization for Standardization and the International Electrotechnical Committee (ISO/IEC) recognized a functional subset of the CLR, known as the Common Language Interface (CLI), as an international standard. This development, initiated by Microsoft and developed by ECMA International, a European standards organization, opens the way for third parties to implement their own versions of the CLR on other platforms, such as Linux or Mac OS X. For information on third-party and open source projects working to implement the ISO/IEC CLI and C# specifications, see Appendix A.
In Figure 1-2, the layer on top of the CLR is a set of framework base classes. This set of classes is similar to the set of classes found in STL, MFC, ATL, or Java. These classes support rudimentary input and output functionality, string manipulation, security management, network communications, thread management, text management, reflection functionality, collections functionality, as well as other functions.
On top of the framework base classes is a set of classes that extend the base classes to support data management and XML manipulation. These classes, called ADO.NET, support persistent data management—data that is stored on backend databases. Alongside the data classes, the .NET Framework supports a number of classes to let you manipulate XML data and perform XML searching and XML translations.
Classes in three different technologies (including web services, Web Forms, and Windows Forms) extend the framework base classes and the data and XML classes. Web services include a number of classes that support the development of lightweight distributed components, which work even in the face of firewalls and NAT software. These components support plug-and-play across the Internet, because web services employ standard HTTP and SOAP.
Web Forms, the key technology behind ASP.NET, include a number of classes that allow you to rapidly develop web Graphical User Interface (GUI) applications. If you’re currently developing web applications with Visual Interdev, you can think of Web Forms as a facility that allows you to develop web GUIs using the same drag-and-drop approach as if you were developing the GUIs in Visual Basic. Simply drag-and-drop controls onto your Web Form, double-click on a control, and write the code to respond to the associated event.
Windows Forms support a set of classes that allow you to develop native Windows GUI applications. You can think of these classes collectively as a much better version of the MFC in C++ because they support easier and more powerful GUI development and provide a common, consistent interface that can be used in all languages.
In the next chapter, we examine the internals of the CLR and how it supports and executes .NET components, formally called assemblies in .NET.