Saturday, January 30, 2010

No you really don't need that singleton (Db40 with StructureMap)

OK, really , StructureMap ROCKS.

So, we've managed to slip in StructureMap at work, and I decided to play a little bit at home to get better using it. Not that it's tough at all, I just wanted to get a little more experience under my belt so I could spend time at work doing work, not mucking with my IOC.

So, I've already blogged about the beauty of Db40. I'm thinking about starting some silly little side project, and I wanted to use db40 as an embedded db. So, db40 grabs a handle on the file and locks it. You must have some kinda singleton thing going on, since only one instance of IObjectServer can hit the file at a time.

The singleton pattern is the devils spawn. I despise it! What to do???First of all, I wrote a quick littler Db40Server object that just wraps up creating the object. It is NOT singleton.

using Db4objects.Db4o;

namespace Ellemy.Data
{
public class Db4oServer
{
private readonly string _pathToDbFile;
private readonly int _port;

public Db4oServer(string pathToDbFile,int port,bool autoStrart)
{
_pathToDbFile = pathToDbFile;
_port = port;
if(autoStrart)
Start();
}

/// <summary>
///
The server
/// </summary>
public IObjectServer Server{get; private set;}
/// <summary>
///
Starts the Server
/// </summary>
public virtual void Start()
{
Server = Db4oFactory.OpenServer(_pathToDbFile, _port);
}
/// <summary>
///
Stops the Server
/// </summary>
public virtual void Stop()
{
Server.Close();
}
public IObjectContainer GetClient()
{
return Server.OpenClient();
}
}
}



Simple enough right? Usually someone would ugly this up by adding singleton implementation right to the object (probably have to couple it up to some app settings to to get those ctor dependencies supplied as well. I didn't do that. I just made the functionality I needed. If we where using the Singleton pattern here, we'd do something like this.
Db40Server.Instance.GetClient()
but I didn't do that... I let StructureMap do that work.
Here's what I did.




namespace Dojo.Business.Bootstrapping
{
public class DojoBusinessRegistry:Registry
{
public DojoBusinessRegistry()
{
Scan(registry =>
{
registry.AssemblyContainingType(typeof (IPerson));
registry.WithDefaultConventions();
});

ForSingletonOf<Db4oServer>().Use<Db4oServer>()
.Ctor<String>("pathToDbFile").Is("Tests.bin") //.EqualToAppSetting("Db4oServerFile")
.Ctor<int>("port").Is(0)//.EqualToAppSetting("Db4oServerPort")
.Ctor<bool>().Is(true)
;

For<IObjectContainer>().Use(context => context.GetInstance<Db4oServer>().GetClient());
For<IObjectServer>().Use(context => context.GetInstance<Db4oServer>().Server);
}

}



What's going on here? First of all, the Scan stuff, just does uses naming conventions to figure out how to resolve dependencies.

No issues there.


But check out the ForSingletonOf line. It tells SM that anytime someone asks for a Db40Server, return the same instance. All the ugly singleton stuff is gone (or in the guts of SM somewhere). Having in SM guts is fine by me, because I can still test the heck outta my code, and inject dependencies as I normally would.
But does it work?


First I wrote this test.



[TestFixture]
public class Registry_Tests
{
[TestFixtureSetUp]
public void StartTest()
{
var container = new Container(registry =>
{
registry.AddRegistry(new DojoBusinessRegistry());

});
ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(container));
}
[Test]
public void Config_is_valid()
{
ObjectFactory.AssertConfigurationIsValid();
}



 



That passed fine. Now not because I don't think JM got it right, but because I'm not sure I know what the hell I'm doing, I wrote this test.




[Test]
public void Db4oServer_is_a_singleton_because_Structure_Map_kicks_ass()
{
var client1 = (IObjectContainer)ServiceLocator.Current.GetInstance(typeof (IObjectContainer));

var client2 = (IObjectContainer)ServiceLocator.Current.GetInstance(typeof(IObjectContainer));

var server = ServiceLocator.Current.GetInstance(typeof (IObjectServer));
var server2 = ServiceLocator.Current.GetInstance(typeof (IObjectServer));

server.ShouldEqual(server2);
}



Note that there's no Assertion on the client1, client2 stuff. The reason I did that was because if we got a new instance of Db40Server, we'd get an exception because the file would be locked by the first instance of Db4oServer when the 2nd one tried to open the file.



Then, just because I like wasting time, I checked that 2 instances of resolved IObjectServer are the same instance.



Anyway, in my humble opinion, this totally rocks... I can quit using that horrendous pattern but still control how instances are created. Thanks a lot Miller!



Comments welcome!

11 comments:

  1. Hey! I really appreciated your point of view! I work with DB4O for about 2 years and the big problem i´ve found is when i use an IIS instance with more than 1 worker per Application. The file is locked by one thread and i get some errors sometimes... I think there is no way to solve this problem unless i create a simple .exe server that locks the file and open the DB4O server at some port and then use ASP.NET to connect in my server instance through sockets ;) Am i correct?

    ReplyDelete
  2. Really, all you'd need to do is on the Start method of Db40Server, just do a GrantAccess(username,password).

    Then (as long as your IIS app was running), you could connect with other apps using Db4oFactory.OpenClient(host,port,username,password)

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Nice post. Some information can also be found at

    http://tiny.cc/gr8xy

    ReplyDelete
  5. I really appreciate this post. I’ve been looking all over for this! Thank goodness I found it on Bing. You’ve made my day! Thx again!
    python course institute in bangalore
    python Course in bangalore
    python training institute in bangalore

    ReplyDelete
  6. I really appreciate this post. I’ve been looking all over for this! Thank goodness.web design company in velachery

    ReplyDelete
  7. The article is so informative. This is more helpful thanks for your information really good and very nice web design company in velachery

    ReplyDelete
  8. Wonderful to visit your websites, This sites are fully filled with full of Information's. I perceived a lots of knowledge from this Articles.keep it up!!!

    android training in chennai

    android online training in chennai

    android training in bangalore

    android training in hyderabad

    android Training in coimbatore

    android training

    android online training

    ReplyDelete
  9. Grammarly key Premium is a paid upgrade that offers over 400 types of checks and features. It checks for grammatical errors, provides vocabulary . https://cyberspc.com/grammarly-premium-crack/

    ReplyDelete