MCAD/MCSD Training Guide (70-310): Developing XML Web Services and Server Components with Visual Basic(R) .NET and the .NET Framework

   


From the discussion in the previous sections, you know that it is easy to create a serviced componentall you need to do is to inherit from the ServicedComponent class and apply attributes to use various COM+ component services. However, to use the COM+ component services, the component must be registered into the COM+ catalog. The following steps are typically involved in creating and registering a serviced component.

  1. Create a class that inherits from the ServicedComponent class.

  2. Compile the class and assign a strong name to create a strongly named assembly.

  3. Install the strongly named assembly in the Global Assembly Cache (GAC).

  4. Run the Services Installation Tool ( regsvcs.exe ) to install the assembly into the COM+ catalog.

After you have installed the serviced component in the COM+ catalog, the component can be used by

In the following subsections, you will walk through the complete process of creating and consuming serviced components.

Creating a Serviced Component

In this section, you will learn how to create a simple serviced component. You will also learn how to set various assembly-level attributes to specify the COM+ application name, description, and the activation type.

STEP BY STEP

7.1 Creating a Serviced Component

  1. Launch Visual Studio .NET. Select File, New, Blank Solution, and name the new solution 310C07 . Click OK.

  2. Add a new Visual Basic .NET Class Library named StepByStep7-1 to the solution.

  3. In the Solution Explorer, right-click project StepByStep7-1 and select Add Reference from the context menu. In the .NET tab of the Add Reference dialog box, select the System.EnterpriseServices component from the list view, and click the Select button. Click OK to add the reference.

  4. In the Solution Explorer, rename the default Class1.vb to NorthwindSC.vb .

  5. Open the NorthwindSC.vb and replace the code with the following code:

    Imports System Imports System.Data Imports System.Data.SqlClient Imports System.EnterpriseServices Public Class NorthwindSC Inherits ServicedComponent Private sqlcnn As SqlConnection Private sqlda As SqlDataAdapter Private ds As DataSet Public Sub New() ' Create a connection to the ' Northwind SQL Server database sqlcnn = New SqlConnection(_ "data source=(local);" & _ "initial catalog=Northwind;" & _ "integrated security=SSPI") End Sub ' This method executes a SELECT query and ' returns the results in a DataSet object Public Function ExecuteQuery(_ ByVal strQuery As String) As DataSet ' Create a SqlDataAdapter object to ' talk to the database sqlda = New SqlDataAdapter(_ strQuery, sqlcnn) ' Create a DataSet object ' to hold the results ds = New DataSet() ' Fill the DataSet object sqlda.Fill(ds, "Results") ExecuteQuery = ds End Function ' This method updates the database with ' the changes in a DataSet object Public Function UpdateData(_ ByVal ds As DataSet) As Integer ' Update the database ' and return the result Dim sqlcb As SqlCommandBuilder = _ New SqlCommandBuilder(sqlda) UpdateData = sqlda.Update(_ ds.Tables("Results")) End Function End Class

  6. Open the AssemblyInfo.vb file in the project and add the following Imports directive:

    Imports System.EnterpriseServices

  7. Add the assembly level attributes ApplicationName , Description , and ApplicationActivation to AssemblyInfo.vb as shown here:

    <Assembly: ApplicationName(_ "Northwind Data Application")> <Assembly: Description("Retrieve and Update data " & _ "from the Northwind database")> <Assembly: ApplicationActivation(_ ActivationOption.Library)>

  8. Select Build, Build StepByStep7-1 . This step packages the serviced component into the file StepByStep7-1.dll , which is located in the bin directory of your project. You can navigate to it through the Solution Explorer: Just select the project and click the Show All Files button in the Solution Explorer toolbar.

Step By Step 7.1 defines a class NorthwindSC that is a serviced component because the class derives from ServicedComponent . Using an assembly level attribute, I have also specified that NorthwindSC should be activated in Library mode. The Library activation mode is the default; I have used it anyway to be explicit.

Recall from the previous sections that a serviced component must reside in a class library (DLL file). Therefore, I chose to create a class library project in Step By Step 7.1. A class library project can contain multiple class files. Therefore, Visual Studio .NET uses a separate file named AssemblyInfo.vb to store assembly-level attributes. For a single-class library, you can write the assembly-level attributes directly in the class file if you want.

Creating a Strongly Named Assembly

Every serviced component must be signed with a strong name before it can be registered into the COM+ catalog. A strong name guarantees a unique identity for an assembly by relying on a pair of public and private keys.

Strong names can be easily created using the Strong Name tool (sn.exe) that comes as a part of the .NET Framework SDK and stored in a file. In Step By Step 7.2, you first create a strong name and then modify the program from Step By Step 7.1 to associate the strong name with the assembly StepByStep7-1.dll . StepByStep7-1.dll needs to be regenerated because an already created assembly cannot be signed with a strong name. Signing with a strong name must be a part of the assembly creation process.

STEP BY STEP

7.2 Creating a Strongly Named Assembly

  1. From the Visual Studio .NET program group in the Windows Start menu, launch the Visual Studio .NET command prompt.

  2. Change the directory to the folder where the 310C07 solution resides and issue the following command to create a pair of public/private keys (see Figure 7.6):

    sn k 70310.snk

    Figure 7.6. You can create a public/private key pair by using the Strong Name tool.

    Both the public and private keys are created and stored in a file named 70310.snk .

  3. Open the AssemblyInfo.vb file of the StepByStep7-1 project. Add an AssemblyKeyFile attribute as shown here:

    <Assembly: AssemblyKeyFile("..\..\..310.snk")>

  4. Build the project. A StepByStep7-1.dll is generated, and a strong name is assigned to the file based on the specified key file.

This key file created in Step By Step 7.2 is also called a strong name key file. You will be reusing the key generated in Step By Step 7.2 to sign assemblies in other examples in this book.

WARNING

Protecting Your Identity When you start using a strong name key file to sign your components, the key pair stored in the strong name key files uniquely identifies your components from the components written by other vendors . Anyone who has access to the key pair can potentially sign malicious code on your behalf . To protect their identity, most software development teams use a slightly different process for signing assemblies. This process is called delay signing, a technique that I'll discuss in detail along with strong names in Chapter 10.

Registering the Serviced Component in the COM+ Catalog

A serviced component must be registered in the COM+ catalog to use any of the COM+ component services. A serviced component must be signed with a strong name before it can be registered. The registration process involves retrieving all the necessary runtime information from the class library and copying it to the COM+ catalog. The runtime information is mostly specified in the form of attributes that are applied at the assembly level, class level, and method level in the class library.

Remember that the COM+ catalog came before the managed code; therefore, any managed component must appear as a COM component before it can be registered in the COM+ catalog. The .NET Framework Class Libraries (FCL) provides the EnterpriseServices.RegistrationHelper class to simplify the registration process. The RegistrationHelper class performs the following steps to register a serviced component:

  1. Use the RegistrationServices.RegisterAssembly() method to register the assembly in the registry. For this reason, all classes in the assembly appear as COM components in the registry.

  2. Generate a COM type library (a TLB file) from the assembly using the TypeLibConverter.ConvertAssemblyToTypeLib() method.

  3. Register the type library by using the LoadTypeLibrary() method.

  4. Use the COM+ admin API to configure COM+ based on the information stored in the type library.

Using the Registration helper class requires that you write a program for registering the serviced components. Alternatively, the .NET Framework provides two other methods for registering a component that do not require writing any registration code. Both of these methods however, internally use the RegistrationHelper class:

NOTE

Administrative Access Is Required for Registration Installing a component to the COM+ catalog requires that a user or an application has administrative privileges.

Table 7.6 compares the advantages and disadvantages of the dynamic and manual registration process.

Table 7.6. Advantages and Disadvantages of the Dynamic and Manual Registration

 

Dynamic Registration

Manual Registration

Advantages

  1. Dynamic registration enables applications to register the components on the go. This might be especially helpful in the case of Web applications.

  1. Assemblies placed in the Global Assembly require manual registration.

  2. Any serviced component that is called by COM clients must be registered manually.

Disadvantages

  1. Registration process requires administrative privileges, and sometimes the first use of the component or the Web application might not have those privileges.

  2. Assemblies placed in GAC cannot be dynamically registered.

  3. If there is any error during the registration process, it is revealed only when the first client attempts to access the component.

  1. Requires an additional registration step.

EXAM TIP

Use Manual Registration to Get Feedback on Registration Errors Unlike dynamic registration, the manual registration process that uses the regsvcs.exe tool provides feedback about the errors that were encountered during the registration process. In most cases, you might want to know the errors and correct them before any client actually accesses the serviced component.

Step By Step 7.3 shows how to register a serviced component manually using the .NET Framework Services Installation Tool ( regsvcs.exe ).

STEP BY STEP

7.3 Installing a Serviced Component in the COM+ Catalog

  1. From the Visual Studio .NET program group in the Windows Start menu, launch the Visual Studio .NET command prompt.

  2. Change the directory to the folder where the StepByStep7-1.dll file resides in the StepByStep7-1 projectin this case, the project's bin directory.

  3. Issue the following command to install the service component assembly to the COM+ Catalog (see Figure 7.7):

    regsvcs StepByStep7-1.dll

    Figure 7.7. You can add a serviced component to the COM+ catalog by using regsvcs.exe .

At this stage, the serviced component created in Step By Step 7.1 is installed in the COM+ catalog and can be instantiated by client programs or can be administered through the Component Services administrative tool.

Managing Components Using the Component Services Administrative Tool

When an application is registered into the COM+ catalog, the application can be easily configured by the system administrators using the Component Services administrative tool. In Step By Step 7.4, you will learn how to use this tool to configure COM+ applications.

STEP BY STEP

7.4 Managing the Serviced Component by Using the Component Services Administrative Tool

  1. Open the Component Services administrative tool from the Administrative Tools section of the Windows Control Panel. Using the tree on the left side, navigate to Computers, My Computer, COM+ Applications. You should be able to view the Northwind Data Application, which was added to the COM+ Catalog in Step by Step 7.3, as shown in Figure 7.8.

    Figure 7.8. You can manage a serviced component application using the Component Services administrative tool.

  2. Right-click on the Northwind Data Application and select Properties from the context menu. This opens the COM+ application Properties dialog box, as shown in Figure 7.9.

    Figure 7.9. You can manage the properties of a COM+ application via its Properties dialog box.

    Notice the Application ID that is automatically assigned to the Northwind Data Application. Browse through all the tabs to get an idea of the properties that can be managed using the Components Services administrative tool.

  3. Click the Activation tab to view the activation option. Notice that the Library option is selected. This activation option, the name, and the description of the application were assigned by the attributes added to the AssemblyInfo.vb file in the StepByStep7-1 project.

  4. Now, expand the Northwind Data Application node in the left pane, and select the Components node to view the serviced components in the Northwind Data Application. In this case, you should find StepByStep7_1.NorthwindSC serviced component.

  5. Right-click the StepByStep7_1.NorthwindSC serviced component and select Properties from the context menu. This opens the serviced component's Properties dialog box, as shown in Figure 7.10. Notice the CLSID that is automatically assigned to the StepByStep7_1.NorthwindSC serviced component. Browse through all the tabs to get an idea of the properties that can be managed using the Components Services administrative tool.

    Figure 7.10. Each class within a serviced component has its own dialog box for setting properties.

  6. Expand the StepByStep7_1.NorthwindSC node in the left pane to drill down into the list of interfaces, as shown in Figure 7.11. You note that although you have not defined any interface in the VB .NET program, an interface is added to the component. The name of the interface is the name of the component preceded with an underscore . Expand the methods node under this interface. You won't find any methods listed there. Explore all the interfaces; you do not find any of the two methods that you defined in the VB .NET program for this component. (Refer to Step By Step 7.1.)

    Figure 7.11. A default interface is created for the serviced component, but no methods of the serviced components have been exposed.

In Step By Step 7.4, you see various options to configure a COM+ application and the serviced components. At this time, you might not have a good idea about what each of these options means and how to configure them. Later in this chapter, when I discuss individual COM+ services, I will return to the relevant property pages and explain them in detail.

Configuration can also be performed at the level of methods. However, the methods of a serviced component are not directly accessible to the Component Services administrative tool. I discuss in the next section what you need to do in order to make the methods of a serviced component visible to the Component Services administrative tool as well as to other COM applications.

Creating Interfaces That Are Visible to COM

COM clients and COM+ services communicate with the .NET components using the interfaces provided by the .NET component. The CCW automatically generates these interfaces for a .NET class based on the setting of the ClassInterface and the InterfaceType attributes. Additionally, you can also write an interface explicitly and inherit the .NET class from that interface. These interfaces do not affect how a managed client calls another managed component (or a serviced component), but they surely affect how COM programs (or COM+) interact with the managed components. I discuss both of these attributes in this section.

The ClassInterface Attribute

The ClassInterface attribute specifies how the interfaces will be generated for a class (if they will be generated at all). This attribute can be applied either to a class or to an assembly. If you apply this attribute to an assembly, the attribute applies to all classes in that assembly.

The ClassInterface attribute can be set with any of the three values specified by the ClassInterfaceType enumeration, as shown in Table 7.7.

Table 7.7. Members of the ClassInterfaceType Enumeration

Member Name

Description

AutoDispatch

This is the default setting for the ClassInterface attribute. This setting automatically generates a dispatch-only interface for the class, which means that the class only supports late binding for COM clients. When using this setting, no type information is published to the COM type libraries.

AutoDual

This setting is called AutoDual because it does two things. First, it automatically creates an interface that exposes all the public members of a class (such as methods, properties, fields, and events) and the public methods, properties, and fields of the base class. Second, this setting generates dual interfaces. That means COM clients can use these interfaces for both late and early binding. When using this setting, all the type information is produced for the class interface and published in the type library. This is not a recommended setting and creates versioning problems.

None

This setting does not generate any automatic interfaces. In this case, you need to explicitly write interfaces for your class. This is the recommended setting for ClassInterface attribute.

Step By Step 7.5 shows how to use these attributes to generate different types of interfaces for a serviced component.

STEP BY STEP

7.5 Creating Interfaces That Are Visible to COM

  1. Add a new Visual Basic .NET Class Library project to Solution 310C07. Name the project StepByStep7-5.

  2. In the Solution Explorer, right-click project StepByStep7-5 and select Add Reference from the context menu to add references to the System.EnterpriseServices library.

  3. In the Solution Explorer, copy the NorthwindSC.vb file from the StepByStep7-1 project to the current project. Delete the default Class1.vb .

  4. Add the following Imports directive to NorthwindSC.vb :

    Imports System.Runtime.InteropServices

  5. Apply the following attribute to the NorthwindSC class:

    <ClassInterface(ClassInterfaceType.AutoDual)> _ Public Class NorthwindSC ... End Class

  6. Open the AssemblyInfo.vb file in the project and add the following Imports directive:

    Imports System.EnterpriseServices

  7. Add assembly level attributes ApplicationName , Description , and ApplicationActivation in the AssemblyInfo.vb as shown here:

    <Assembly: ApplicationName(_ "Northwind Data Application with Interfaces")> <Assembly: Description("Retrieve and Update data " & _ "from the Northwind database")> <Assembly: ApplicationActivation(_ ActivationOption.Library)> <Assembly: AssemblyKeyFile("..\..\..310.snk")>

    Note that you are using the key file, 70310.snk , already created in Step by Step 7.2. If you haven't created one before, create it now following step 2 of Step by Step 7.2.

  8. Select Build, Build StepByStep7-5 . This step packages the serviced component into the file StepByStep7-5.dll , which is located in the bin directory of your project.

  9. Register StepByStep7-5.dll into the COM+ catalog using the Services Installation tool as demonstrated in Step By Step 7.3.

  10. Open the Component Services administrative tool (or click on the Refresh toolbar icon if the tool is already open). You should see the icon of Northwind Data Application with Interfaces. Double-click on the icon and use the tree view on the left to drill down to the interfaces of the NorthwindSC component of this application. Expand the _NorthwindSC interface to see its methods. Select the Methods node in the left pane and then select View, Property View from the menu. You see a list of methods, as shown in Figure 7.12.

    Figure 7.12. The ClassInterfaceType.AutoDual setting automatically generates a class interface for a class.

  11. Right-click the ExecuteQuery() method and select properties from the shortcut menu. The property sheet, as shown in Figure 7.13, allows you to configure individual methods.

    Figure 7.13. A method can be configured using its Properties Window.

  12. Switch back to Visual Studio .NET and open the NorthwindSC.vb file. Insert the following interface just before the class definition:

    Public Interface INorthwind Function ExecuteQuery(ByVal strQuery As String) _ As DataSet Function UpdateData(ByVal ds As DataSet) As Integer End Interface

  13. Modify the definition NorthwindSC class as shown here:

    <ClassInterface(ClassInterfaceType.None)> _ Public Class NorthwindSC Inherits ServicedComponent Implements INorthwind Private sqlcnn As SqlConnection Private sqlda As SqlDataAdapter Private ds As DataSet Public Sub New() ' Create a connection to the ' Northwind SQL Server database sqlcnn = New SqlConnection(_ "data source=(local);" & _ "initial catalog=Northwind;" & _ "integrated security=SSPI") End Sub ' This method executes a SELECT query and ' returns the results in a DataSet object Public Function ExecuteQuery(_ ByVal strQuery As String) As DataSet _ Implements INorthwind.ExecuteQuery ' Create a SqlDataAdapter object to ' talk to the database sqlda = New SqlDataAdapter(_ strQuery, sqlcnn) ' Create a DataSet object ' to hold the results ds = New DataSet() ' Fill the DataSet object sqlda.Fill(ds, "Results") ExecuteQuery = ds End Function ' This method updates the database with ' the changes in a DataSet object Public Function UpdateData(_ ByVal ds As DataSet) As Integer _ Implements INorthwind.UpdateData ' Update the database ' and return the result Dim sqlcb As SqlCommandBuilder = _ New SqlCommandBuilder(sqlda) UpdateData = sqlda.Update(_ ds.Tables("Results")) End Function End Class

  14. Select Build, Build StepByStep7-5 . Unregister and then register StepByStep7-5.dll into the COM+ catalog using the Services Installation tool.

  15. Open the Component Services administrative tool. Navigate to the NorthwindSC component of the Northwind Data Application with Interfaces. You'll see the NorthwindSC component. Expand the component to see its interfaces and methods. You'll see that the new INorthwind interface has replaced the default _NorthwindSC interface. The new interface contains only the methods of the serviced component, not those of its base classes. (see Figure 7.14).

    Figure 7.14. When ClassInterfaceType is set to None, you only get the interfaces, which you explicitly implement.

In Step By Step 7.5, I demonstrated how to use the ClassInterface attribute with ClassInterfaceType set to AutoDual or None. You already know how the AutoDispatch option works because that's the default. If you don't apply the ClassInterface attribute at all, the ClassInterface attribute is, in fact, implicitly applied with the ClassInterfaceType set to AutoDispatch.

From Step By Step 7.4 and Step By Step 7.5, you can also see that with both the AutoDispatch and AutoDual settings, an interface is automatically generated. The name of the automatic interface is the name of the class preceded by an underscore. For example, the class NorthwindSC has the interface _NorthwindSC. However, when you set the ClassInterfaceType to None, no automatic interfaces are generated. In that case, you only get information about interfaces and methods written to the COM+ catalog if you explicitly define and then implement an interface.

Versioning Problems and the ClassInterface Attribute

You can see from Step By Step 7.5 that the AutoDual setting of the ClassInterface exports the type information and DispId of the class and base class members to the COM type library. This allows COM clients to bind their programs with the DispId of the members at compile time.

The DispId s are generated based on the position of the member in the class. This can cause versioning problems because if in the next version of your component, you change the order of the members and export the class to a COM type library, the DispId of the members will be changed. This will cause already compiled COM programs to fail. Because of this versioning issue, even if the AutoDual setting of the class interface looks flexible, it is not a recommended option.

The AutoDispatch setting, on the other hand, does not export the type information and DispId . Therefore, clients cannot bind to a particular DispId at compile time, and no problems occur when the order of methods are changed in the next version. However, because of its support for late binding only, AutoDispatch is normally limited to scripted or interpreted execution environments.

The last option, the None setting, prevents any automatic interface generation in the COM type library. If you want any interfaces to be written to the COM type library, you must explicitly define them. In this case, you get the benefit of both early and late binding.

However, writing your own interface separates the "view" of a class from its implementation. Even in the future, if you decide to change the order of the methods in your implementation, you are fine unless you modify the interface definition.

To summarize, the best practice is to mark the class with the ClassInterface attribute set to the ClassInterfaceType.None value and create your own interface explicitly. I will use this technique in the rest of the examples in this chapter.

The InterfaceType Attribute

The InterfaceType attribute can be applied to the interfaces that you declare. You can use this attribute to configure how these interfaces are exposed to the COM clients. The InterfaceType attribute can be set with any of the three values specified by the ComInterfaceType enumeration, as shown in Table 7.8.

Table 7.8. Members of the ComInterfaceType Enumeration

Enumerators

Description

InterfaceIsDual

This is the default setting for an interface. This value specifies that the interface should be exposed to COM as a dual interface. This allows COM clients to use either early or late binding.

InterfaceIsIDispatch

This value specifies that the interface should be exposed to COM as a dispatch-only interface; that is, one supporting only late binding.

InterfaceIsIUnknown

This value specifies that the interface should be exposed to COM as an IUnknown derived interface.

In most cases, you should leave the default value of the InterfaceType attribute unchanged.

NOTE

Configuring COM Visibility By default, all public elements in your programs are visible to COM. You might want to hide certain elements from COM. You can mark those elements with the ComVisible attribute with its Value property set to False. These hidden members are not included in the COM type library, and the CCW rejects all requests to hidden elements from a COM client.

The ComVisible attribute can be set up in a hierarchy. That is, if you apply this attribute to an assembly and set its value to False, all the classes and members in the assembly are hidden from COM. However, if any class in the assembly overrides the ComVisible attribute by setting its value to True, that class will be visible to COM clients even though its parent is not.

Component Identification

COM+ uniquely identifies each COM+ application and its component using GUIDs. If you don't use GUIDs to identify the COM+ application and components, the assembly registration process will assign them automatically.

Each assembly is registered as a separate COM+ application; you can use the assembly level ApplicationID attribute to specify a GUID for the COM+ application. However, in most cases you might not want to specify a fixed ApplicationID because doing so prevents COM+ partitioning.

NOTE

COM+ Partitions A COM+ partition is a logical container that allows multiple versions of COM+ applications to exist on a single computer. By default, all applications are installed in a partition named the Global Partition. However, administrators can define additional partitions to install the same or different versions of the application. Applications installed in different partitions can be configured and managed independently of each other. For more information on COM+ partitions, refer to the COM+ Platform SDK documentation in the MSDN Library (msdn.microsoft.com/library).

Each component in an assembly can be uniquely identified by applying the Guid attribute as in the following code:

<Guid("0F1FD944-ADDC-4d34-A4D0-90F9AA707930")> Public Class NorthwindSC Inherits ServicedComponent Implements INorthwind ...

You should pass a GUID to the constructor of the Guid attribute. A GUID can be generated using the command line GUID generation tool ( guidgen .exe ) or through the Tools, Create GUID menu option in Visual Studio .NET.

In Step By Step 7.5, you did not hard-code any GUID for the components; as a result, a GUID was automatically generated for the component by the registration process each time a different version of the component was registered into the COM+ catalog.

When you are developing, you might need to register the component several times in the COM+ catalog. In that case unless you hard-code a GUID for the component, you end up registering multiple versions of the same componenteach with a different GUIDin the COM+ catalog.

Now when you use the Component Services administrative tool, you see multiple instances of a component in the COM+ application. It's hard to determine which component you need to configure. In this case, a good idea is to hard-code each component with a GUID instead of depending on the automatically generated GUID.

Installing the Component in the Global Assembly Cache

Before client programs can invoke methods on a serviced component, the serviced component must be installed at a location where the client programs can reliably locate it. Serviced components are usually shared by several applications on a computer; therefore, a common practice is to install a serviced component in the Global Assembly Cache (GAC). The GAC is a machine-wide central repository for storing shared components.

NOTE

Benefits of Installing a Component in the GAC Apart from acting as a central repository for storing shared components, the GAC provides several other benefits such as side-by-side versioning and file security. You will learn more about the GAC and its features in Chapter 10.

Installing the assembly in the GAC is not a requirement for using a serviced component. In fact, an application that uses the serviced component will work correctly as long as the CLR can load the corresponding assembly.

Installing the serviced component assembly in the GAC is the recommended option when you deploy an application because it ensures that the CLR is always able to locate an assembly. To learn more about the GAC and to learn how the runtime locates assemblies, refer to Chapter 10.

EXAM TIP

Strong Name Required for Installing a Component to the GAC An assembly must be signed with strong name before it can be installed in the GAC.

In Step By Step 7.6, you'll learn how to use the Global Assembly Cache tool ( gacutil.exe ) to install an assembly to the GAC and how to view the contents of the GAC.

NOTE

Installing a Serviced Components for Use by Web Forms ASP.NET uses a technique called shadow copy to eliminate the need to stop the running Web application whenever any components of that application need to be updated or replaced. To participate in the shadow copy process, a component must be deployed in the bin directory of the virtual root of a Web application instead of in the GAC.

STEP BY STEP

7.6 Installing a Serviced Component in the GAC by Using the Global Assembly Cache Tool ( gacutil.exe )

  1. From the Visual Studio .NET program group in the Windows Start menu, launch the Visual Studio .NET command prompt.

  2. Change the directory to the folder where the StepByStep7-5.dll file resides in the StepByStep7-5 projectin this case, the project's bin directory.

  3. Issue the following command to install the assembly to the GAC (see Figure 7.15):

    gacutil /i StepByStep7-5.dll

    Figure 7.15. You can add an assembly to the GAC by using the gacutil.exe .

  4. Open Windows Explorer. Navigate to the assembly cache folder, which is the assembly folder in your windows installation folder such as c:\WINNT\assembly or C:\Windows\assembly . Verify that the serviced component is added to the GAC as shown in Figure 7.16.

    Figure 7.16. You can view the assemblies in the GAC by using Windows Explorer.

Step By Step 7.6 uses the Global Assembly Cache tool to deploy an assembly to the GAC. This tool comes as part of the .NET Framework SDK.

Internally, the GAC is maintained using a bunch of directories nested within each other. To be able to view the contents of the GAC in a much more readable way, the .NET Framework installs the Assembly Cache Viewer ( shfusion.dll ) shell extension that integrates with the Windows shell and allows you to view contents of the GAC just like viewing files in a folder.

Component Versioning

All components in an assembly share the same version. The version of an assembly can be specified using the assembly level AssemblyVersion attribute. When working with Visual Studio .NET, each time you modify and recompile a program, the version of the assembly is changed. This happens because the AssemblyVersion attribute is set to 1.0.* , which means that the major and minor version of the assembly are fixed to 1 and 0, respectively, but * in the third partthe build numberspecifies a number that changes automatically when the assembly is rebuilt.

A common destination for installing the serviced component is the GAC. The GAC is capable of installing different versions of an assembly side by side. While in development, if you install several builds of a component in the GAC, you can easily clutter up the GAC with several versions of the component when you are only interested in the most recent one.

For development, it might be good idea to fix the version number specified in the AssemblyVersion attribute so that the GAC always contains only the most recent version of the components. However, in a production environment, there might be cases in which you need to maintain different versions of a serviced component in the GAC.

Consuming a Serviced Component

Instantiating and using a serviced component is no different from any other managed component. In Step By Step 7.7, I create a Windows form application that instantiates the serviced component and calls methods on it. This application allows users to retrieve and update data from the SQL Server sample Northwind database.

STEP BY STEP

7.7 Using a Serviced Component

  1. Add a new Visual Basic .NET Windows application named StepByStep7-7 to the solution.

  2. In the Solution Explorer, right-click project StepByStep7-7 and select Add Reference from the context menu to add references to the System.EnterpriseServices and StepByStep7-5 components.

  3. In the Solution Explorer, rename the default Form1.vb to NorthwindSCClient.vb . Open the form in code view and change all occurrences of Form1 to refer to NorthwindSCClient instead.

  4. Add the following Imports directives:

    Imports System.Data.SqlClient Imports StepByStep7_5

  5. Place two Label controls, a TextBox control ( txtQuery ), two Button controls ( btnExecute and btnUpdate ), and a DataGrid control ( dgResults ) on the form. Arrange the controls as shown in Figure 7.17. Set the Multiline property of the TextBox control to True .

    Figure 7.17. The design of a form that uses the NorthwindSC serviced component.

  6. Add the following code in the class definition:

    ' Declare the serviced component Private nsc As NorthwindSC

  7. Double-click the form and add the following code in the Load event handler:

    Private Sub NorthwindSCClient_Load(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Instantiate the serviced component nsc = New NorthwindSC() End Sub

  8. Add the following code to the Click event handlers for the Button controls:

    Private Sub btnExecute_Click(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Call the ExecuteQuery() method of the ' NorthwindSC serviced component to execute ' query and bind the results to the data grid dgResults.DataSource = _ nsc.ExecuteQuery(txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, "Invalid Query", _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub Private Sub btnUpdate_Click(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUpdate.Click Try ' Call the UpdateData() method of the ' NorthwindSC serviced component to update ' the database and display the number ' of updated rows in the database Dim intRows As Integer = nsc.UpdateData(_ CType(dgResults.DataSource, DataSet)) MessageBox.Show(String.Format(_ "{0} row(s) updated successfully", intRows), _ "Row(s) Updated", MessageBoxButtons.OK, _ MessageBoxIcon.Information) ' Load the updates and bind the grid ' with the updates dgResults.DataSource = _ nsc.ExecuteQuery(txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, "Update Failed", _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub

  9. Set the form as the startup form for the project. Build the project. Set the project StepByStep7-7 as the startup project.

  10. Run the solution. Enter a query in the text box and click the button. The code invokes a method on the serviced component. The code binds the results from the method to the DataGrid control. Make some changes to the data in the DataGrid control and click the Update button to save the changes to the database. If there is no error in the updates, a message box with the number of rows updated is displayed as shown in Figure 7.18.

    Figure 7.18. NorthwindSCClient uses the NorthwindSC serviced component to retrieve and update data from the Northwind database.

Although Step By Step 7.7 uses a Windows application to invoke a serviced component, you have other options too. For example, a distributed application can use a remoting server or a Web service to consume a serviced component.

REVIEW BREAK

  • Administrators can use the Component Services administrative tool to configure COM+ applications at runtime. Using this tool, they can change certain behavior of a COM+ application without needing to recompile the application.

  • If configuration at the method level is required, a class must implement an interface with those methods.

  • Depending on the ClassInterface attribute, to automatically generate class interfaces is bad because it leads to versioning problems. You should set the ClassInterface attribute for a class to the ClassInterfaceType.None value and create your own interface explicitly.

  • An assembly must be signed with a strong name before the assembly can be registered into the COM+ catalog.

  • A serviced component is generally shared by several client applications. Therefore, the most recommended place to install a serviced component assembly is the Global Assembly Cache (GAC) because it ensures that the CLR is always capable of locating an assembly.

  • A distributed application may use different types of applications such as a Windows application, Windows service, Web application, Web service, Remoting server, and so on to consume a serviced component.

GUIDED PRACTICE EXERCISE 7.1

You have just learned the basics of developing serviced components. You want to create a sample application that you will use to experiment with various COM+ services. You want to start with a serviced component similar to the one created in Step By Step 7.5.

You want to develop the application in a gradual manner. Each time you add a new feature, you want to test whether the feature is working and then proceed with adding a new feature. You are only interested in using the most recent version of a component. You do not want multiple copies of a component registered with the COM+ catalog and installed in the GAC.

How would you create such a serviced component?

This exercise helps you practice creating serviced components and controlling their versioning and identification.

You should try working through this problem on your own first. If you are stuck, or if you'd like to see one possible solution, follow these steps:

  1. Add a new Visual Basic .NET Class library named GuidedPracticeExercise7-1 to the solution.

  2. In the Solution Explorer, right-click the project GuidedPracticeExercise7-1 and select Add Reference from the context menu to add a reference to the System.EnterpriseServices component.

  3. In the Solution Explorer, copy the NorthwindSC.vb file from the StepByStep7-5 project to the current project. Delete the default Class1.vb .

  4. Select Tools, Create GUID. In the Create GUID dialog box, select the Registry Format as the GUID Format as shown in Figure 7.19 and then click the Copy button. Click Exit to close the Create GUID dialog box.

    Figure 7.19. The Create GUID dialog box allows you to create Globally Unique Identifiers.

  5. Open NorthwindSC.vb and apply the Guid attribute to the NorthwindSC class as shown in the following code segment. Instead of the GUID shown in the following code segment, paste the value that you copied in step 4. Remove any curly braces from the GUID value:

    <ClassInterface(ClassInterfaceType.None), _ Guid("C379E2A0-950C-4c43-A667-0B5901FA6B0A")> _ Public Class NorthwindSC

  6. Open the AssemblyInfo.vb file in the project and add the following Imports directive:

    Imports System.EnterpriseServices

  7. Add the following Assembly level attributes to the AssemblyInfo.vb file:

    <Assembly: ApplicationName(_ "Northwind Data Application with Fixed Version" & _ " and component identification")> <Assembly: Description("Retrieve and Update data" & _ " from the Northwind database")> <Assembly: AssemblyVersion("1.0.0")> <Assembly: AssemblyKeyFile("..\..\..310.snk")>

    Note that you are using the key file, 70310.snk , already created in Step by Step 7.2. If you haven't created one before, create it now following step 2 from Step by Step 7.2.

  8. Build the project. GuidedPracticeExercise7-1.dll is generated, and a strong name is assigned to the file based on the specified key file.

The serviced component with a fixed version and component identification information is now ready. When you rebuild and reregister this assembly in the COM+ catalog, you will still have just one entry for the component. Similarly, when you install multiple builds of this assembly to the GAC, only the most recent copy is present in the GAC at all times.

If you have difficulty following this exercise, review the sections "Creating a Serviced Component," "Component Identification," and "Component Versioning" earlier in this chapter. After doing that review, try this exercise again.


   
Top

Категории