Monday, January 17, 2011

How To Not Hate Dependency Injection.

The Dependency Injection pattern is all the rage these days and more and more DI containers are becoming part of how we build .Net applications.  I still have a love / hate relationship with Dependency Injection.  I love the idea of being able to inject any external dependencies into my classes, thereby breaking those dependencies, but I hate the idea of creating an interface for every class that I need to inject, then maintaining that interface as well as any classes that implement it.  Let me show you what I mean.

Dependency Injection on an MVC  Controller Base Class With Interfaces

Typical scenario, I have an ASP.Net MVC2 application that needs to get data from a database. All of my data access code is contained in service classes like UserService, SystemService, CompanyService, etc.  I don’t want to manually declare and instantiate whatever service classes I need in each of my controller classes, so I created an abstract base class for my controllers called AtsControllerBase which has properties that will provide instances of my service classes using a lazy creation pattern.  Here’s an abbreviated version of the base class.

    public abstract class AtsControllerBase : Controller
    {
        //******************************************************
        // PROPERTIES
        //******************************************************

        // StateBox
        private HfStateBox _stateBox;
        public HfStateBox StateBox
        {
            get { return NewIfNull(_stateBox); }
            set { _stateBox = value; }
        }
        // AuthToken
        public AuthToken AuthToken { get { return StateBox.AuthToken; } }
        // ApplicantService
        private ApplicantService _applicantService;
        public ApplicantService ApplicantService
        {
            get { return NewIfNull(_applicantService); }
            set { _applicantService = value; } 
        }
        // CompanyService
        private CompanyService _companyService;
        public CompanyService CompanyService
        {
            get { return NewIfNull(_companyService); }
            set { _companyService = value; }
        }
        // FolderService
        private FolderService _folderService;
        public FolderService FolderService
        {
            get { return NewIfNull(_folderService); }
            set { _folderService = value; }
        }       
        // JobService
        private JobService _jobService;
        public JobService JobService
        {
            get { return NewIfNull(_jobService); }
            set { _jobService = value; }
        }
        // SecurityService
        private SecurityService _securityService;
        public SecurityService SecurityService
        {
            get { return NewIfNull(_securityService); }
            set { _securityService = value; }
        }
        // SystemService
        private SystemService _systemService;
        public SystemService SystemService
        {
            get { return NewIfNull(_systemService); }
            set { _systemService = value; }
        }
        // ServicePlanService
        private ServicePlanService _servicePlanService;
        public ServicePlanService ServicePlanService
        {
            get { return NewIfNull(_servicePlanService); }
            set { _servicePlanService = value; }
        }       
        // UserService
        private UserService _userService;
        public UserService UserService
        {
            get { return NewIfNull(_userService); }
            set { _userService = value; }
        }
        // WorkflowService
        private WorkflowService _workflowService;
        public WorkflowService WorkflowService
        {
            get { return NewIfNull(_workflowService); }
            set { _workflowService = value; }
        }


        //******************************************************
        // UTILITY METHODS
        //******************************************************
       
        // NewIfNull
        public T NewIfNull<T>(T obj) where T:new()
        {
            if (obj == null) { obj = new T(); }
            return obj;
        }
    }

This pattern works well for me because my concrete controllers never need to instantiate a service, they can just access any of the service properties in the base class.  I don’t have the overhead of newing up a bunch of service classes that the concrete controller won’t use because the services aren’t instantiated until the first time the service property is accessed.  That’s the lazy creation part.  Finally, each property has a public setter, so I can replace any of my services if I need to inject a mock or stub or something.

So this already looks a lot like Dependency Injection.  After all we have public setters for each of the services so we could switch them out if needed.  There’s one thing missing though.  We have no interfaces.  Our properties are all using the concrete type of the service class (like FolderService) instead of some public interface (like IFolderService).  So we could inject something, but it would have to be another instance of FolderService.  That get’s us nowhere. How can we inject something else like a MockFolderService??   Hmmmm,  I guess the right way to implement Dependency Injection is to create an IFolderService interface for my FolderService class.  At least that’s the pattern that I always see in books and code samples.  Let’s try that.  Ok, FolderService only has 10 public methods.  That’s not too bad so we’ll start with that one.  Here’s the service code.

    public partial class FolderService : ServiceBase
    {

        //**************************************************************************************
        // FOLDER METHODS
        //**************************************************************************************

        // GetFolderByFolderGuid
        public virtual Folder GetFolderByFolderGuid(Guid folderGuid)
        {
            string sql = @"SELECT *
                           FROM [Folder]                            
                           WHERE [FolderGuid] = @FolderGuid";
            SqlDao dao = SharedSqlDao;
            SqlCommand command = dao.GetSqlCommand(sql);
            command.Parameters.Add(dao.CreateParameter("@FolderGuid", folderGuid));
            return dao.GetSingle<Folder>(command);
        }

        // GetListOfFolderWithCountsForCompany
        public virtual List<FolderWithCount> GetListOfFolderWithCountsForCompany(Guid companyGuid)
        {
            string sql = @"SELECT f.*, (SELECT COUNT([ApplicantGuid])
FROM [ApplicantInFolder] WHERE [FolderGuid] = f.[FolderGuid]) As ApplicantCount

                          FROM [Folder] f
                          WHERE f.[CompanyGuid] = @CompanyGuid";
            SqlDao dao = SharedSqlDao;
            SqlCommand command = dao.GetSqlCommand(sql);
            command.Parameters.Add(dao.CreateParameter("@CompanyGuid", companyGuid));
            return (dao.GetList<FolderWithCount>(command));
        }

         // GetListOfFoldersForCompany
        public virtual List<Folder> GetListOfFoldersForCompany(Guid companyGuid)
        {
            string sql = @"SELECT f.*
                                  FROM [Folder] f
                                  WHERE f.[CompanyGuid] = @CompanyGuid";
            SqlDao dao = SharedSqlDao;
            SqlCommand command = dao.GetSqlCommand(sql);
            command.Parameters.Add(dao.CreateParameter("@CompanyGuid", companyGuid));
            return (dao.GetList<Folder>(command));
        }


        // GetListOfFoldersForApplicant
        public virtual List<Folder> GetListOfFoldersForApplicant(Guid applicantGuid)
        {
            string sql = @"select f.*
                        from Folder f
                        join ApplicantInFolder aif on aif.FolderGuid = f.FolderGuid
                        where aif.ApplicantGuid = @ApplicantGuid";
            SqlDao dao = SharedSqlDao;
            SqlCommand command = dao.GetSqlCommand(sql);
            command.Parameters.Add(dao.CreateParameter("@ApplicantGuid", applicantGuid));
            return (dao.GetList<Folder>(command));
        }

        // GetCachedListOfFoldersForCompany
        public virtual List<FolderWithCount> GetCachedListOfFoldersForCompany(Guid companyGuid)
        {
            if (companyGuid.Equals(NullValues.NullGuid))
            {
                // return empty list if a null guid was passed
                return new List<FolderWithCount>();
            }
            string key = "__GetCachedListOfFoldersForCompany_" + companyGuid.ToString();
            CacheHelper helper = new CacheHelper();
            List<FolderWithCount> list = helper.TryGet<List<FolderWithCount>>(key);
            if (list == null)
            {
                RefreshCachedListOfFoldersForCompany(companyGuid);
                list = helper.TryGet<List<FolderWithCount>>(key);
            }
            return list;
        }


        // RefreshCachedListOfFoldersForCompany
        public virtual void RefreshCachedListOfFoldersForCompany(Guid companyGuid)
        {
            string key = "__GetCachedListOfFoldersForCompany_" + companyGuid.ToString();
            CacheHelper helper = new CacheHelper();
            List<FolderWithCount> list = helper.TryGet<List<FolderWithCount>>(key);
            list = GetListOfFolderWithCountsForCompany(companyGuid);
            helper.Add(key, list);
        }

        // Insert
        public virtual void Insert(Folder folder)
        {
            this.FolderPersister.Insert(folder);
        }


        // Save
        public virtual void Save(Folder folder)
        {
            this.FolderPersister.Save(folder);        
        }

        // Delete
        public virtual void Delete(Folder folder)
        {
            this.FolderPersister.Delete(folder.FolderGuid);
        }


        // AddApplicantToFolder
        public virtual void AddApplicantToFolder(Guid applicantGuid, Guid companyGuid, Folder folder)
        {
            this.FolderPersister.SafeInsertApplicantInFolder(applicantGuid, folder.FolderGuid);
            RefreshCachedListOfFoldersForCompany(companyGuid);
        }
    }

We’ve basically got 10 public data access methods. Now I need to create a public interface that defines all of the public methods, so I’m going to create a new IFolderService interface.  ReSharper even makes it easy for me with a handy Extract Interface tool.  So my interface looks like this.

    public interface IFolderService
    {
        Folder GetFolderByFolderGuid(Guid folderGuid);
        List<FolderWithCount> GetListOfFolderWithCountsForCompany(Guid companyGuid);
        List<Folder> GetListOfFoldersForCompany(Guid companyGuid);
        List<Folder> GetListOfFoldersForApplicant(Guid applicantGuid);
        List<FolderWithCount> GetCachedListOfFoldersForCompany(Guid companyGuid);
        void RefreshCachedListOfFoldersForCompany(Guid companyGuid);
        void Insert(Folder folder);
        void Save(Folder folder);
        void Delete(Folder folder);
        void AddApplicantToFolder(Guid applicantGuid, Guid companyGuid, Folder folder);
        SqlDao SharedSqlDao { get; set; }
        ApplicantPersister ApplicantPersister { get; set; }
        CareerSiteSettingsPersister CareerSiteSettingsPersister { get; set; }
        CompanyPersister CompanyPersister { get; set; }
        CompanySettingsPersister CompanySettingsPersister { get; set; }
        FolderPersister FolderPersister { get; set; }
        HistoryItemPersister HistoryItemPersister { get; set; }
        JobPersister JobPersister { get; set; }
        JobStubPersister JobStubPersister { get; set; }
        ResumePersister ResumePersister { get; set; }
        UserPersister UserPersister { get; set; }
        WorkflowPersister WorkflowPersister { get; set; }
        T NewIfNull<T>(T obj) where T : new();
        string GetSqlCsv(List<Guid> list);
        string GetSqlCsv(List<int> list);
    }

There, that wasn’t too bad. Now I just need to change my controller FolderService property to type IFolderService and I have Dependency Injection implemented for the FolderService.  I can create new classes that implement IFolderService and inject them into my controller using the public FolderService property like this.

            var controller = new FolderController();
            IFolderService mock = new MockFolderService();
            controller.FolderService = mock;

That’s great! Now I just need to do that for… wait… I’m going to have to do that for every service class in my application.  Then I’m going to have to maintain each of those interfaces every time I make a change to a service class.  That’s a lot of extra code and a lot of work that’s going to have to be repeated every time I make a change to my service classes.  Why am I doing this again?  This is starting to feel like the hate part of the love / hate relationship.

Dependency Injection on an MVC  Controller Base Class Without Interfaces

So I don’t really want to create an interface for every single dependency in my application and then have to maintain that interface as well as my concrete classes, but what other option do I have?  Everybody implements DI using interfaces right?  Interfaces are like a badge of honor that demonstrate you know how to architect an application the right way.  You haven’t been to architecture land until you have your interface badge. We love interfaces!

Well I don’t love any code that I have to spend extra time maintaining every time I make a change to my application.  All that stuff adds up after a while.  I call it the death of a thousand cuts.  One day you look up and all those little extra tasks like maintaining interfaces add up to a mountain of friction that makes it miserable to make changes to your code.  Now there are places for interfaces, but if possible I’m opting for an easier alternative.

So step one in the easier alternative, throw out the interface that I just created and change my FolderService property back to type FolderService (no more IFolderService).  Step two…. actually we don’t need a step two we’re done.  Lets take a closer look at one of the data access methods in our FolderService class.

       // GetFolderByFolderGuid
        public virtual Folder GetFolderByFolderGuid(Guid folderGuid)
        {
            string sql = @"SELECT *
                           FROM [Folder]                            
                           WHERE [FolderGuid] = @FolderGuid";
            SqlDao dao = SharedSqlDao;
            SqlCommand command = dao.GetSqlCommand(sql);
            command.Parameters.Add(dao.CreateParameter("@FolderGuid", folderGuid));
            return dao.GetSingle<Folder>(command);
        }

Hmmm, that’s a virtual method.  That means that I can create a class that inherits from FolderService and just override the GetFolderByFolderGuid() method.  The FolderService class actually functions as my interface in this scenario.   So, if I’m writing a unit test where I want to stub out my FolderService I could create the following class.

    public class FolderServiceStub: FolderService
    {
        public override Folder GetFolderByFolderGuid(Guid folderGuid)
        {
            return new Folder() {FolderGuid = folderGuid, FolderName = "Test Folder"};
        }
    }

FolderServiceStub inherits from FolderService so I can use that stub class anywhere that an object of type FolderService is expected.  The end result is that my testing code (or any other DI code) winds up looking almost exactly like my code did with full on interface based Dependency injection

    var controller = new FolderController();
    FolderService mock = new FolderServiceStub();
    controller.FolderService = mock;

Summary

So, make all of your public methods virtual and you can implement a Poor Man’s Dependency Injection that doesn’t require interfaces.  It’s not the right solution every situation, but I’ve found that it works most of the time for me. It also has the one quality that I prize most in code.  It’s so simple that I forget it’s even there.

7 comments:

  1. Nice post. I completely agree in that we should all be pragmatic about practices and patterns and not just blindly follow them.

    However, the two main examples in this post can still easily be accomplished through IoC containers and mocking frameworks: -

    1. Mocking frameworks like Rhino can mock concrete classes by override their virtual methods with a derived class pretty much exactly as you've done - and there's no need to hand-craft your mocks or stubs.

    2. Unity (or many other IoCs containers I'm sure) can resolve from one type to another in the same type hierarchy. So there's no need to use interfaces there either - simply put your "real" concrete type on the class, and then before you create the object through the container, ensure that you register a mapping to redirect it to e.g. your mock. It takes one line of code to do it :)

    Hope that helps!

    ReplyDelete
  2. Read your article and find more ideas that I will start to work on hope to work with me...
    Great advice, and helpful for us.

    ReplyDelete
  3. I've been telling people for years that an interface has its place, but base classes should be the de facto.
    Part of the problem is that the term "interface" mean so many different things.
    So, when "interface" is used in the context of the properties and methods that a class exposes, people automatically think they need to create an "interface" such as ISomeClass.
    However, when you have written tons of framework code, and/or read the best practices according to the experienced experts, you know that the best practice is to use base classes first and foremost.
    The case in which an ISomeClass interface is required is "when you need to perform an operation on disparate class types"; i.e. classes that don't have any shared base functionality. Otherwise, an ISomeClass interface gives you nothing but higher maintenance cost.
    I agree that this seemingly small thing is one of the most annoying things plaguing OOP development.
    If people can't get this right, they have no business "architecting" anything.

    Thanks for the post.

    ReplyDelete
  4. yes its. this blog is good. i feel good.

    ReplyDelete
  5. A breath of fresh air. I always thought interfaces were a bit of an emperor's new clothes situation. Yes, interfaces are good for grouping behaviours but shouldn't be used to describe an entire classes.

    ReplyDelete
  6. Very useful information. Thank you for sharing it. Thanks 99th

    ReplyDelete
  7. Thanks a lot for sharing such a great information. It is really useful. Thanks pmdin

    ReplyDelete