NHibernate Starter Kit Enhanced

by Ben Hart 15. November 2008 05:32

I've spent some time today enhancing the NHibernate Starter Kit. My previous post describes what the kit is about in greater detail, so please give it a read. To recap, though, the kit is intended to be a means to get started with NHibernate within minutes. Once installed you'll have a new project template within Visual Studio that contains all NHibernate binaries, a simple domain project demonstrating some common mappings, a console application that generates a database and some test data, and some example unit tests that tie it all together.

It is not intended to be a demonstration of advanced features of NHibernate, nor a demonstration of any best practices. Once this kit has got you started there are many better resources out there to teach you those. I've intentionally kept things as simple as possible.

A common objection to NHibernate is that it's too complicated. I want this kit to prove that objection wrong.

So what's changed?

I wasn't too delighted about the PersistentEntity class to begin with. I didn't want anyone to assume that this was a requirement, nor did I want to have to create an enterprise ready class like that found in the S#arp Architecture. Having a base class for entities in NHibernate does help though, so I tried to keep it as simple as possible.

Unfortunately my class had some issues that would result in confusing bugs. The previous override of object.Equals() looked more or less like this:

PersistentEntity other = obj as PersistentEntity;
 
if (this == other)
{
    return true;
}
 
if (other == null)
{
    return false;
}
 
return this.Id == other.Id;

This was problematic since two transient entities (newly created, and not yet saved to the database) would evaluate as equal. The id of the entity is only assigned once the object has been saved, and thus that last statement would be true for any two unsaved entities! "Thanks for that great starter kit, Ben..."

The next problem was with the naive override of object.GetHashCode():

public override int GetHashCode()
{
    return Id;
}

This works for many situations, but falls flat in others. Firstly (and most seriously), this means that two transient entities will have the same hash code, and once saved they will suddenly have a different hash! Secondly persistent entities of different types have the same id, they too will both have the same hash code.

The default mapping used a hilo generator for id's. In a nutshell, hilo uses a sequence of unique ids to assign to entities as they are saved. The sequence is tracked using a separate table in the database, and this results in every entity being given a unique id. I like hilo since it gives the flexibility of guids (don't have to worry about the autoincrement quirks of various databases, for one, nor the constraint of hitting the database to get an id), with the usability of integers (if you ever need to manage the database by hand integers are a lot easier to work with than guids). If anyone changed the mapping away from hilo to one that gave different types the same id, GetHashCode() would start causing problems.

So I've reluctantly changed the id of the base class from an integer to an guid. PersistentEntity now has the following implementations (courtesy of Gabriel Schenker's good article up on nhforge):

public override bool Equals(object obj)
{
    PersistentEntity other = obj as PersistentEntity;
 
    if (this == other)
    {
        return true;
    }
 
    if (other == null)
    {
        return false;
    }
    //Transient entities will both have the same id, and as such we must check for reference equality.
    if (this.Id == Guid.Empty || other.Id == Guid.Empty)
    {
        return ReferenceEquals(this, other);
    }
 
    return this.Id == other.Id;
}
 
public override int GetHashCode()
{
    // Once we have a hash code we'll never change it
    if (_oldHashCode.HasValue)
    {
        return _oldHashCode.Value;
    }
 
    // When this instance is transient, we use the base GetHashCode()
    // and remember it, so an instance can NEVER change its hash code.
    if (Id == Guid.Empty)
    {
        _oldHashCode = base.GetHashCode();
        return _oldHashCode.Value;
    }
 
    return Id.GetHashCode();
}

This was the right choice. It's kept PersistentEntity as simple as possible, at the small cost of making some manual DBA work a little harder.

Some more mappings

In the interests of getting started more quickly, I've added some more entities and mappings to the starter kit.

Domain

I've thrown together the classes as seen on the left. Again nothing fancy, just a means to illustrate common mappings.

A venue has a one-to-many to courses, which is mapped to cascade="all".

A course has a many-to-one back to the venue, and well as a many-to-many to the students in the course.

A student has a many-to-many to courses.

There are some simple tests that verify some of this behaviour. By no means conclusive, but enough to get started with.

Picking up the theme yet?

 

I've also realised that the template will not work without some effort in Visual Studio 2005. I might address this in future, but for now have limited the vsi to only install to Visual Studio 2008.

To remove the previous version, simply delete the old NHibernateStarterKit.zip file from "[My Documents]\Visual Studio 2008\Templates\ProjectTemplates\Visual C#", and the corresponding 2005 if applicable.

As before, if you have any suggestions or problems, please contact me any which way you please.

NHibernateStarterKit.vsi (738.18 kb)

 

Technorati Tags: ,,,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

C# | NHibernate

Generic Variance Baby!

by Ben Hart 13. November 2008 09:40

I've been pretty busy lately. The day job is getting interesting, as are a few side projects. A lot has happened the last few weeks, both in my world, and the larger .NET'o'sphere. I haven't had much time to write to this blog, but I've made an effort to keep up with others.

What I'm still most excited by (yes, it's been a few weeks of heightened heart-rate) is the introduction of generic variance in C#. I don't care to admit how many times I've written something along the lines of the following:

public void DoSomething()
{
    IList<Child> children = new List<Child>();
    IList<Parent> parents = children;
}
class Parent{}
class Child : Parent{}

The above invalidity is obvious (after the first time, at least), but more subtle variations continue to catch me (lambdas spring to mind). It seems so intuitive to be able to assign a generic type of a subclass to that of a superclass. I've occasionally refactored to a point relying on something along these lines, before pulling up the handbrake (or is that the shift to reverse?) when the compiler reminds me it's not valid.

GVAvatarBut no longer. Well, at least, soon no longer... C# 4.0 introduces generic variance.

I'm not going to add anything to what's already been written. My only contribution is my little friend on the left here - lovingly created to celebrate the occasion. He represents the wonderful diversity in code that will soon be possible. (Except the t-shirt. That's chosen because a friend of mine played with My Little Ponies as a boy, so that's for him.)

If you want to learn more about generic variance, and be casually able to drop the terms covariant and contravariant into the standup tomorrow morning, Nate Kohari has a great summary in this post. Alternatively, download the CTP of VS2010, and shake up that invariance...

Technorati Tags: ,,,,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

.NET | C#

SPAM!!!

by Ben Hart 11. November 2008 09:19

I feel like my blog has come of age.

While I've been too busy on personal life to write even so much as an apology post, some sneaky bot managed to get some medication advertising as a comment on an old post.

Nice. And here I was thinking my readers were getting a rise from my good writing alone...

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Blogging

Repeat after me, "check yourself before giving bad advice"

by Ben Hart 3. November 2008 06:59

Some time ago I started to give some advice to someone on the alt.net mailing list regarding converting an ASP.NET 1.1 web site to ASP.NET 2.0. I'd gone through this some time before that, and thought I could share some pointers. Another reader had suggested he skip 2.0, and bring it into 3.5, which made sense.

I don't really use the Web Site template (or is it a model?). Having come from the early days, Web Application was a lot more familiar, and I (thankfully) bridged into 2.0 after the initial release of the separate installer. I've since played around a little with the Web Site, but the Web Application will remain the only one I'd choose. Not sure if that's resistance to change (since I've never really taken the time to understand the real differences) or just my natural knack to pick the best model.

To cut a long story short, I couldn't understand the problems he was having converting the now Web Site (he'd rolled into VS 2008) into a Web Application. Dude, just right-click the site, and select "Convert to Web Application". No, really, dude, while it was a while ago, I've been through this. Just right-click the site, and select "Convert to Web Application". This carried on for a few more mails, before I presumed he'd finally seen the context menu item. It turns out his silence was more likely his giving up in frustration (or disgust).

I'm currently playing around with the Flixon site generator (which has a home elsewhere, the forum announcement seems to pip it in Google page rank, though), which generates a ASP.NET Web Site. Being more comfortable with Web Applications (and not wanting to learn how to reference Web Site code from a separate assembly for testing), I thought I'd just convert it to a Web Application. So just right-click, and select "Convert to Web Application". Hmm. Ok. Just right-click and select "Convert to Web Application". Hmmmmmmmm, that's weird. That option doesn't exist.

Turns out you can only convert a Web Application to a Web Application (as ridiculous as that might sound). The first step is to copy and paste all files from the Web Site to a freshly created Web Application. The rest of the steps are best described by Mohamed Meligy in this post. I think (but I'm not certain) that the same applies to 2.0, but who knows.

Sorry about the bad advice, guy. Next time I promise I'll check before harping on.

Technorati Tags: ,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

.NET | ASP.NET

IEC Website WTF

by Ben Hart 2. November 2008 02:02

Here in sunny South Africa we're also gearing up for elections. Granted, not quite with the international significance as those over in the US, but it could be argued that my local government is just as likely to affect my day to day existence.

I'm obviously registered as a voter, but I wanted to check on Friday where, having recently moved district. Conveniently our local Independent Electoral Commission has a site you can enter your details, and check your registration status. I popped on over, and got this message:

IECHome

I haven't seen that for quite some time. Brought me right back to the turn of the millennium. It's clearly been updated recently (notice the mention of Chrome), but hell's teeth. I luckily use windows and have the option of IE (in fairness, they do mention IE 4 and greater, so in theory some adventurous mac users could have a go at it). But we're in a country whose government has openly embraced open source software. We're the birthplace and oftentimes home of he who is called Mark, arguably the one most likely to take Linux to the masses.

I took a look at the output html, and, in and amongst all the scary ASP.NET-and-Web-Forms-Designer-bastard-child HTML I think I found the reason non-IE browsers are not supported, called on load of the body:

<script language="vbscript"> 
Sub ShowMsg()
        
    if len(trim("")) > 1 then 
        msgbox("")
    end if 
    
    
    
End sub 
</script>

Now I'm no expert in vbscript (I missed that bus, and its route seems to have been cancelled), but my powers of deduction tell me that this one doesn't do much. Did anyone think to check the output html? Did anyone peruse the code that resulted in this? Perhaps a code review is too much to ask for, but having a developer on the team who leaves this in all the way to production is more a liability than an asset.

Come on guys, sort it out. This is important. I'm happy to lend a hand - drop me a line, I'll happily donate a few hours a week.

Technorati Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

.NET | Life | South Africa

NHibernate Starter Kit

by Ben Hart 21. October 2008 11:28

It took me a while to get into NHibernate. I knew it was an awesome tool, with an active community, but it always seemed simpler to just roll-my-own before taking the time to learn a proper ORM. I think part of the problem was accessibility. Coming from a Microsoft development world one gets used to running installers and adding templates to try new products out.

Despite the wealth of tutorials, screen casts, a pretty thorough reference, easily available source code, and a vibrant community, many people still have the impression that NHibernate is hard. It seems that those extra 4 steps to get started (download the latest binary package, extract and add references to NHibernate, add "nhibernate.cfg.xml", and add entity mappings) is enough to deter people, myself included for quite some time.

But why go through this?

Once I broke through the initial resistance, I'm not turning back. It's a great ORM, the first that's entered my world that gets me close to modelling my domain how I'd like it, without much compromise. The ability to throw together some C# classes, test these until I'm happy with their shape and behaviour, and then have a database generated which supports their persistence has liberated me. Most ORMs in the .NET space place too much emphasis on the database, and the temptation to use the tools and wizards has me back in SQL Server Management Studio far more often than I'd like. While I'm not a zealot (at least I try to mention the alternatives when I encourage NHibernate), I do feel more people need this liberation from the database. As a great man once said,

I have a dream that one day the majority of .NET developers will emancipate themselves from the binds of an inherently cumbersome environment.

I have a dream that one day these developers will join me in dropping the notion that "business logic classes" are simply filters between the user interface and the database.

I have a dream that one day even the most hardened of stored procedure advocates will concede that the ability to refactor with ease and unit test in isolation is compelling enough a reason to forever rid themselves of TSQL and its ilk.

Today I took a small step towards that dream.

Nah, I'm just lazy.

I often want to create a quick NHibernate based application, generally for a proof of concept, and I'm tired of finding those dll's on my bloated drive. I'm also tired of copying and pasting the config file, and the plumbing I need to generate a schema.

So I thought I'd save myself some time and create a Visual Studio template that does this for me. In fairness it was a little more of an adventure than I was bargaining for, but in about 20 proofs of concept I should break even. It was interesting to get to know Visual Studio templates, and some of the limitations thereof, so never any time wasted.

When installed (I created a .vsi which is just a zip in disguise, so feel free to unzip it and butcher) a new project template becomes available under Visual C#, "NHibernate Starter Kit".

VSProjects

NHSKSolutionAdding this project creates a solution with three projects, Domain, SchemaGenerator, and Tests. Domain contains a simple base class for entities (by no means required for NHibernate), and an example entity and corresponding mapping.

SchemaGenerator is a console application that uses these mappings to generate a database schema and insert some test data. Rather than create the database from scratch, it assumes that the database as defined in the App.config has already been created.

Tests has an example integration test: creating a new entity, saving to the database, and retrieving it.

I've scattered a few comments around the code for luck.

One of the limitations of a multi-project template is the ability to include files that aren't in a project. Typically we'd all have a lib or dependencies folder that contains common 3rd party libraries, and that was my initial intention. Rather than go the whole hog to get this (which involved implementing IWizard in a class library, signing, GAC'ing, jumping around in frustration), I've cheated by placing them in the bin folder, included in the project. Not ideal, but works fine. (Incidentally, the template includes the latest released version of NHibernate, 4.0.1, and NUnit 2.4.8. I'm actually not sure whether redistributing these is a problem, so drop me a line if I need to take down).

In theory, you should simply be able to install the template, create a database (simply an empty database), match this to the connection string, run the schema generator and marvel how that table is created for you. You might even want to add a few properties to MyEntity, jack out the corresponding mapping, run that schema generator, and marvel some more how the table changes without that pesky designer. Run the NUnit test for luck too.

But that's theory, at this stage all I know is that it works on my machine (Visual Studio 2008, SQL Server 2005 Express). Let me know if it works on yours.

I intend to enhance the kit over time, adding more to the sample entity as a reference for mappings, demonstrating common patterns, and so on.

In the meantime, download the kit, and free yourself from the database.

Technorati Tags:

NHibernateStarterKit.vsi (737.61 kb)

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

NHibernate

Quitting the game

by Ben Hart 18. October 2008 02:25

I subscribe to Tim Ferris's blog for a fresh perspective and different read to my usual development related blogs. He writes on a variety of topics, summarised, I suppose, to living the best life you can with the short time you have allotted. He's the author of The 4-Hour Workweek, and has often had me questioning the value of the traditional work environment, and the games we play therein.

His most recent post (in fairness a cross-post, I didn't bother finding the source) is great. In summary, it quotes Andrew Lahde's (a hedge fund manager) farewell letter, calling it quits after reaping the rewards of the current financial 'crisis' (in which he reaped an 866% return in one year).

I will no longer manage money for other people or institutions. I have enough of my own wealth to manage. Some people, who think they have arrived at a reasonable estimate of my net worth, might be surprised that I would call it quits with such a small war chest. That is fine; I am content with my rewards. Moreover, I will let others try to amass nine, ten or eleven figure net worths. Meanwhile, their lives suck. Appointments back to back, booked solid for the next three months, they look forward to their two week vacation in January during which they will likely be glued to their Blackberries or other such devices. What is the point? They will all be forgotten in fifty years anyway. Steve Balmer, Steven Cohen, and Larry Ellison will all be forgotten. I do not understand the legacy thing. Nearly everyone will be forgotten. Give up on leaving your mark. Throw the Blackberry away and enjoy life.

Sage advice.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Life

Mocking ASP.NET MVC HtmlHelper extension methods using Moq

by Ben Hart 17. October 2008 00:43

I'm in the process of upgrading our ASP.NET MVC Preview 5 app to Beta. Been quite painless so far, but hit a snag with an unmentioned change to the signature of the ViewContext class.

We've followed many by monkeypatching the HtmlHelper class further, extending it to a variety of uses. Obviously we need to test these extensions, so need a reference to an HtmlHelper instance.  We used to have the following helper method to get an HtmlHelper object:

public HtmlHelper CreateHtmlHelper(ViewDataDictionary viewData)
{
    var sw = new StringWriter();
    var rd = new RouteData();
    var tc = new TestController();
    var td = new TempDataDictionary();
    var tv = new TestView();
    var req = new HttpRequest("", "http://localhost/", "");
    var res = new HttpResponse(sw);
    var hc = new HttpContext(req, res);
    var hcw = new HttpContextWrapper(hc);
    var rc = new RequestContext(hcw, rd);
    var cc = new ControllerContext(rc, tc);
    var vc = new ViewContext(cc, "View", viewData, td);
 
    return new HtmlHelper(vc, tv);
}

I'd been aware of this method (had stumbled across it when certain tests seemed to be taking longer than they should), thought it looked pretty dodgy, ignored it, and added it to the growing list of technical debt. "Well it isn't broken..." I've joked with my teammate responsible about adding it the daily wtf, but we've both agreed we've both seen worse.

The beta of ASP.NET MVC has changed the ViewContext constructor to now require an IView and not a view name string, which fortunately broke the above, allowing me to reclaim some debt. We're using Moq, which allowed the following, much simpler, method.

public static HtmlHelper CreateHtmlHelper(ViewDataDictionary viewData)
{
    var mockViewContext = new Mock<ViewContext>(new Mock<HttpContextBase>().Object, 
                                                    new RouteData(), 
                                                    new Mock<ControllerBase>().Object, 
                                                    new Mock<IView>().Object, 
                                                    viewData,
                                                    new TempDataDictionary());
 
    var mockViewDataContainer = new Mock<IViewDataContainer>();
    mockViewDataContainer.Expect(v => v.ViewData).Returns(viewData);
 
    return new HtmlHelper(mockViewContext.Object, mockViewDataContainer.Object);
}

The HtmlHelper requires a ViewContext, and an IViewDataContainer. Mocking the ViewContext is clearly the most work, but not that dificult. In certain circumstances the HtmlHelper needs to get the ViewDataDictionary off the IViewDataContainer, so the Mock of the container above returns the one we fill with test data, which is passed into the method. This might not be necessary, depending on your situation.

If your helper extensions need more from the HttpContext (such as the Request), obviously you'll need to set those expectations too, ours currently don't. Ben Hall has an similar implementation using RhinoMocks (which would need to be changed to cater for the IView requirement), which sets expectations allowing the Resolve method to be used.

Update: I've since realised that one doesn't really 'Mock' an extension method (or any method for that matter), so don't call me out on the title!

Technorati Tags: ,,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

ASP.NET MVC | TDD

It's a whole new world in here

by Ben Hart 15. October 2008 09:52

I'm a late adopter of the whole blogging thing. At times I feel like a late developer at school, finding excuses to be last showering after phys ed. Thankfully I'm a developer who's focused on web technologies for a number of years now, making the technicalities a lot more accessible. But still, hosting a site publicly has introduced a new world to me.

Obviously I want what I write to be read. There are many things I can do by myself that have more obvious immediate reward. I've decided to devote these chunks of time for various reasons, lowest on the list is a misplaced geek narcissism. While I'm in this for the long term, knowing people are reading makes it immediately worthwhile. But getting people to read at all is the challenge.

This has given me a baptism of fire into the world of analytics, submission, optimisation and all things that offend my modesty and original intent. I was vaguely aware that there were professional bloggers out there (Jeff Atwood, for one, but doubtless others I read), but I must confess sites like ProBlogger have been an eye opener.

I've recently added Google AdSense, but that'll likely end shortly. After two days and a few hundred impressions I'm sitting on a balance of $0.00, so when I hit infinity I'll still get nothing back. I think Justin Etheridge summed it up best, "You're annoying your readers, and you're not making any money". The AdSense experiment has given me insight into the sheer volumes that the pros must need to sustain themselves, though, and a better understanding of the origin of spam.

Besides, I don't want to turn this into a profession. I enjoy my job as a developer, my learning and experience that I blog about can only ever stem from this. I feel I'd have sold out if I construct posts targeting keywords, with appropriate density, of course. I'm not prepared to use Google keyword tools to find the long tail, and produce content that will draw them in. The thought of paid reviews and listings is bizarre, as is reciprocal (paid?) linking.

I've recently sent my blog out to numerous free submission sites, though. I poured myself a single malt, rubbed some anti-inflammatory into the wrists for the carpal tunnel, googled for "Blog Submission", and hit them hard. I've never typed my own name so many times. I felt like my parents must have when Christmas card mass mailing was still vogue. I'll likely draw a visitor here or there, though, so that'll be nice. Some are actually really decent, and I plan to spend some time browsing their directories.

I've had the links to dzone and DotNetKicks for a while, knowing that they're both great sites that could draw traffic, but mainly since BlogEngine had them here by default. Only recently I noticed that other bloggers submitted their own content (I had assumed that was faux pas, so avoided it). I followed their lead and submitted some of my own posts. Not everything, mind you, just the few I think are the more valuable. My ever-watched Google analytics has demonstrated the numbers of readers this had drawn, so I'll likely continue. To those of you who've followed the links, welcome, and please let me know if this is, indeed, reprehensible.

This is, after all, a whole new world to me...

Technorati Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Blogging

Dependency Inversion for Dummies: Factories and Service Locators

by Ben Hart 12. October 2008 08:09

In my previous post on dependency inversion I elaborated on constructor injection, the injecting of objects a classes depends on into said class's constructor. We saw that this injection both made the dependency more explicit, as well as going some way towards revealing the class with the dependency's purpose. I had stated that explicitly declaring these dependencies makes construction of the object more complex, and had the risk that you end up repeating much code every time you construct a class. I took some hints from the Hollywood TV Series Scriptwriter's Bible by leaving off with a cliffhanger (my way of encouraging Kiefer to roll out the next season of 24 on schedule)... "join me next time when we look at some creational patterns that solve this". And I know you've been hanging.

Creational Patterns

A cursory glance over sites that list design patterns shows that they're generally grouped into categories. These likely hark to the original groupings in the GOF book: creational, structural and behavioural. The book defines creational patterns as being those that:

"...abstract the instantiation process. They help us make a system independent of how its objects are created, composed, and represented."

The book illustrates 4 such patterns: Abstract Factory, Builder, Factory Method, Prototype and Singleton, some competing, many quite tailored to specific scenarios. Common to all, though, is centralisation of object creation. None of these patterns really fit our quite simple case, though, but quoting the GOF gives me some geek cred, so deal with it.

First a few modifications to our code

Remember that in the last post our unremarkable TagService did little more than than filter tags from a data store to be unique. It had a dependency on a concrete data store, which we effectively eliminated. We were stuck in a situation where we knew we needed an instance of a class, we just didn't want to build it in our code. While waxing lyrical about programming to an interface and not an implementation, more astute readers would have picked up the most obvious violation of this, within the console class itself. The first step of today's exercise is thus extracting an ITagService interface.

class Program
{
    static void Main(string[] args)
    {
        ITagService tagService = new UniqueTagService(new TagDatabase());
        foreach (string tag in tagService.GetUniqueTags())
        {
            Console.WriteLine(tag);
        }
        Console.ReadLine();
    }
}
 
public interface ITagService
{
    IList<string> GetUniqueTags();
    int TagCount();
}
 
public class UniqueTagService : ITagService
{
    private ITagData _db;
 
    public UniqueTagService(ITagData database)
    {
        _db = database;
    }
 
    public IList<string> GetUniqueTags()
    {
        return new List<string>(_db.GetTags().Distinct());
    }
 
    public int TagCount()
    {
        return _db.GetTags().Length;
    }
}
 
public interface ITagData
{
    string[] GetTags();
}
 
public class TagDatabase : ITagData
{
    private string[] _tags = new[] { "Word1", "Word2", "Word3", "Word1" };
 
    public string[] GetTags()
    {
        return _tags;
    }
}

Another Interface?

Better believe it. We don't want our client code (the console app) to depend directly on a concrete tag service. The interface defines the functionality we require, gluing the concrete service with the console app just creates a further dependency, one we'll regret when the inevitable changes come through.

Notice, also, that the tag service has been renamed. Its function is to draw tags from a database, and filter them to be unique. Having classes and members that are aptly named is the single best convention we can adopt, and saves having to insist on developers commenting their code (which is about as effective as herding cats).

The dependency is still there

Exactly. The console class requires the tag service, and still constructs it when required. If we continued on this path, we'd soon have a number of locations that cement that same dependency. A number of stinky dependencies. Yuck.

Simple Factory

Probably the simplest means we could improve this is through a factory. Not the abstract factory the GOF defined (which would allow us to construct and return different tag services according to need), but a plain old simple factory (to coin an acronym, POSF), a class whose purpose is to build an object.

class Program
{
    static void Main(string[] args)
    {
        ITagService tagService = TagServiceFactory.BuildTagService();
        foreach (string tag in tagService.GetUniqueTags())
        {
            Console.WriteLine(tag);
        }
        Console.ReadLine();
    }    
}
 
public static class TagServiceFactory
{
    public static ITagService BuildTagService()
    {
        return new UniqueTagService(new TagDatabase());
    }
}

In the above code we've simply created a static class with a method that constructs and returns a tag service when requested. The main advantage here is that we've encapsulated the creation of the tag service, and have centralised it. Other code that might require the service can request one, without knowing all the gritty details of the concrete class, and required dependencies. Should we need to change the implementation of ITagService (for example, change the data store), we need only change it in one location.

Service Locator

The above implementation is pretty simple, but has already greatly improved our code. A potential downside would be the explosion of a number of factories, one for each requested type. A simpler scenario will be a centralised service locator, a one-stop-shop from which we can request a type, and receive an object. DI containers do just this, but let's take a look at a pretty naive implementation of our own.

public static class ServiceLocator
{
    public static T GetInstance<T>()
    {
        return (T) GetInstance(typeof(T));
    }
    public static object GetInstance(Type type)
    {
        return GetInstance(type.Name);
    }
    public static object GetInstance(string name)
    {
        switch (name)
        {
            case "ITagService":
                return new UniqueTagService(new TagDatabase());
        }
        throw new ArgumentOutOfRangeException("name", string.Format("{0} is not a registered type.", name));
    }
}

While not so pretty, this simple class allows us a centralised and flexible means to register and construct services. A real service locator would likely construct the classes dynamically using reflection, and hopefully do some autowiring of dependencies, with some caching for good measure. This one serves to illustrate the concept, though.

Client code could choose which method they'd like to call, but having the generic saves some casting. Our console app uses the service locator with this line of code:

ITagService tagService = ServiceLocator.GetInstance<ITagService>();

Of course you'd be crazy to reinvent the wheel, and there are ample DI/IOC containers out there that do just this for us. I personally use StructureMap, but have always meant to investigate the competition.

Next week on Dependency Inversion for Dummies: We establish a shortlist of DI containers, compare how types are registered, define auto-wiring, learn about instance caching, and generally have the time of our life.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

C# | Design Patterns

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About me...

I'm a passionate .NET developer, with C# my language of choice. I've been at it for a number of years now, and enjoy that I'll never shake the feeling I'm just starting out.

I love software, and I love building it even more. I love knowing that my work facilitates others', and that one line of code at a time, we're increasing our capability.

More...


Badges

Page List