Enterprise Services (COM)

Enterprise Services (COM+)

Enterprise Services is the set of .NET services known previously as COM+. In this section, we'll be providing an overview of Enterprise Services and its features as they relate to our CSLA .NET framework. We'll also discuss some of the background concepts necessary to understand how Enterprise Services work.

Some of the more commonly used services provided by COM+ include the following:

  • Two-phase distributed transactions

  • Object pooling

  • Queued components

  • Role-based security

Enterprise Services includes all the functionality of COM+, so it's a very large topic. The discussion that follows is far from complete, and we'll only focus on topics of immediate interest for this book ” specifically , the two-phase distributed transactions that allow us to protect data access when multiple databases are involved.

Enterprise Services and COM+ Contexts

Because Enterprise Services and COM+ are essentially one and the same, Enterprise Services features can only be used by code running on a machine in which COM+ is installed ”which means Windows 2000 or higher. It also means that any of our code that uses Enterprise Services features will be running in a COM+ context .

So what is a COM+ context and how does it relate to Windows or .NET? To understand that, we need to take a quick step backwards and remind ourselves that all .NET code runs in a .NET application domain ”an isolated environment that fulfills a role comparable to the one that's fulfilled by a Windows process for regular Windows applications. Because everything in Windows ”including .NET code ”must run within a Windows process, the result is that a .NET application domain must run within a Win32 process. In fact, a Win32 process can contain multiple application domains. AppDomains are contained within Win32 processes as shown in Figure 3-15.

image from book
Figure 3-15: .NET AppDomain running inside a Win32 process

As we know, code that's running in one Windows process is isolated from code in any other Windows process. .NET simply adds a new layer to this by saying that code in one application domain is isolated from code in any other application domain. (This is true regardless of whether those application domains are in the same Windows process.)

When our code is running within Enterprise Services, things are even more complex. In that case, our code will also be running within an Enterprise Services (COM+) context, in which a context groups objects that have the same Enterprise Services environment properties (such as transactional requirements). If we're updating some databases and have several objects involved, they'll all be part of that context.

So a .NET application domain runs within a Windows process, and within our application domain we may have code running within an Enterprise Services context, as shown in Figure 3-16.

image from book
Figure 3-16: Enterprise Services context inside a .NET AppDomain inside a Win32 process

In fact, within a single application domain, we may have some code running with no Enterprise Services context, and other code running within an Enterprise Services context. There may even be several Enterprise Services contexts running within the same application domain!

Running Code in Enterprise Services

To use any Enterprise Services feature, we must do some extra work in our code, and in our deployment. First, there are some steps that we must take when creating our project and class in order to gain access to Enterprise Services. Next we must consider how our DLL will get installed into a COM+ application from which it can run.

There are two ways to get our code to run within an Enterprise Services context. We can program specifically to use Enterprise Services by subclassing ServicedComponent , or we can have code that's already running in Enterprise Services call a "normal" .NET assembly, in which case the latter will be loaded into Enterprise Services as well.

Note  

If we're using .NET 1.1 and running under Windows Server 2003, we can also use a new capability that allows us to enter and leave COM+ transactional protection programmatically. We call a method to enter the transactional context, do our data access, and then call a method to indicate we're done with our transaction. Because the technologies involved weren't widely deployed at the time of writing, however, we won't use that technique in our architecture.

Using Enterprise Services Directly

The direct (and most common) way to use Enterprise Services is to reference System.EnterpriseServices.dll via the Add References dialog box, use the System.EnterpriseServices namespace with a using statement, and then inherit from ServicedComponent when we create our class as follows:

 public class MyEntSvc : ServicedComponent { } 

We also need to add some attributes to the AssemblyInfo.cs file of any project that contains a class inheriting from ServicedComponent . First, we need to give the assembly a strong name , which uniquely identifies an assembly by name, version number, culture, and a publisher key. All assemblies have a name and a version number, so it's the publisher key that makes the difference. If you already have a key file with your publisher key, you're all set. If you don't, you can use the sn.exe .NET command-line utility to create a key file with this command as shown here:

  > sn -k mykey.snk  

The resulting file contains a unique key that you can use to sign your assemblies. Keep this key file safe, because it uniquely identifies you or your organization as the publisher. Once you have a key file, you need to reference it within the AssemblyInfo.cs file by adding lines like this:

  // Strong name [assembly: AssemblyKeyFile("h:\rdl\mykey.snk")]  

Next time the assembly is compiled, the public key from the key file will be incorporated into the DLL, giving the DLL a strong name.

We also need to provide some Enterprise Services information. Remember that our code will end up running in a COM+ application or container. We can provide some attributes that help to define the nature of this application; at a minimum, we should provide an application name, a description, and an activation type as shown here:

 // Enterprise Services attributes  [assembly: ApplicationName("TestService")] [assembly: Description("My test service")] [assembly: ApplicationActivation(ActivationOption.Library)]  

When using Enterprise Services in .NET, we'll typically want to use a COM+ library application. This gives us access to the Enterprise Services features we want, but causes the code to run in the calling process ”whether that's a Web Forms page, a web service, or a remoting host.

Note  

The alternative is to use a COM+ server application, which causes COM+ to host our component in a separate process. By running the Enterprise Services components in the calling process, we avoid the overhead of making cross-process calls.

Note that nothing we've done so far actually uses any Enterprise Services features. All we've done is to engineer our class and assembly so that they run within the Enterprise Services environment ”when we create an object of type MyEntSvc , it will always run within a COM+ context. To use any specific feature, we must apply attributes to the assembly, class, and methods (as appropriate) that identify the features we want to use as shown in Figure 3-17.

image from book
Figure 3-17: An object and assembly loaded into an Enterprise Services context

Installing the Assembly into COM+

At this point, we've coded our DLL to use Enterprise Services by creating a class that inherits from ServicedComponent . For it to work, it must run within a COM+ context, which means that it must be installed into a COM+ application. As we stated previously, there are two ways to get a .NET assembly into a COM+ application: automatically and manually.

If any .NET code attempts to interact with a serviced component that we've written but haven't yet installed in COM+, the .NET runtime will automatically attempt to put that component into COM+, based on the information we put in the AssemblyInfo.cs file. This will succeed if the calling code is running under a user account with the Windows privileges required to create and install a component into a COM+ application. However, if the user account running the code doesn't have enough privileges, it will fail.

Note  

The default ASP.NET user account doesn't have the security required to install Enterprise Services components.

Because of the potential for problems arising from the automatic approach, it's usually better to use manual installation before running the application. This way, we can do the installation while logged in under an administrator account, and make sure that it's successful! Manual installation is done via the .NET regsvcs.exe command-line utility. For instance, if we have a .NET assembly called testservice.dll that contains a serviced component, we could install it like this:

  > regsvcs testservice.dll  
Note  

We can't simply drag and drop .NET assemblies into the component services console like we can with COM components. COM+ and the component services console predate .NET, and don't understand how to deal with .NET assemblies.

This command will create a COM+ application (if it doesn't already exist) by using the information from the attributes we added to the AssemblyInfo.cs file. In our example, it would create a library application. (If the COM+ application already exists, the regsvcs.exe utility doesn't change its settings.) The command then registers our assembly with COM+, placing the assembly into the COM+ application.

Using Enterprise Services Indirectly

The less obvious way to get our code to run within a COM+ context is to write a "normal" DLL, and then call it from another DLL that does use Enterprise Services. For example, we could create a new DLL containing a new class as follows:

  public class MyWorker {   public void DoWork()   {} }  

This project doesn't reference System.EnterpriseServices , nor does the class inherit from ServicedComponent . Code like this always runs within the application domain of the code that invokes it. Normally, we'd think of that being a Windows Forms executable or a Web Forms page, but it could also be that this component is invoked from another DLL that's already running in Enterprise Services. Suppose, for example, that our MyEntSvc class had the following code:

 public class MyEntSvc : ServicedComponent {  public void CallWorker()   {     MyWorker obj = new MyWorker();     obj.DoWork();   }  } 

Because the calling code is running in a COM+ context within the application domain, any DLLs that it invokes will run within that same COM+ context as shown in Figure 3-18.

image from book
Figure 3-18: A normal .NET assembly loaded into an Enterprise Services context

This is a powerful feature because it means that we can write much of our code "normally," but still be able to run it from within Enterprise Services if we need to do so. We'll make use of this feature in Chapter 5 where we'll write some components for dealing with data access.

Two-Phase Distributed Transactions

Enterprise Services includes a wide variety of features and capabilities, but the one that we need to support in our n- tier framework is two-phase distributed transactions. Two-phase transactions are designed to provide transactional protection when we update two or more transactional resources (whereby a "transactional resource" means something like a database or a message queue).

Note  

Enterprise Services uses the Distributed Transaction Coordinator (DTC) to manage its distributed transactions. The DTC supports a variety of transactional resource types, including SQL Server, MSMQ, and any XA-compliant database.

The other reason for using Enterprise Services transactions is when we want to create a number of server-side objects that all interact with the database as part of a single transaction. Enterprise Services transactions can be valuable here, even when we're talking to only one database, because the resulting code is much simpler. If we tried to do this by hand, we'd have to pass a transaction or connection object around manually from component to component in order to coordinate the transaction.

Keep in mind, however, that designing a set of server-side components to update a single database, and then relying on Enterprise Services transactions to simplify your code, will cause a performance hit: We're still incurring the overhead of COM+ and the DTC. We should always question the value of breaking server processing into multiple components and weigh the benefits of componentization against the performance cost of Enterprise Services transactions. If we aren't updating two or more transactional resources or trying to include many components in a single transaction, then Enterprise Services transactions probably aren't the right solution.

Note  

The performance difference is clearly documented in an MSDN Library article titled "Performance Comparison: Transaction Control" by Priya Dhawan. [1] The article documents a set of performance tests in which we see that manual transactions via ADO.NET are about twice as fast as automatic transactions in COM+.

To use Enterprise Services transactions, we must add the [Transaction()] attribute to our class. We'll also add the [EventTrackingEnabled()] attribute so that we can use the component services console to view statistics about our component as it is running, as shown here:

  [Transaction(TransactionOption.Required), EventTrackingEnabled(true)]  public class DataService : ServicedComponent { } 

The [Transaction()] attribute indicates that instances of this class must run in a context that's protected by transactions. It accepts an argument ”one of the values of the TransactionOption enumeration ”that indicates exactly how the object treats transactions, as shown in Table 3-2.

Each method in a transactional object can include an attribute to indicate whether it will be using the Autocomplete feature. Autocomplete is the idea that the transaction is assumed to succeed, unless the method raises an error. If an error is raised, the transaction will be rolled back. If we omit the [AutoComplete()] attribute or set its value to false , we must indicate manually whether our code completed successfully.

Autocomplete is the easiest way to use Enterprise Services transactions, and is the approach we'll use in our transactional data-portal implementation. To use Autocomplete, we write our method this way:

  [AutoComplete(true)]  public void UpdateData()   {   } 

Within our method, all we need to do now is ensure that any failure to update the data results in an exception being thrown. If an error is raised, COM+ will automatically roll back the transaction for any databases and any components involved. Typically, what this means is that we can simply write our data-access code so that we don't catch any errors ourselves. By allowing any error simply to occur, we get COM+ to handle the rollback for us. (We should still use a using block to close the database connection, however.)

Table 3-2: Enterprise Services Transaction Options

Value

Description

Required

The object must run in a transaction. If the calling object already has one, it will join that transaction. Otherwise, a new transaction will be created.

RequiresNew

The object will always run in a new transaction.

Supported

The object will participate in an existing transaction. If there's no existing transaction, it will run in a nontransactional context.

Disabled

The object will run in the context of any other transactional components, but it will not participate in the transaction.

NotSupported

The object will run in a separate, nontransactional context, and is thus isolated from any transactional components.

 [AutoComplete(true)]   public void UpdateData()  {     using(SqlConnection cn = new SqlConnection())     {       // Open connection here       // Do data updates here     }  } 

We're not catching any errors, so they'll propagate up to COM+, where they'll cause the transaction to roll back. However, we do have a using block, so we're going to close the connection object whether there was an error or not.

COM+ doesn't alter or stop the error; it just uses it as an indicator that our transaction should be rolled back. The error itself is automatically raised back to the client code that called our component. In the case of our transactional data portal, the calling code will be the client-side data portal.

[1] Priya Dhawan, "Performance Comparison: Transaction Control: Building Distributed Applications with Microsoft .NET," MSDN, February 2002. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/Dnbda/html/Bdadotnetarch13.asp.



Expert C# Business Objects
Expert C# 2008 Business Objects
ISBN: 1430210192
EAN: 2147483647
Year: 2006
Pages: 111
Authors: Rockford Lhotka
BUY ON AMAZON

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net