Serviced Component Woes (Part II)
"Like any COM object, Transaction Server objects can maintain internal state across multiple interactions with a client. An object that has this behavior is stateful. Transaction Server objects can also be stateless, which means the object doesn't hold an intermediate state while waiting for the next call from a client.
When a transaction is either committed or aborted, all the objects involved in the transaction are deactivated, causing them to lose any state they acquired during the course of the transaction. This helps ensure transaction isolation and database consistency; it also frees server resources for use in other transactions.
Completing a transaction enables Transaction Server to deactivate an object and reclaim its resources, thus increasing the scalability of the application. Maintaining state on an object requires the object to remain activated, holding potentially valuable resources, such as database connections. Stateless objects are more efficient and are recommended. "
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlsg/html/msdn_sql_mts.asp
So, it makes perfect sense that fields set during a transaction should not stay around. After all, if the transaction aborts for some reason, how do you know which fields are valid and which are not? It is possible that only half of the fields on the object were properly set before an exception was thrown that caused the transaction to abort. However, what happens when the transaction does not modify any fields on the object? Why must I still destroy the entire state of my object? There is no really good reason that I can think of. Consider this contrieved example:
public enum ShipperRegistry { GlobalUDDI, InternalUDDI }
public class ShippingProviderLocator
{
public string FindCheapestShipper(ShipperRegistry registry)
{
// connect to UDDI registry, find shippers implementing the shipping contract,
// get the cheapest one, and return the url of the service.
}
public string FindMostReliableShipper(ShipperRegistry registry, IRatingProvider p)
{
// connect to UDDI registry, find shippers implementing the shipping contract,
// get the most reliable one, and return the url of the service.
}
}
[Transaction]
public class OrderManager : ServicedComponentWithWSTransactionSupport, IOrderManager
{
string _shipperUrl;
public void InitializeForCheapestShipping()
{
ShippingProviderLocator locator = new ShippingProviderLocator();
_shipperUrl = locator.FindCheapestShipper(ShipperRegistry.GlobalUDDI);
}
public void InitializeForMostReliableShipping()
{
ShippingProviderLocator locator = new ShippingProviderLocator();
_shipperUrl = locator.FindMostReliableShipper(ShipperRegistry.GlobalUDDI, new StandardRatingProvider());
}
[AutoComplete]
public void Ship(Package p)
{
if(_shipperUrl == null)
{
throw new InvalidOperationException("The shipping manager has not been initialized");
}
ShippingLogManager logManager = new ShippingLogManager();
ShippingServiceProxy proxy = new ShippingServiceProxy();
proxy.Url = _shipperUrl;
ShippingInformation shippingInfo = proxy.Ship(p);
logManager.LogShipment(shippingInfo);
}
}
Now, maybe some of you COM+ gurus can explain why I would ever want this class to destroy all its fields after the Ship method returns? I can tell you why I wouldn't want to, because the initalization is darn expensive and I don't want to re-run it every time I ship something.