Does MEF lend any value to the Singleton pattern?
I am working on an MEF project to discover usage and implementation techniques. My first phase of discovery is to implement a dynamically configurable and centralized data controller. One way to customize behavior is to inherit a class I provide that enforces a singularity rule. While the Singleton pattern is much maligned in it's use, I may have found an implementation that could validate, to some degree, the pattern's struggling existence.
The Situation
Suppose a data control module (DataController) imported by the Host is intended to supply a common conduit to databases on request by sibling modules. I only need one DataController and to be composed as a module, DataController must implement IDataController. Implementation of DataProvider as the base class is purely optional; howev开发者_高级运维er, derivation from DataProvider will require some additional handling.
The Observations
Gathering the facts:
A static class cannot implement or extend abstract classes or interfaces. This fact alone eliminates the use of a static class to ensure a singular existence of a DataController.
A DataController implementing the Singleton pattern would ensure a singular existence per application domain. There is no restriction on the DataController; allowed to inherit the required interface to be imported and composed in the Host.
Given derivation of DataController, the standard implementation for the Singleton pattern may prove to be challenging in same cases. The proposed data library provides both publicly accessible classes: IDataController, and an abstract DataProvider. To ensure a single instance of the derived DataController, the implementation will require some deviation from the norm.
The Solution
At this point, the solution seems clear. Implementation of the Singleton pattern by the DataHandler base class. I am not naive enough to think that there are other ways I could do this. But here is my rough expectations on how to implement the pattern:
// DataLibrary referenced by Host
public interface IDataController
{
IDataController Start();
DbConnection CreateConnection<TDbConnection>(params string[] args)
where TDbConnection : DbConnection, IDbConnection;
}
public abstract class DataProvider
{
// singleton implementation
private static IDataController dcInstance;
protected static IDataController Instance
{
get{ return dcInstance; }
}
// ========================
abstract IDataController CreateController();
protected IDataController instanceController<TDataController>()
where TDataController : IDataController, new()
{
return new TDataController ();
}
}
// references DataLibrary
[Export(typeof(IDataController))]
public class DataController : DataProvider, IDataController
{
public IDataController Start()
{
return CreateController();
}
protected override IDataController CreateController()
{
return instanceController<DataController>();
}
public SqlConnection CreateConnection(params string[] args)
{
// instance and return new SqlConnection
}
}
Keep in mind that I have been working this out - read, theorizing - and have not completed the implementation. There will most likely be some updates as I debug any issues.
Obviously, this implementation is only enforced if the DataController module inherits the abstract base class, DataProvider. Therefore, it stands to reason that we should enforce a rule of singularity to avoid abuse or misuse if the developer opts to derive a DataController from DataProvider.
All that said, I am curious if there is a more acceptable, or practical implementation than what I have devised. And, I begin to question if the Singleton pattern is the right choice. With the Singleton pattern's much maligned existence (and, for the most part, rightfully so), I should, therefore, question my choice.
Is there a more practical implementation to meet my requirements? *Is this the right implementation of the Singleton pattern in this case?* Does this implementation actually lend any value to the pattern's existence?
If you want to enforce the fact that only a single instance of a class exists in the container, then you can just set the "shared" part creation policy:
[Export(typeof(IDataController))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class DataController : IDataController
{
...
}
Each part importing IDataController
will then receive the same instance. Note that this already the default behavior in MEF if you specify no part creation policy at the import or export side.
You should not build "singletonness" into a class. Whether something is a singleton is part of the component metadata or the configuration of the container. Other dependency injection containers follow the same approach. For example, in autofac you declare something as being a singleton like this:
builder.Register(c => new DataController())
.As<IDataController>().SingleInstance();
Unless you have more implementation code that all derived classes from DataProvider would share, you might want to simply do away with your abstract class. This implementation guarantees thread-safey and uses lazy construction without the use of locks. However, requires .NET 4.
public interface IDataController
{
DbConnection CreateConnection<TDbConnection>(params string[] args)
where TDbConnection : DbConnection, IDbConnection;
}
[Export(typeof(IDataController))]
public class DataController : IDataController
{
// singleton implementation
private static volatile Lazy<IDataController> _ControllerInstance = new Lazy<IDataController>(() => new DataController());
public static IDataController ControllerInstance
{
get { return _ControllerInstance.Value; }
}
public DbConnection CreateConnection<TDbConnection>(params string[] args)
where TDbConnection : DbConnection, IDbConnection
{
throw new NotImplementedException();
}
}
精彩评论