Creating and Consuming a Serviced Component



Creating and Consuming a Serviced Component

From the discussion in the previous sections, you know that it is easy to create a serviced component—all 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 list summarizes the typical steps involved in creating and registering a serviced component.

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

  2. Compile the class by assigning a strong name to create a strongly named assembly.

  3. Run the .NET Services Installation tool (regsvcs.exe) to install the assembly into the COM+ Catalog.

NOTE

Install Serviced Components to the Global Assembly Cache When a serviced component is shared between multiple client applications, you should also install the serviced component assembly in the Global Assembly Cache (GAC). Registration in GAC ensures that the client applications are able to locate the serviced components. You'll read more about this topic later in the section titled "Installing the Component in the Global Assembly Cache."


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

  • Application Programs— To create instances of the serviced components and execute methods on them.

  • System Administrators— To use the Component Services administrative tool to configure various attributes of the serviced component.

In the following sub-sections, 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 activation type, as shown in Step-by-Step 7.1.

STEP BY STEP

7.1 Creating a Serviced Component

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

  2. Add a new Visual C# .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.cs to NorthwindSC.cs.

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

    
    using System;
    
    
    
    using System.Data;
    
    using System.Data.SqlClient;
    
    using System.EnterpriseServices;
    
    
    
    namespace StepByStep7_1
    
    {
    
        public class NorthwindSC : ServicedComponent
    
        {
    
            private SqlConnection sqlcnn;
    
            private SqlDataAdapter sqlda;
    
            private DataSet ds;
    
    
    
            public NorthwindSC()
    
            {
    
                // Create a connection to the
    
                // Northwind SQL Server database
    
                sqlcnn = new SqlConnection(
    
                    "data source=(local);" +
    
                    "initial catalog=Northwind;" +
    
                    "integrated security=SSPI");
    
            }
    
    
    
            // This method executes a SELECT query and
    
            // returns the results in a DataSet object
    
            public DataSet ExecuteQuery(string strQuery)
    
            {
    
                // 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");
    
    
    
                return ds;
    
            }
    
    
    
            // This method updates the database with
    
            // the changes in a DataSet object
    
            public int UpdateData(DataSet ds)
    
            {
    
                // Update the database
    
                // and return the result
    
                SqlCommandBuilder sqlcb =
    
                    new SqlCommandBuilder(sqlda);
    
                return sqlda.Update(ds.Tables["Results"]);
    
            }
    
        }
    
    }
    
    
  6. Open the AssemblyInfo.cs file in the project and add the following using directive:

    
    using System.EnterpriseServices;
    
    
  7. Add assembly-level attributes ApplicationName, Description, and ApplicationActivation to AssemblyInfo.cs as follows:

    
    [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\Debug or bin\Release 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 the ServicedComponent class. Using an assembly level attribute, I have also specified that the NorthwindSC serviced component 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, so Visual Studio .NET uses a separate file named AssemblyInfo.cs to store assembly-level attributes. You can, though, if you wish, write the assembly-level attributes directly in the class file.

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 keys: a public key and a private key.

The Strong Name tool (sn.exe) that comes as a part of the .NET Framework SDK can be used to create a strong name. In Step-by-Step 7.2, you first create a strong name and then modify the program in 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 the 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 320C07 solution resides and issue the following command to create a pair of public/private keys (see Figure):

    
    sn –k 70320.snk
    
    
    6. You can create a public/private key pair by using the Strong Name tool.

    graphics/07fig06.jpg

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

  3. Open the AssemblyInfo.cs file of the StepByStep7_1 project. Scroll down in the file and change the AssemblyKeyFile attribute as follows:

    
    [assembly: AssemblyKeyFile(@"..\..\..\70320.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 pair generated in Step-by-Step 7.2 to sign assemblies in many of the 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 identities, most software development teams use a slightly different process for signing an assembly. This process is called delay signing, a technique that I discuss in detail, along with strong names, in Chapter 10.


Registering the Serviced Component into the COM+ Catalog

A serviced component must be registered into the COM+ Catalog to use any of the COM+ component services. A serviced component must be signed with a strong name before it may 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 on the method level in the class library.

Remember that the COM+ Catalog came before 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) provide the System.EnterpriseServices.RegistrationHelper class to simplify the registration process. The RegistrationHelper.InstallAssembly() method performs the following steps to register a serviced component in the COM+ Catalog:

  1. Use the RegistrationServices.RegisterAssembly() method to register the assembly in the Registry. All classes in the assembly therefore 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 COM LoadTypeLibrary() method.

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

NOTE

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


Using the RegistrationHelper class requires you to 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, use the RegistrationHelper class internally:

  • Dynamic or Lazy Registration— In dynamic registration, the registration of a serviced component is delayed until the component is first used. When a client application attempts to create an instance of a serviced component for the first time, the CLR registers the assembly and the type library and configures the COM+ Catalog. Dynamic registration occurs only once for a particular version of an assembly.

  • Manual Registration— The .NET Framework provides the .NET Services Installation tool (regsvcs.exe) to manually install a serviced component from the command line.

Figure compares the advantages and disadvantages of the dynamic and manual registration process.

Advantages and Disadvantages of Dynamic and Manual Registration
 

Dynamic Registration

Manual Registration

Advantages

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

1. Manual registration is the only way for registering the Assemblies, which are placed in the GAC.

2. Manual registration enables the serviced components to be called by COM clients, in addition to the .NET clients.

Disadvantages

1. The registration process requires administrative privileges and sometimes the first user of the component or the Web application may not have those privileges.

2. Assemblies placed in the 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.

Step-by-Step 7.3 shows how to register a serviced component manually by using the .NET Services Installation tool (regsvcs.exe).

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 may want to know the errors and correct them before any client actually accesses the serviced component.


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 project—in this case, the project's bin\Debug directory.

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

    
    regsvcs StepByStep7_1.dll
    
    
    Figure. You can add a serviced component to the COM+ Catalog by using regsvcs.exe.

    graphics/07fig07.jpg

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

Using the Component Services Administrative Tool to Manage Components

After an application is registered into the COM+ Catalog, the application can be configured easily 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 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.

    8. You can manage a serviced component application by using the Component Services administrative tool.

    graphics/07fig08.jpg

  2. Right-click on the Northwind Data Application and select Properties from the context menu. This opens the Northwind Data Application Properties dialog box, as shown in Figure. 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 with the Components Services administrative tool.

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

    graphics/07fig09.jpg

  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.cs file in the StepByStep7_1 project.

  4. 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 the 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. Notice the CLSID (Class Identifier) 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 with the Components Services administrative tool.

    10. You can manage the properties of a serviced component via its Properties dialog box.

    graphics/07fig10.jpg

  6. Expand the StepByStep7_1.NorthwindSC node in the left pane to drill down into the list of interfaces as shown in Figure. Note that although you have not defined any interface in the C# program, there is an interface 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. Even if you explore all the interfaces, you will not find any of the two methods that you defined in the C# program for this component (refer to Step-by-Step 7.1).

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

    graphics/07fig11.jpg

In Step-by-Step 7.4, you see various options for configuring a COM+ application and the serviced components. At this time, you may 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 also can 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 to make the methods of a serviced component visible to the Component Services administrative tool as well as other COM applications.

Creating Interfaces That Are Visible to COM/COM+

COM clients and COM+ services communicate with the .NET components by using the interfaces provided by the .NET component. The COM Callable Wrapper (CCW) automatically generates these interfaces for a .NET class based on the setting of the ClassInterface and the InterfaceType attributes of the System.Runtime.InteropServices namespace. Additionally, you can 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 the 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 the classes in that assembly.

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

Figure 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 supports only 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 for two reasons. 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 classes. Second, this setting generates dual interfaces. This means the COM client can use these interfaces for both late binding as well as 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 the interface for your class. This is the recommended setting for the 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 C# class library project to solution 320C07. 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 a reference to the System.EnterpriseServices library.

  3. In the Solution Explorer, copy the NorthwindSC.cs file from the StepByStep7_1 project to the current project. Open the file and change the namespace name to StepByStep7_5. Delete the default Class1.cs.

  4. Add the following using directive to NorthwindSC.cs:

    
    using System.Runtime.InteropServices;
    
    
  5. Apply the following attribute to the NorthwindSC class:

    
    [ClassInterface(ClassInterfaceType.AutoDual)]
    
    public class NorthwindSC : ServicedComponent
    
    {
    
       ...
    
    }
    
    
  6. Open the AssemblyInfo.cs file in the project and add the following using directive:

    
    using System.EnterpriseServices;
    
    
  7. Add assembly-level attributes ApplicationName, Description, and ApplicationActivation to AssemblyInfo.cs as follows:

    
    [assembly: ApplicationName(
    
        "Northwind Data Application with Interfaces")]
    
    [assembly: Description("Retrieve and Update data " +
    
        "from the Northwind database")]
    
    [assembly: AssemblyKeyFile(@"..\..\..\70320.snk")]
    
    

    Note that you are using the key file, 70320.snk, already created in Step-by-Step 7.2. If you haven't created one before, create it now by 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\Debug or bin\Release directory of your project.

  9. Register StepByStep7_5.dll into the COM+ Catalog by using the .NET Services Installation tool:

    
    regsvcs StepByStep7_5.dll
    
    
  10. Open the Component Services administrative tool (or click on the Refresh toolbar icon if the tool is already open). You see an icon labeled 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.

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

    graphics/07fig12.jpg

  11. Right-click on the ExecuteQuery() method and select properties from the shortcut menu. The property sheet enables you to configure a method by means such as changing its description, as shown in Figure.

    13. You can configure a method by using its Properties dialog box.

    graphics/07fig13.jpg

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

    
    public interface INorthwind
    
    {
    
        DataSet ExecuteQuery(string strQuery);
    
        int UpdateData(DataSet ds);
    
    }
    
    
  13. Modify the definition of NorthwindSC class and the ClassInterface attribute as follows:

    
    [ClassInterface(ClassInterfaceType.None)]
    
    public class NorthwindSC :
    
         ServicedComponent, INorthwind
    
    {
    
       ...
    
    }
    
    
  14. Select Build, Build StepByStep7_5. Use the .NET Services Installation tool to register StepByStep7_5.dll into the COM+ Catalog:

    
    regsvcs StepByStep7_5.dll
    
    
  15. Open the Component Services administrative tool. Navigate to the Components node of the Northwind Data Application with Interfaces application. You now see a second instance of the NorthwindSC component. Expand the second instance of the component to see its interfaces and methods. You see that instead of the default interface NorthwindSC, the component now has the INorthwind interface with only the method that the serviced component implements and does not contain methods of its base classes (see Figure).

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

    graphics/07fig14.jpg

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 one. If you don't apply the ClassInterface attribute at all, then in fact the ClassInterface attribute is implicitly applied with ClassInterfaceType set to AutoDispatch.

From Step-by-Step 7.4 and Step-by-Step 7.5, you also note 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. Therefore, information about interfaces and methods are written to the COM+ Catalog only 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 enables COM clients to bind their programs with the DispId of the members at compile time.

The DispIds are generated based on the position of the member in the class. This causes a versioning problem 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 is changed in next version. Because of its late binding support, however, use of AutoDispatch is limited to only 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 binding and late binding. However, writing your own interface separates the view of a class from its implementation. Even if you decide to change the order of the methods in your implementation in the future, 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 the 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 Figure.

Figure 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 the COM client to get both a late-binding as well as an early-binding facility.

InterfaceIsIDispatch

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

InterfaceIsIUnknown

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

NOTE

Configuring COM Visibility By default, all public members in your programs are visible to COM. You may sometimes want to hide certain elements from COM. You can mark those elements by setting the Value property of the ComVisible attribute to false. The 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 of the classes in the assembly overrides the ComVisible attribute by setting its value to true, then that class will be visible to COM clients.

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 independent of each other. For more information on COM+ partitions, refer to the COM+ Platform SDK documentation in the MSDN Library (msdn.microsoft.com/library).


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

Component Identification

COM+ uses GUIDs to uniquely identify each COM+ application and its components. If you don't use GUIDs to identify the COM+ application and components, the assembly registration process assigns 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 may not want to specify a fixed ApplicationID because doing so prevents COM+ partitioning.

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 : ServicedComponent,

                           INorthwind

{

   ...

}

You pass a GUID to the constructor of the Guid attribute. You can generate a GUID by 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 hardcode 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 may need to register the component several times in the COM+ Catalog. In that case, unless you hardcode a GUID for the component, you end up registering multiple versions of the same component, each with a different GUID, in 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. A good idea in this case is to hardcode each component with a GUID rather than depend on the automatically generated GUID.

Installing the Component in the Global Assembly Cache

Before the 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 most recommended option when you deploy an application because it ensures that the CLR can always locate an assembly. To learn more about the GAC and how the CLR locates assemblies, refer to Chapter 10.

EXAM TIP

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


In Step-by-Step 7.6, you 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 Component for Use by Web Forms ASP.NET uses a technique called shadow copy to eliminate the need to stop a 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 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 project—in this case, the project's bin\Debug directory.

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

    
    gacutil /i StepByStep7_5.dll
    
    
    Figure. You can add an assembly to the GAC by using the gacutil.exe.

    graphics/07fig15.jpg

  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.

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

    graphics/07fig16.jpg

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

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

Component Versioning

All components in an assembly share the same version. You can specify the version of an assembly by using the assembly-level AssemblyVersion attribute. When working with Visual Studio .NET, each time when you modify and recompile a program, the version of the assembly is increased because the AssemblyVersion attribute is set to 1.0.*, which means that the major and minor versions of the assembly are fixed to 1 and 0 respectively, but the * in the latter part specifies a build number and a revision 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. During development, if you install several builds of an assembly in the GAC, you can easily clutter up the GAC with several versions of the assembly, even though you are interested in only the most recent one.

To keep the matter simple, you may fix the version number specified in the AssemblyVersion attribute so that the old copy of an assembly in the GAC is overwritten when a new copy of the assembly with the same version is installed in the GAC.

Consuming a Serviced Component

Instantiating and using a serviced component is no different from doing so with 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 enables 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 C# .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.cs to NorthwindSCClient.cs. Open the form in code view and change all occurrences of Form1 to refer to NorthwindSCClient instead.

  4. Add the following using directives:

    
    using System.Data.SqlClient;
    
    using StepByStep7_5;
    
    
  5. Place two GroupBox 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.

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

    graphics/07fig17.jpg

  6. Add the following code in the class definition:

    
    // Declare the serviced component
    
    private NorthwindSC nsc;
    
    
  7. Double-click the form and add the following code in the Load event handler:

    
    private void NorthwindSCClient_Load(
    
        object sender, System.EventArgs e)
    
    {
    
        // Instantiate the serviced component
    
        nsc = new NorthwindSC();
    
    }
    
    
  8. Double-click the Button controls and add the following code to their Click event handlers:

    
    private void btnExecute_Click(
    
        object sender, System.EventArgs e)
    
    {
    
        try
    
        {
    
          // Call the ExecuteQuery() method of the
    
          // NorthwindSC serviced component to execute the
    
          // query and bind the results to the data grid
    
            dgResults.DataSource =
    
                nsc.ExecuteQuery(txtQuery.Text);
    
            dgResults.DataMember = "Results";
    
        }
    
        catch(Exception ex)
    
        {
    
            MessageBox.Show(ex.Message, "Invalid Query",
    
              MessageBoxButtons.OK, MessageBoxIcon.Error);
    
        }
    
    }
    
    
    
    private void btnUpdate_Click(
    
        object sender, System.EventArgs e)
    
    {
    
        try
    
        {
    
            // Call the UpdateData() method of the
    
            // NorthwindSC serviced component to update
    
            // the database and display the number
    
            // of updated rows in the database
    
            int intRows = nsc.UpdateData(
    
                (DataSet) dgResults.DataSource);
    
            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(Exception ex)
    
        {
    
            MessageBox.Show(ex.Message, "Update Failed",
    
              MessageBoxButtons.OK, MessageBoxIcon.Error);
    
        }
    
    }
    
    
  9. Build the project. Set 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.

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

    graphics/07fig18.jpg

Although Step-by-Step 7.7 uses a Windows application to use 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 behaviors 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 not advised because it leads to versioning problems. You should set the ClassInterface attribute for a class to the ClassInterfaceType.None 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 able to locate 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 should test whether the feature is working and then proceed with adding the new feature. You are interested in using only 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 C# .NET Class library named GuidedPracticeExercise7_1 to the solution.

  2. In the Solution Explorer, right-click 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.cs file from the StepByStep7_5 project to the current project. Open the file and change the namespace to GuidedPracticeExercise7_1. Delete the default Class1.cs.

  4. Select Tools, Create GUID. In the create GUID dialog box, select the Registry format as the GUID format, as shown in Figure, and then click on the Copy button.

    19. The Create GUID dialog box enables you to create a GUID.

    graphics/07fig19.jpg

  5. Open NorthwindConstructSC.cs 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("3F45D6D8-244B-4039-80C1-0427A266874B")]
    
    public class NorthwindSC :
    
        ServicedComponent, INorthwind
    
    {
    
       ...
    
    }
    
    
  6. Open the AssemblyInfo.cs file in the project and add the following using directive:

    
    using System.EnterpriseServices;
    
    
  7. Add the following assembly-level attributes to the AssemblyInfo.cs file:

    
    [assembly: ApplicationName(
    
        "Northwind Data Application with Fixed Version" +
    
        " and component identification")]
    
    [assembly: Description("Retrieve and Update data" +
    
        " from the Northwind database")]
    
    
  8. Change the AssemblyVersion and AssemblyKeyFile attribute in the AssemblyInfo.cs file as shown here:

    
    [assembly: AssemblyVersion("1.0.0")]
    
    [assembly: AssemblyKeyFile(@"..\..\..\70320.snk")]
    
    

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

  9. Build the project. A GuidedPracticeExercise7_1.dll is generated, and a strong name is assigned to the file based on the specified key file.

  10. Launch the Visual Studio .NET command prompt and change the directory to the folder where the DLL file generated in step 9 resides. Issue the following command to install the assembly to the GAC:

    
    gacutil /i GuidedPracticeExercise7_1.dll
    
    
  11. At the command prompt, issue the following command to install the service component assembly to the COM+ Catalog:

    
    regsvcs GuidedPracticeExercise7_1.dll
    
    

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.