Introduction
Enterprise Components are components that belong to a COM+ application and are registered with Component Services. While they can be considered "legacy" to a certain extent, they are still very useful in cases when you need to use distributed transactions, declarative security or queuing, for example.
.Net provides support for enterprise components development in the form of ServicedComponent class that each component needs to derive from, as well as a number of attributes that can be applied to enterprise component's assembly, interfaces, class or methods.
Goal for Spring is to provide location transparency for enterprise components by allowing developers to export any class as an enterprise component.
Solution
Main obstacle to this goal is the fact that enterprise components need to be contained within a physical assembly on the file system in order to be registered with the COM+ Services. To make things more complicated, this assembly has to be strongly named before it can be successfully registered.
Spring provides two classes that allow all of this to happen.
Spring.Enterprise.ServicedComponentExporter class is responsible for exporting single component and making sure that it derives from ServicedComponent class. It also allows you to specify class-level and method-level attributes for the component in order to define things such as transactional behavior, queuing, etc.
Spring.Enterprise.EnterpriseServicesExporter class corresponds to a COM+ application, and it allows you to specify list of components that should be included in the application, as well as the application name and other assembly-level attributes.
Let's say that we have a simple service interface and implementation class, such as these:
namespace MyApp.Services
{
public interface IUserManager
{
User GetUser(int userId);
void SaveUser(User user);
}
public class SimpleUserManager : IUserManager
{
private IUserDao userDao;
public IUserDao UserDao
{
get { return userDao; }
set { userDao = value; }
}
public User GetUser(int userId)
{
return UserDao.FindUser(userId);
}
public void SaveUser(User user)
{
if (user.IsValid)
{
UserDao.SaveUser(user);
}
}
}
}
And corresponding object definition for it in the application context config file:
<object id="userManager" type="MyApp.Services.SimpleUserManager">
<property name="UserDao"><ref object="userDao"/></property>
</object>
Let's say that we want to expose user manager as an enterprise component so we can leverage its support for transactions. First we need to export our serviced as a component:
<object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services">
<property name="TargetName"><value>userManager</value></property>
<property name="ClassAttributes">
<list>
<object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices"/>
</list>
</property>
<property name="MethodAttributes">
<dictionary>
<entry key="*">
<list>
<object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices"/>
</list>
</entry>
</dictionary>
</property>
</object>
Exporter defined above will create a composition proxy for our SimpleUserManager class that extends ServicedComponents and delegates method calls to SimpleUserManager instance. It will also mark proxy class with a TransactionAtribute and all methods with an AutoCompleteAttribute.
Next thing we need to do is configure exporter for the COM+ application that will host our new component:
<object id="MyComponentExporter" type="Spring.Enterprise.EnterpriseServicesExporter, Spring.Services">
<property name="ApplicationName"><value>My COM+ Application</value></property>
<property name="Description"><value>My enterprise services application.</value></property>
<property name="AccessControl">
<object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices">
<property name="AccessChecksLevel"><value>ApplicationComponent</value></property>
</object>
</property>
<property name="Roles">
<list>
<value>Admin : Administrator role</value>
<value>User : User role</value>
<value>Manager : Administrator role</value>
</list>
</property>
<property name="Components">
<list>
<ref object="MyApp.EnterpriseServices.UserManager"/>
</list>
</property>
<property name="Assembly"><value>MyComPlusApp</value></property>
</object>
This exporter will put all proxy classes for the specified list of components into a specified assembly, sign the assembly, and register it with the specified COM+ application. If application does not exist it will create it and configure it using values specified for Description, AccessControl and Roles properties.
Accessing Enterprise Component from Application
Because serviced component classes are dynamically generated and registered, you cannot instantiate them in your code using new operator. Instead, you need to use Spring.Enterprise.ServicedComponentFactory definition, which also allows you to specify configuration template for the component as well as the name of the remote server component is running on, if necessary.
<object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services">
<property name="Name"><value>MyApp.EnterpriseServices.UserManager</value></property>
<property name="Template"><value>userManager</value></property>
</object>
You can then inject this instance of the IUserManager into a client class and use it just like you would use original SimpleUserManager implementation. As you can see, by coding your services as plain .Net objects, against well defined service interfaces, you can achieve true location transparency for your services through configuration.