CSLA.Server.ServicedDataPortal

As far as the data portal mechanism goes, we've done all the hard work at this point. The client-side and server-side DataPortal classes we've created take care of all the details around properly invoking the DataPortal_xyz() methods in our business objects. All that remains is to provide two-phase transactional support via Enterprise Services (COM+).

In fact, we've already started this process in our implementation of the client-side DataPortal , where we detect whether each DataPortal_xyz() method on a business object has the [Transactional()] attribute. If a method does have this attribute, the client-side DataPortal invokes a transactional server-side DataPortal , which is what we'll write in this section.

The transactional server-side DataPortal class is relatively trivial to create because it relies on the Server.DataPortal class we've already written to do all the real work. In fact, the only thing that the transactional DataPortal class does is delegate method calls to Server.DataPortal !

What makes the whole thing work is that the transactional DataPortal object is running as a serviced component in Enterprise Servicesits methods will have attributes to set up a transactional context before delegating the calls to Server.DataPortal . Because the transactional context is already set up, this means that Server.DataPortal and our business object code will run within that same context, enjoying all the protection of two-phase transactions.

Of course, it also means that our business logic to do data access will suffer the performance penalties that come with two-phase transactions. It's important to weigh the arguments we made in Chapter 3 against each other to determine what will work best in your application environment.

Transactional DataPortal

The transactional DataPortal has the same four methods as Server.DataPortal , as shown in Table 5-4.

Table 5-4: Methods Exposed by the Transactional DataPortal

Method

Description

Create()

Invokes Server.DataPortal 's Create() method

Fetch()

Invokes Server.DataPortal 's Fetch() method

Update()

Invokes Server.DataPortal 's Update() method

Delete()

Invokes Server.DataPortal 's Delete() method

Prior to delegating each call to Server.DataPortal , each of these methods uses Enterprise Services attributes to configure a transactional context within COM+.

Creating the Assembly

Because this code will run in COM+, it needs to be in a separate assembly. We don't want or need to force the rest of our framework code or our business objects to be registered within COM+. Only this one class requires this extra step. Figure 5-8 shows all our framework assemblies and which ones depend on which.

image from book
Figure 5-8: Component dependency diagram
Tip 

The fact that we can avoid registering the remainder of our framework (and, more important, our business DLLs) in COM+ is really nice. Whereas COM+ (or MTS) was somewhat helpful in managing and deploying COM-based code, it complicates the deployment of .NET code, so the fewer assemblies we need to put into COM+, the better.

Add a new project to the solution, and name it CSLA.Server.ServicedDataPortal . We know that we'll be running in Enterprise Services, and therefore that we'll need a strong name, so add the following lines to AssemblyInfo.cs , changing the file name to point to your particular key file:

  [assembly: AssemblyKeyFile(@"c:\mykey.snk")]  

Since our code will be delegating to Server.DataPortal , we need a reference to that project. We'll also need a reference to the Enterprise Services library within .NET. Add both of these using the Add Reference dialog as shown in Figure 5-9.

image from book
Figure 5-9: Referencing the DataPortal and EnterpriseServices assemblies

To integrate properly with Enterprise Services, we need to add some attributes to the AssemblyInfo.cs file. (These attributes were discussed in Chapter 3.) First, we need to use the Enterprise Services namespace:

 using System;  using System.EnterpriseServices;  

Then we can add the attributes:

  // EnterpriseServices settings [assembly: ApplicationActivation(ActivationOption.Library)] [assembly: ApplicationName("CSLA DataPortal")] [assembly: Description("CSLA .NET data portal")] [assembly: ApplicationAccessControl(true)]  
Tip 

Note the use of the ApplicationAccessControl attribute, which indicates that COM+ security should be enabled for this assembly. This attribute isn't required in .NET 1.0, but in .NET 1.1 we'll get a warning when attempting to register our assembly with COM+ if we omit it. The default is to have security enabled, so by adding this attribute we don't change anything, but we avoid the warning in .NET 1.1.

The assembly now is configured to run in a COM+ library application, which offers optimal performance.

Tip 

In the world of COM and Visual Basic 6, we usually avoided using library applications because of stability issues. By running in the same process as the host, a COM component could destabilize the host process due to bugs in the component. This issue isn't nearly as big in .NET. First, the ASP.NET environment includes self-healing technologies so that if an assembly such as our transactional DataPortal did have a bug that crashed the host, it would automatically recover. Second, .NET code is far less likely to destabilize the host than COM code was, so there's less chance of this happening in the first place.

When using the transactional DataPortal , we'll typically be invoking via remoting, so the transactional DataPortal will be running within our remoting host application (that is, IIS, as implemented later in this chapter). By being set as a library application, it means that our code will run in that same process, but within a transactional context. This offers the best performance, since it avoids any extra cross-process communication on the server.

It's also possible to use the transactional DataPortal in the client process, assuming that the client is running on a machine where COM+ is installed (Windows 2000 or higher). Again, being configured to use a library application avoids any extra cross-process communication and offers the best possible performance.

Creating the DataPortal Class

At this point, our assembly is ready to go, and we can create the transactional DataPortal class. Rename Class1.cs to DataPortal.cs . Next, to run in Enterprise Services, we must inherit from ServicedComponent . Change the code in the class to the following:

  using System; using System.EnterpriseServices; namespace CSLA.Server.ServicedDataPortal {   public class DataPortal : ServicedComponent   {   } }  

This is the basic structure of any class that will run within Enterprise Services. The ServicedComponent base class already inherits from MarshalByRefObject , so any class we create for Enterprise Services will create anchored objects that always run on the machine where they are created.

We use attributes on the class and its methods to control which of the Enterprise Services are to be used. In our case, we'll specify that we want transactional behaviors:

 using System; using System.EnterpriseServices; namespace CSLA.Server.ServicedDataPortal {  [Transaction(TransactionOption.Required), EventTrackingEnabled(true)]  public class DataPortal : ServicedComponent   {   } } 

The [Transaction()] attribute allows us to tell Enterprise Services that we require a transaction. Notice that we aren't requiring a new transaction, so we can participate in an existing transaction if one is already under way. However, if no transaction exists, COM+ will always create one before running any code.

The [EventTrackingEnabled()] attribute tells Enterprise Services that our object should provide tracking events to COM+. This means our object will display usage information in the component services management console, so we can tell how many objects are active, and so forth.

Data Access Methods

All that remains now is to implement the four methods, which are almost identical to one another. They merely create a Server.DataPortal object and delegate the method call to that object.

Each of our delegating methods has an [AutoComplete()] attribute, telling COM+ that the method should be assumed to succeed unless it throws an exception. In other words, the only way COM+ will roll back the transaction is if an exception is thrown by our code. If that doesn't happen, it will commit the transaction.

The magic here is that the Server.DataPortal object is created within our preexisting transactional context. In other words, Server.DataPortal is protected by transactions. When it then calls methods on our business object, those methods are also protected by transactions. If our business object throws an exception, the transaction will be rolled back by COM+.

Here are the four methods:

  [AutoComplete(true)]     public object Create(object criteria, object principal)     {       CSLA.Server.DataPortal portal = new CSLA.Server.DataPortal();       return portal.Create(criteria, principal);     }     [AutoComplete(true)]     public object Fetch(object criteria, object principal)     {       CSLA.Server.DataPortal portal = new CSLA.Server.DataPortal();       return portal.Fetch(criteria, principal);     }     [AutoComplete(true)]     public object Update(object obj, object principal)     {       CSLA.Server.DataPortal portal = new CSLA.Server.DataPortal();       return portal.Update(obj, principal);     }     [AutoComplete(true)]     public void Delete(object criteria, object principal)     {       CSLA.Server.DataPortal portal = new CSLA.Server.DataPortal();       portal.Delete(criteria, principal);     }  

Updating the CSLA Project

As with Server.DataPortal , our client-side DataPortal code makes use of ServicedDataPortal.DataPortal , so we need to return to the CSLA project and add a reference as shown in Figure 5-10.

image from book
Figure 5-10: Referencing the ServicedDataPortal assembly

With this reference, our solution should compile. At this point, we have a basically functional framework, though we're obviously not done yet. We still need to create a remoting host in which the server-side DataPortal objects can run if we're configured to use remoting. We also need to implement the custom table-driven security objects, and we'll wrap up by creating a generic, read-only name-value collection object for use by business applications.



Expert C# Business Objects
Expert C# 2008 Business Objects
ISBN: 1430210192
EAN: 2147483647
Year: 2006
Pages: 111

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