May 2004 - Posts
Note: this entry has moved.
Recently, Matt Powell wrote about returning XML from webservices, and I certainly agree with him that returning it as an opaque string is really bad. Later on, Matevz Gacnik suggested a couple points to consider when to choose one or the other. Matt continued his rant this time tackling at the heart of the problem: why do you need to load a full-blown DOM just to get the nice XML returned from the webservice? At this point, I felt I should kick in.
You see, one of the greatest things about having a fully OO platform is that you can easily complement what's missing by simply inheriting a couple classes, plugging your stuff in the infrastructure. So, let me state it clear and loud: you don't have to load an XmlDocument to return well-formed XML from webservices. Let's see how this is accomplished.
As you may already know, the ASP.NET WebMethod framework uses the XmlSerializer to convert arguments and return values to their XML representations. When your webmethod returns an XmlDocument, it will serialize the XmlDocument.DocumentElement, effectively the root element. If you return an XmlNode, it will simply serialize it. So, you could either return an XmlDocument *if you already have it at hand*, or an XmlNode (this could even be the DocumentElement of the previous one). The client will see the same WSDL and the Visual Studio Add Web Reference feature will generate a proxy returning an XmlNode in either case. So, *both* methods below result in the same proxy class:
[WebMethod]
public XmlDocument MyDocumentMethod()
{
//...
}
[WebMethod] public XmlNode MyNodeMethod()
{
//...
}
// Proxy code
MyService proxy = new MyService();
XmlNode xml = proxy.MyDocumentMethod();
// or xml = proxy.MyNodeMethod();
When the XmlSerializer used to build the SOAP Body for our return value realizes it's an XmlDocument/XmlNode, it will simply call its WriteTo(XmlWriter w) method. So, what can we do with all this knowledge? Well, we can leverage it to avoid loading full DOMs when we have other (better IMO) representations such as an XmlReader or an XPathNavigator. The process is simply to create a special-purpose XmlNode-derived class that will serve as our trojan horse (very appropriate now that Troy is in vogue ;)) into the webservice serialization infrastructure.
Implementation
Implementing the XmlNode.WriteTo() method with an XmlReader is all too easy:
private class XmlReaderNode : SerializableNode
{
private XmlReader _reader;
private bool _default;
public XmlReaderNode() {}
public XmlReaderNode(XmlReader reader, bool defaultAttrs)
{
_reader = reader;
_reader.MoveToContent();
_default = defaultAttrs;
}
public override void WriteTo(XmlWriter w)
{
w.WriteNode(_reader, _default);
_reader.Close();
}
}
Note that we need to move the reader to the actual content, because we don't have to serialize the document declaration again, because the result is placed inside the SOAP body. We'll get to the base SerializableNode class in a minute. Basically it overrides everything and throws NotSupportedExceptions.
Implementing the XmlNode.WriteTo() method with an XPathNavigator is also easy, as we can take advantage of the Mvp.Xml project XPathNavigatorReader:
private class XPathNavigatorNode : SerializableNode
{
private XPathNavigator _navigator;
public XPathNavigatorNode() {}
public XPathNavigatorNode(XPathNavigator navigator)
{
_navigator = navigator;
}
public override void WriteTo(XmlWriter w)
{
w.WriteNode(new XPathNavigatorReader(_navigator), false);
}
}
Note that in both cases, we use the built-in infrastructure by calling the XmlWriter.WriteNode() method. Specially in the case of the XmlReaderNode, that means we're never building an in-memory DOM. In the case of the XPathNavigatorNode it means that we're working with the existing in-memory infoset, and we're not loading another one for the serialization.
The only "trick" in the base SerializableNode is in its constructor, because the base XmlNode and XmlElement don't allow empty constructors. The workaround was to create an element with a dummy name and an empty owner document:
private abstract class SerializableNode : XmlElement
{
public SerializableNode() : base("", "dummy", "", new XmlDocument()) {}
public override XmlNode AppendChild(XmlNode newChild)
{
throw new NotSupportedException(SR.XmlDocumentFactory_NotImplementedDOM);
}
//...all other members...
}
At this point you may be wondering why all these classes are private. Well, in order to isolate the user from the internal implementation details of these classes, I prefer to create a factory class that simply returns XmlNode values:
public class XmlNodeFactory
{
private XmlNodeFactory() {}
public static XmlNode Create(XPathNavigator navigator)
{
return new XPathNavigatorNode(navigator);
}
public static XmlNode Create(XmlReader reader)
{
return Create(reader, false);
}
public static XmlNode Create(XmlReader reader, bool defaultAttrs)
{
return new XmlReaderNode(reader, defaultAttrs);
}
//...private inner classes follow...
}
Usage
Now, let's get back to your webservice code. Most probably, specially if you have been reading my weblog and many others that advice against the XmlDocument, you'll already have an XmlReader or XPathNavigator you got from your business classes or your data access layer. With that at hand, you can simply use the following code:
[WebMethod]
public XmlNode GetFromNavigator()
{
XPathNavigator nav;
// Get your navigator...
return XmlNodeFactory.Create(nav);
}
[WebMethod]
public XmlNode GetFromReader()
{
XmlReader reader;
// Get your reader, maybe even from SQL Server?...
return XmlNodeFactory.Create(reader);
}
Note that I mention getting the reader from SQL Server. If you look at the XmlReaderNode code shown in the previous section, you'll notice that once the WriteTo() serialization method is invoked, the reader is closed.
The interesting thing in the XPathNavigator case is that you can position the navigator on the node you want to return, and have only that "fragment" serialized. For example:
[WebMethod]
public XmlNode GetFromNavigator()
{
string xml = "<customer><order>25</order><info>Daniel Cazzulino</info></customer>";
XPathDocument doc = new XPathDocument(new StringReader(xml));
XPathNavigator nav = doc.CreateNavigator();
//Move to the customer node
nav.MoveToFirstChild();
//Move to the order node
nav.MoveToFirstChild();
return XmlNodeFactory.Create(nav);
}
The returned data will be only the <order>25</order> element (and any children it may have). Cool, huh? That's XPathNavigatorReader courtesy ;)
The full project source code belongs to the Mvp.Xml project and can be downloaded from SourceForge. Enjoy!
Check out the Roadmap to high performance XML.
Note: this entry has moved.
Looks like so...
Update: dead for Whidbey, live for Longhorn :(
Note: this entry has moved.
In case you haven't heard, the Visual Studio 2005 Community Technology Preview May 2004 is available to all MSDN subscribers! :D
It's funny how I found it, as it was released less than 2 days ago and not even the new MSDN Subscriber Downloads was updated, although I've just checked the Visual Studio 2005 Developer Center and it now contains the news. I was just about to report a bug through the new MSDN Feedback Center, when I had to choose the version, and guess what, a new one was there! :o)
I wish the feed was updated more often. In fact, that should be the first one, as it's probably the one requiring less work, IMO.
Note: this entry has moved.
I've been doing a comparison of both products recently. Before you read, I must say that I used log4net a lot (I even appear as a contributor :o), so I may be biased. On the other hand, I only evaluated EIF, looked at how it's used in a real world product, and read the documentation provided, as well as built the examples.
Here are my conclusions.
Conventions:
· x: feature is supported equally.
· -: feature is supported/implemented, but is not as good as the other product
· +: feature is better supported/implemented than the other product
|
Feature |
EIF |
log4net |
|
General |
|
Decoupling of event source and event sink |
x |
x |
|
Arbitrary target event sinks |
x |
x |
|
Number of built-in event sinks supported |
3 |
20 |
|
Extensible |
x |
x |
|
Configuration |
-
(quite complex) |
+
(very simple) |
|
WMI supported built-in |
x |
|
|
Arbitrary log data |
+ |
-
(built-in just text) |
|
Multiple logging "domains" (ability to configure the framework by modules, i.e. each logical feature has its own config, repository and sinks) |
|
+++
(a BIG plus for highly composed and pluggable apps) |
|
Installation |
-
(mostly requires admin permissions) |
+
(xcopy) |
|
Support |
? |
x
(excelent peer support) |
|
Source provided |
|
+ |
|
Supported Platforms |
.NET |
.NET, CF, Mono, SSCLI |
|
|
|
|
|
Runtime |
|
|
|
Performance with logging enabled |
x |
x |
|
Performance with logging disabled for a certain event |
- |
++
(orders of magnitude faster) |
|
Reconfiguration |
-
(only possible with WMI or Windows Trace) |
+
(full configuration monitoring & reloading) |
|
|
|
|
|
API - Dev friendliness |
|
|
|
Event (source) categorization |
-
(uses the event Type. leads to increasingly big hierarchies of event types) |
++
(combination of log level + logger name -dotted notation-) |
|
Event source inheritance |
|
++
(filters and sinks applied to all descendant sources) |
|
Customization |
x |
+
(everything) |
|
|
|
|
One of the "distinguishing" features of EIF is request tracing, but it is mostly irrelevant as it only works using the remoting CallContext, which reduces its applicability considerably. And remoting is not a long-term platform to rely on either... For request-like tracing on a single thread (important when there're multiple components involved in an operation happening on a single machine/thread) log4net offers similar functionality through its Nested Diagnostics Context.
For business events that rely on arbitrary objects being send, EIF has a clear advantage. I wonder, however, how many business notification scenarios are actually handled through logging... Through customization similar functionality could be implemented in log4net, however.
Overall: both seem like good logging packages. EIF may be more manageable as it has strong relationship with WMI. log4net, on the other hand, clearly wins on simplicity and in my opinion satisfies most common logging and tracing needs.
Except for the extra WMI stuff (which can also be implemented as custom extensions to log4net - I've done it) and arbitrary objects publication (also achievable, but requiring more work), I'd choose log4net.
Side Note
log4net "event source" (loggers): in log4net, you ask for a logger to the LogManager. Using a simple dotted syntax, you create hierarchycal loggers that inherit sinks, filters and threshold very easily. For example, the following code asks for a logger and logs an informational message:
ILog log = LogManager.GetLogger("Finance.Accounting.Customers.Administration");if (Log.IsInfoEnabled)
{ log.Info("Adding user " + _userId);}
some other component in the same module may use the following:
ILog log = LogManager.GetLogger("Finance.Accounting.Orders");if (Log.IsInfoEnabled)
{ log.Info("Adding order " + _orderId);}
All the "Finance" application, or the "Accounting" module can be configured in a single place, and affect both components. A common use is to pass the current component type to the GetLogger() method, therefore making the logger match class full names, which is also an easy way to configure groups of components or modules in an application. For example:
<!-- Enables all logging events in finance application (both components above) -->
<logger name="Finance ">
<level value="ALL" />
</logger>
<!-- Enables INFO logging events in accounting module (both components above) -->
<logger name="Finance.Accounting">
<level value="INFO" />
</logger>
<!-- Enables INFO logging events in Orders module (only for the last code above) -->
<logger name="Finance.Accounting.Orders">
<level value="INFO" />
</logger>
Custom logger levels can be used directly, without previous definition, for example "BusinessEvent" or "CriticalOperation". This functionality, combined with hierarchycal loggers is very flexible and completely removes the need in EIF for defining custom types for every event. In addition, arbitrary key-value (only strings) can be logged by using a so-called Mapped Diagnostics Context. Functionality similar to request tracing in EIF is available through Nested Diagnostic Context, that is threads-specific.
I'd love to hear arguments in favor of EIF. I may be missing some other features it has to offer, or I may have misunderstood some of its functionality, as (like I said) I haven’t used it before.
Have you used both products? What do you think about the points made above?
Note: this entry has moved.
I've released the seventh eighth "version" of the Mvp.Xml project on SourceForge. This is a maintenance release with some bugs fixed in the XPathNavigatorReader. Thanks to Tom Smalley and Joost Ploegmakers!
So far, the package contains:
Each feature is explained in a corresponding weblog post.
Update: the Mvp.Xml project now also powers the Atom.NET project, as explained in the announcement post.
Enjoy!
Note: this entry has moved.
This article is a companion to
Code Generation in the .NET Framework Using XML Schema article
published in the MSDN XML DevCenter.
In that article, I introduce you to the main classes in the .NET framework that
enable code generation from XSD files, the very clases the XSD.EXE tool uses.
The ideas come from my initial post on Customizing
XSD->Classes code generation, the "easy" way.
In this article I'll show how the VS.NET custom tool included in the MSDN article code download is created.
Building the XsdCodeGen Custom Tool
Creating a custom tool for VS.NET is extremely easy. Microsoft has released a
project with all the necessary classes for that purpose, which can be
downloaded from GotDotNet
(http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=4AA14341-24D5-45AB-AB18-B72351D0371C).
Everything we need to do is create a new class library project using that one
as a skeleton, and add a new class inheriting from the BaseCodeGeneratorWithSite
provided:
[Guid( "9B7CF25A-1782-433b-B534-0B94E76A7D62" )]
[ComVisible( true )]
public class XsdCodeGenerator : BaseCodeGeneratorWithSite
The generator is registered as a COM component, and that’s why we need to
specify the GuidAttribute and the ComVisibleAttribute. The former should
receive a new GUID which can be created from the Tools -> Create GUID menu.
We call it a generator because that’s the registry key they are registered
under. That key has two sub keys, one for the VB.NET and another for the C#
generators. We should register our tool with both, as we’re using the
language-agnostic CodeDom. Therefore, we define two constants that hold the
GUIDs of such categories:
private static Guid CSharpCategory =
new Guid( "{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}" );
private static Guid VBCategory =
new Guid( "{164B10B9-B200-11D0-8C61-00A0C91E29D5}" );
We’ll also store the GUID assigned to the class itself for easy access, as
we’ll need it for registration. We also define the custom tool name and
description with constants. The former is important as it’s the one that is
assigned to a file through the Property Browser in order to execute the tool:
private const string CustomToolName = "XsdCodeGen";
private const string CustomToolDescription = "MSDN Classes Generator";
The registry key we need to create looks like the following:
[HKLM\SOFTWARE\Microsoft\VisualStudio\{Version}\Generators\{C#/VB}\{ToolName}]
@="{ToolDescription}"
"CLSID"="{ToolGUID}"
"GeneratesDesignTimeSource"=dword:00000001
As we don’t have any dependencies on any VS version, we should register under
both keys, “7.0” and “7.1”, replacing the {Version} part. Also, we will
register for both language categories. We will store as a constant the full
registry key skeleton to format with the variable parameters. The registration
methods are:
private const string KeyFormat = @"SOFTWARE\Microsoft\VisualStudio\{0}\Generators\{1}\{2}";
protected static void Register( Version vsVersion, Guid categoryGuid )
{
using( RegistryKey key = Registry.LocalMachine.CreateSubKey(
String.Format( KeyFormat, vsVersion, categoryGuid.ToString( "B" ), CustomToolName ) ) )
{
key.SetValue( "", CustomToolDescription );
key.SetValue( "CLSID", CustomToolGuid.ToString( "B" ) );
key.SetValue( "GeneratesDesignTimeSource", 1 );
}
}
protected static void UnregisterCustomTool( Version vsVersion, Guid categoryGuid )
{
Registry.LocalMachine.DeleteSubKey(
String.Format( KeyFormat, vsVersion, categoryGuid.ToString( "B" ), CustomToolName ), false );
}
Finally, we have to call these methods at COM registration/unregistration
time. We can specify the methods to be executed at that time, through the ComRegisterFunctionAttribute/ComUnregisterFunctionAttribute
attributes:
[ComRegisterFunction]
public static void RegisterClass( Type t )
{
// Register for both VS.NET 2002 & 2003 (C#)
Register( new Version( 7, 0 ), CSharpCategory );
Register( new Version( 7, 1 ), CSharpCategory );
// Register for both VS.NET 2002 & 2003 (VB)
Register( new Version( 7, 0 ), VBCategory );
Register( new Version( 7, 1 ), VBCategory );
}
[ComUnregisterFunction]
public static void UnregisterClass( Type t )
{
// Unregister for both VS.NET 2002 & 2003 (C#)
Unregister( new Version( 7, 0 ), CSharpCategory );
Unregister( new Version( 7, 1 ), CSharpCategory );
// Unregister for both VS.NET 2002 & 2003 (VB)
Unregister( new Version( 7, 0 ), VBCategory );
Unregister( new Version( 7, 1 ), VBCategory );
}
After compiling the project, we need to run the following command (in a VS.NET
command prompt) on the project output folder to register the custom tool:
regasm /codebase XsdCodeGenerator.CustomTool.dll
And to unregister it:
regasm /unregister XsdCodeGenerator.CustomTool.dll
Alternatively, you can go to the project properties dialog, under
Configuration Properties > Build, and set Register for COM Interop to true.
To run the custom tool for a file, we just need to set its Custom Tool
property to XsdCodeGen.
Now we can finally move to the actual code generation inside the tool.
Generating Code
The first step is to factor out the code we used for the console application.
We will associate the custom tool with XSD files, so we will already have the
source filename in place. We won’t need the language because the BaseCodeGeneratorWithSite
base class we used already provides access to a CodeDomProvider that
is the appropriate one according to the current project type where the file
lives in. Finally, as we specified at tool registration time that we would
generate source at design-time (GeneratesDesignTimeSource registry
value), VS.NET will automatically generate a new file dependent on the schema
file, much like it does with a TypedDataset schema and its
corresponding class file, a WebForm and its code-behind, a WinForm and its
resource file, etc. All we need is implement the following method from the tool
base class:
protected abstract byte[] GenerateCode(
string inputFileName, string inputFileContent );
In order to better reuse code from the custom tool and the console app, we
will separate the XSD processing in a new class and we will make the console
app use this one by referencing the custom tool project. We could also create
an entirely new class library just for this purpose, too.
So, the GenerateCode() method implementation is:
protected override byte[] GenerateCode(
string inputFileName, string inputFileContent )
{
string code = "";
try
{
// Process the file.
CodeNamespace ns = Processor.Process( inputFileName, FileNameSpace );
// Generate code for it.
CodeGeneratorOptions opt = new CodeGeneratorOptions();
opt.BracingStyle = "C";
StringWriter sw = new StringWriter();
GetCodeWriter().GenerateCodeFromNamespace( ns, sw, opt );
// Finaly assign it to the result to return.
code = sw.ToString();
}
catch( Exception e )
{
code = String.Format( "#error Couldn't generate code!\n/*\n{0}\n*/", e );
}
// Convert to bytes.
return System.Text.Encoding.ASCII.GetBytes( code );
}
The new Processor class has almost the same code as the console
app, but only generates the CodeNamespace, leaving the
responsibility of generating the final source form to the caller. Note that the
base class for the tool already provides access to the appropriate ICodeGenerator
instance we mentioned, through the GetCodeWriter() method. We can
also access the Custom Tool Namespace property that appears in the
Property Browser for a file, through the FileNameSpace property.
We’re done with the tool now. We can start using it right away by associating
the XsdCodeGen custom tool with any WXS file. Note that VS.NET
locks the assembly when the tool is run for the first time. Should we want to
keep modifying it and re-compiling, we will have to restart the IDE.
Extensions for codegen can be easily created to leverage the tool, simply by
placing the extension assembly next to the custom tool installation folder, and
adding the extension to the schema as follows:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema
...etc...> <xs:annotation>
<xs:appinfo> <Code xmlns= "http://weblogs.asp.net/cazzu">
<Extension Type="XsdGenerator.Extensions.ArraysToCollectionsExtension, XsdGenerator.Library" />
<Extension Type="XsdGenerator.Extensions.FieldsToPropertiesExtension, XsdGenerator.Library" />
<Extension Type="MyExtensions.DoSomethingCool, MyExtension" />
</Code>
</xs:appinfo>
</xs:annotation>
You'll need those extensions in order to modify the default output that matches the xsd.exe one. Enjoy!
Note: this entry has moved.
Rob Howard wrote the
second part on the provider design pattern they are using in Whidbey, and
how to implement it in v1.x. An interesting reading. First of all, I'm
really amazed at how open these guys are to good feedback from the community.
Rob even dedicated a paragraph named "Extending configuration" that explains
why they chose a NameValueCollection for provider
initialization over an XmlNode, as
I suggested (I'm sure other did too, it's a pretty obvious thing to ask).
I really don't share the feeling that a NameValueCollection is
*sooo much* easier than an XmlNode and its added flexibility, but
at least I understand now why they did so: they expect complex providers to
have their own section handler with all the information they need to work,
which makes perfect sense!
There's a major performance issue in the implementation he suggests for v1.x,
though, that has to do with the actual creation of the specific provider
instance. There's a sort of mess in the naming and wording when this
feature is explained. The article says:
When calling routines on the Membership class, internally it
will always forwards those calls to an instance of the MembershipProvider—first
creating an instance of MembershipProvider using a factory method, Instance(),
and then calling the appropriate base class method on the retrieved instance.
Well, from the naming point of view, if Instance() is employed as a factory
method, it should be called CreateProvider() or something like that, to denote
that an instance will be created each time the method is called. And here comes
the confusion, because Instance() more closely resembles the Singleton pattern
rather than the Factory Method. If you think about it, there's no reason why we
should instantiate a new membership object all the time. Not even the dynamic
nature of providers justify this, because switching providers in the config
file will cause an app restart anyway, loading the appropriate one afterwards.
And this is even more important than a mere wording because instantiating the
provider dynamically requires using reflection. The abbreviated method shown in
the article is:
public static MembershipProvider Instance() {
// Use the cache because the reflection used later is expensive
...etc...
// Load the configuration settings
object[] paramArray = new object[1];
paramArray[0] = membershipProvider.Attributes["connectionString"];
return (MembershipProvider)( ((ConstructorInfo)cache[cacheKey]).Invoke(paramArray) );
}
Note that by caching only the constructor information, you're not earning much
anyways. You end up using reflection at each method call anyway. So, if you
convert this method from a factory method into a singleton, you directly cache
the provider instance you create the first time the method is accessed.
Furthermore, I'd directly move this singleton instance creation to the
Membership (or similar for other provider-enabled features) altogether:
public class Membership
{
static MembershipProvider _instance;
static Membership()
{
// Get the names of the providers
MembershipConfiguration config = MembershipConfiguration.GetConfig();
// Read the configuration specific information for this provider
Provider provider = (Provider) config.Providers[config.DefaultProvider];
// The assembly should be in \bin or GAC
try
{
Type type = Type.GetType( provider.Type );
_instance = (MembershipProvider) Activator.CreateInstance( type );
// Initialize the provider with the attributes.
_instance.Initialize( provider.Name, provider.Attributes );
}
catch (Exception e)
{
throw new Exception("Unable to load provider", e);
}
}
public static bool Validate( string username, string password )
{
return _instance.Validate( username, password );
}
public static void CreateUser( string username, string password )
{
return _instance.CreateUser( username, password );
}
}
You may have noticed that the original method in the article wrongly
calls a special constructor passing a specific hardcoded attribute,
"connectionString":
paramArray[0] = membershipProvider.Attributes["connectionString"];
// Special ctor?!
return (MembershipProvider)( ((ConstructorInfo)cache[cacheKey]).Invoke(paramArray) );
It's obvious that by doing so the Instance() method is no longer
generic, as it's expecting a provider to implement a specific ctor overload and
receive a connection string! What if I have an XmlMembershipProvider?
Given the base ProvideBase class all providers must implement, and
what is explained in the article it makes much more sense to use the ProviderBase.Initialize
method for provider initialization, as I did in the static ctor above:
Type type = Type.GetType( provider.Type );
_instance = (MembershipProvider) Activator.CreateInstance( type );
// Initialize the provider with the attributes.
_instance.Initialize( provider.Name, provider.Attributes );
Anyway, it's good that we're having these discussions. The ASP.NET community
needs these concepts to turn web applications into well architected solutions,
leaving the ASP spaghetti programming style behind once and for all.
Note: this entry has moved.
Half the article is almost a straight copy of topics covered by
Fowler's article. Trivial renaming is done to shadow this fact,
basically replacing Fowler's MovieFinder example with BusinessObject one, which
supposedly should be more attractive. And you only realize this if you read it
entirely, and right at the bottom, find the link to Fowler's work and go read
it (you realize in the first page if you already did, like myself).
He partially cites Fowler:
Martin Fowler's argument against this (Service Locator) pattern is
its inaptitude to adapt to mock objects (for unit testing).
This reference is clearly distorted to make the previous discussion
about Dependency Injection appear as supported and encouraged by Fowler.
Initially, Fowler says:
A common reason people give for preferring dependency injection is
that it makes testing easier. The point here is that to do testing, you need to
easily replace real service implementations with stubs or mocks.
Inmediately after, he states *his* view on the matter:
However
there is really no difference here between dependency injection and service
locator: both are very amenable to stubbing. I suspect this observation comes
from projects where people don't make the effort to ensure that their service
locator can be easily substituted.
Which is absolutely unquestionable.
The article is unnecessarily really hard to follow. The author makes too many
assumptions in order for it to be understandable. For example, he assumes the
reader is familiar with PetShopDNG. He makes a strong assertion like the
following:
To summarize, the Abstract Factory and the dynamic code
instantiation are a poor relation of the IOC approach. Why should
we manage the life cycle of a dependency where an intermediary object could do
it ? On another hand, if some techniques like code generation (AOP) allow to
insert a behavior between client and provider, IOC gives a similar result with
less effort.
Without clarifying a bit of it in the following paragraphs. He also says
When .NET IOC Frameworks will be ready (that's to say in some years
;))...
Well, as I showed
in my previous article, the building blocks are in-place already, if
you follow the ComponentModel architecture.
He shows an example where a class accesses a transaction object by using
Transaction.Current:
public bool Buy(SharesInfo shares)
{
Transaction tx = Transaction.Current;
// Execute against DB with tx
This is a very common approach in .NET, and it appears to be the one Indigo
will be encouraging. The first drawback is that being a static property
call, Transaction must be a specific implementation, not an interface. This was
solved in ASP.NET Whidbey providers by having this class and its static members
be just proxies on a concrete implementation instantiated as a singleton
depending on config. The main drawback, however, is that if many services
are potentially available, having [Service].Current for each of them makes
for a highly descentralized service management. There's no way to administer,
configure and provide all these services in a single place, therefore,
maintainability and testability are hurt.
Note: this entry has moved.
Required reading: Inversion
of Control Containers and the Dependency Injection pattern by
Martin Fowler. If you haven't read it, you will not understand what I'm
talking about, and I'm not fond of reproducing other's work here. It's better if you just read it, it's a very interesting article.
I'd like to analyze Fowler's article in the light of .NET and what we have now
in v1.x. After reading his article, he seems to imply that lightweight
containers are a new concept mainly fuelled by the Java community unsatisfied
with heavyweight EJB containers. It turns out that .NET supported and heavily
used this approach since its very early bits, released back in PDC'00 (July
14th 2000).
The basic building blocks for lightweight containers in .NET live in the
System.ComponentModel namespace. Core interfaces are:
-
IContainer: the main interface implemented by containers in
.NET.
-
IComponent: provides a very concrete definition of what a
component is in this context. It's any class implementing this interface, and
which can therefore participate in .NET containers.
-
ISite/IServiceProvider: the former inherits the
later. It provides the vital link between a component and the container it
lives in (its site), which enables service retrieval by the component. IComponent
has a Site
property of this type.
-
IServiceContainer: a default container for services, that IComponents
can access through the ISite. Not actually required as the IContainer
can store available services through other means.
The relationship between these clases can be depicted as follows:

A typical container/component/service interaction is:
-
Specific container class is created by client code.
-
Container initializes all services that components will have access to
(optionally through an internal
IServiceContainer). Client
can optionally (if the container exposes its own IServiceContainer
) add further services at will.
-
Either the container or client code adds components.
-
Container "sites" these components by setting their
IComponent::Site
property, with an ISite implementation that offers services
that are retrieved from the optional internal IServiceContainer
or another implementation.
-
Client code can access components by name or index through
IContainer
indexer.
-
Components can perform actions requiring services that are retrieved
through
IComponent::Site::GetService(Type). This method is
inherited from IServiceProvider actually.
So, in Fowler's
terms, the IContainer implementation performs Interface Dependency
Injection (through IComponent) of a single dependency, the
Dynamic Service Locator (ISite : IServiceProvider). The former
happens because the IContainer automatically sets the IComponent::Site
property upon receiving the component to add through its IContainer::Add(IComponent)
method. The later is the implementation of IServiceProvider::GetService(Type)
method, which allows dynamic retrieval of services from the container.

Fowler dislikes dynamic service locators because he says they rely on string
keys and are loosely typed. In .NET IServiceProvider, you don't
pass a string key but the actual Type of the service you request. What's more,
the default interface implementation, System.ComponentModel.Design.ServiceContainer,
checks that services published with a certain Type key are actually assignable
to that type. Therefore, it's safe to cast them back upon retrieval. At most
you get a null value from a provider, but never an InvalidCastException.
Following his example so that this is a natural adaptation to the .NET world,
his MovieLister component will look like the following:
public class MovieLister : Component
{
public void Movie[] MoviesDirectedBy( string name )
{
IMovieFinder finder = (IMovieFinder)
GetService( typeof( IMovieFinder ) );
if ( finder == null ) return new Movie[0];
ArrayList all = finder.FindAll();
// Iterate, filter by name and return subset.
}
It's common, that instead of implementing IComponent directly,
concrete components inherit from its built-in default implementation Component,
which provides a GetService shortcut method that also checks that
the Site property is set before requesting a service from it.
Lifecycle of components is handled through three states:
-
IComponent::ctor: at construction time, the component is
still not ready for work, as it can't access services.
-
IComponent::Site { set; }: the component is "sited", therefore
it's fully functional now. At this (overridable) point, components can
further configure themselves, for example by caching a reference to a service
they use frequently:
public class MovieLister : Component, IMovieLister
{
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
// Cache the finder.
this._finder = (IMovieFinder)
GetService( typeof(IMovieFinder) );
}
}
-
IComponent::Dispose: when it's not needed anymore, a component may
be diposed using the IDispose interface inherited by IComponent.
Fowler notes some drawbacks in general with regards to the service locator
approach:
So
the primary issue is for people who are writing code that expects to be used in
applications outside of the control of the writer. In these cases even a
minimal assumption about a Service Locator is a problem.
By
standarizing on
IComponent and
ISite from
System.ComponentModel,
this isn't a problem anymore in .NET. Any component that uses these interfaces
can be hooked into any container, and query services. This doesn't require
dependencies on external products or unproven approaches: .NET uses extensively
this feature.
Since
with an injector you don't have a dependency from a component to the injector,
the component cannot obtain further services from the injector once it's been
configured.
As injection is being done for the service
locator itself (the
ISite : IServiceProvider instance), this isn't
a problem anymore. Further services can be easily requested from it.
A
common reason people give for preferring dependency injection is that it makes
testing easier. The point here is that to do testing, you need to easily
replace real service implementations with stubs or mocks. However there is
really no difference here between dependency injection and service locator:
both are very amenable to stubbing. I suspect this observation comes from
projects where people don't make the effort to ensure that their service
locator can be easily substituted.
I agree completely on this view. Aren't these architectures all about the
ability to dynamically remove dependencies/hook/replace implementations
dynamically? It's obvious to me that if such objective is not
achieved, it's clearly not because injection vs service locator
choice, but an implementation bug. Testing and stubbing with .NET
containers is straightforward as components retrieve services by interface
type, so stub impls. of those interfaces can be plugged into a testing IContainer
implementation without problems.
Note that additionally, the IContainer can expose its internal
IServiceContainer as yet another service, so that a component could
publish a new service for consumption by others:
public class MovieLister : Component, IMovieLister
{
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
// Publish ourselves as new service.
IServiceContainer container = (IServiceContainer)
GetService( typeof(IServiceContainer) );
container.AddService( typeof(IMovieLister), this );
}
}
This combination of IServiceProvider, IContainer and
IComponent is in broad usage TODAY in Win and Web
Forms platforms, as well as design-time and generally the IDE
infrastructure. You're usin them everytime you create a Windows Form, Windows
User Control, WebForm, etc.
Layering Service Containers
One scenario that .NET System.ComponentModel supports and
that hasn't even been discussed by Fowler is that of chained service
containers. Let's say you have a component, sited in a container, that performs
some quite complex functionality. Now, let's say this complex functionality
requires additional services that are provided by a specialized container and
further components. In this case, the "main" component needs to instantiate a
new container and execute further components. Needless to say, these components
may require not only services from this new "child" container but also the
parent one, the one where the "main" component lives.
Stacking service providers at this point is extremely useful. What
you actually need is a Chain of Responsibility pattern where the service
implementation is returned by the first provider in the chain that can respond
to the request for it. This would allow you not only to chain different sets of
services, but also to override implementations from a parent service provider.
This is supported natively in .NET through the ServiceContainer implementation,
and is heavily used in Visual Studio.NET IDE: some services are offered to
components by a specific designer, or a VS package, or the IDE itself. Most
requests for services propagate up the chain if necessary until they reach the
IDE main container.
I've used exactly the same architecture for an upcoming automatic wizard
framework for Shadowfax that acts as a child container inside the
IDE. Some components need execution of yet another lower layer, a
transformation engine that works with code templates to generate code
(among other things), which is also a child container. At this point, the three
layers, IDE, wizard and transformation engine, are chained together, so any
component in the transformation engine, for example, can query services that
are being offered by the IDE itself.
This is an extremely powerful and flexible approach, as you don't have to
build monolythic container but can instead rely on components instantiating
more specialized child containers to perform specific work.
Container Configuration
Of course, any good container should be configurable either programmatically
and through configuration files. Fowler discusses the following with regards to
configuration:
I
often think that people are over-eager to define configuration files. Often a
programming language makes a straightforward and powerful configuration
mechanism. Modern languages can easily compile small assemblers that can be
used to assemble plugins for larger systems. If compilation is a pain, then
there are scripting languages that can work well also.
I agree absolutely. One usual dual config mechanism (XML + API) in .NET is
creating an XSD for the file, get classes generated ready for XML
Serialization, and support config either through the file reference, which is
simply deserialized into the object model generated for the XSD, or through
this object model itself, like so:
public class MyConfigurableContainer : IContainer
{
MyConfigurableContainer(string file) { ... }
MyConfigurableContainer(MyConfigModel config) { ... }
}
However, unless codegen customization is used, this raw XML serialization
model is very poor when it comes to programmatic configuration, as classes only
have parameterless ctors (so all initialization has to be done through property
setters), there's no way to know which properties are required or optional, by
default multi-value properties are arrays instead of typed collections, and so
on.
Non-language
configuration files work well only to the extent they are simple. If they
become complex then it's time to think about using a proper programming
language.
It's very interesting how most people nowadays perceive programmatic
configuration APIs as a drawback over XML config files. I can't really
understand why. With dynamic compilation becoming almost common place (i.e.
ASP.NET v2 model, upcoming XAML, and so on), having a good programmatic API
coupled with full programming language intellisense surely surpasses XML files
in usability and productivity, specially for complex stuff.
It's usually the case (i.e. most of .NET) that after inventing a huge daunting
configuration file format, admin UIs are created to manipulate them (i.e. .NET
Framework Configuration, upcoming ASP.NET v2 admin console, etc.). At this
point you start wondering: if nobody is ever going to touch those files except
from those UIs, which is the advantage of having it in XML? Why don't just have
those UIs generate compiled controllers that programmatically hook and
configure everything? Just imagine the savings in parsing, validating,
loading time... After all, you have to XCopy deploy those configs, just
like the "assemblers" would...
The missing feature?
So, all the plumbing and required interfaces for implementing lightweight
containers in .NET are already in-place. The framework doesn't contain any
class to perform configuration of a container, though. This is not necessarily
a bad thing, as it doesn't force any concrete file format or configuration API,
leaving that to implementers. Creating such feature for an specific container
(such as the Shadowfax Wizard container, or the transformation engine -
code-named T3 for Templated Text Transformations) is almost trivial. Reading
config, loading types, hooking services and components and that's it.
So, once more, we can see that .NET is the pioneer on supposedly "new"
patterns. It's true that this pattern (and many others found throughout the
.NET Framework) don't have enough advertising, and that may be the cause for
their scarse use in .NET application architectures.
In a future post I'll discuss Apache Avalon
and the
Spring Framework, and how they compare to what's built-in .NET.
Update: maybe I should also mention that I've been using this tecnique with excelent results since the initial release of an opensource
XML-based code generator back in Nov-2002
More Posts
Next page »