Twitter Feed Popout byInfofru

OverrideThis.com

Adventures in .NET Software Craftsmanship!

Lazy Loading Dependencies with StructureMap

Lately I have been working with an existing custom .NET Library designed to register and execute tasks triggered by events, sort of like a local Event Bus.  The Library was built in house and is used successfully in many applications but it lacks integration with Dependency Injection or Common Service Locator which makes it hard to implement in a solution where separation of concerns are enforced strictly. 

 

To highlight the issue, look at the following snippet, it is a sample of the exposed API for registering tasks associated to an event. 

 

public interface IEventTaskRegister {
    void RegisterFor<T>(ITask action) where T : IEvent;
}
// Sample use scenario.
RegisterFor<OnSomeEvent>(new SomeTask()); 

 

At first glance, there is nothing wrong with the previous code but if you look more carefully you will notice that we are passing a concrete instance of a type.  Now, my preference would be to pass just a type and have the event task library instantiate the type and its dependencies using some form of service location, but modifying the event task library is not an option for us at this time.   One possible solution is to simply use Service Location when we are registering our associated events, as you can see in the following snippet.

 

RegisterFor<OnSomeEvent>(new SomeTask(ObjectFactory.GetInstance<IRepository>()));
// Or Even Less Code.
RegisterFor<OnSomeEvent>(ObjectFactory.GetInstance<SomeTask>());

 

Luckily in the current project all tasks are registered during startup so there isn’t much wrong with muddying the registration code with service location.  But, at this point we have unknowingly created another issue, since the execution of the registration code happens once during system startup the tasks and its dependencies will be unique throughout the execution of the application.  This is a huge problem for us as our current implementation of the IRepository behaves differently depending on context information provided during instantiation, see snippet. 

 

// Repository.
public interface IRepository {
    void DoSomething();
}
public class RepositoryImpl : ITask {
    private readonly IContext context;
    public RepositoryImpl(IContext context) {
        this.context = context;
    }
    public void DoSomething() {
        // Does something that varies
        // depending on the context
        // passed in during instatiation.
    }
}
// SomeTaks
public interface ITask {
    void Execute(); 
}
public class SomeTask : ITask {
    private readonly IRepository repository;
    public SomeTask(IRepository repository) {
        this.repository = repository;
    }
    public void Execute() {
        // Do Something w/Repository.
    }
}

 

One possible solution, is to modify the way we write our implementation of ITask to allow for lazy instantiation of its dependencies but still take of advantage of Dependency Injection an Inversion Of Control.  The following snippet highlights the solution the key is to inject a Func<IRepository> not an IRepository, that way the consumer is actually the one who will be instantiating the dependency on demand.

 

// SomeTaks
public interface ITask {
    void Execute(); 
}
public class SomeTask : ITask {
    private readonly Func<IRepository> repositoryBuilder;
    public SomeTask(Func<IRepository> repositoryBuilder) {
        this.repositoryBuilder = repositoryBuilder;
    }
    public void Execute() {
        var repository = this.repositoryBuilder();
        // Do Something w/Repository.
    }
}
Now the cool part about this solution, is that we don’t need to modify the way we are currently registering our dependencies in StructureMap, all our registries work as expected.

Codes and Slides on MVC Extensibility

I have made my slides and code available from my presentation on MVC Extensibility at the MVC Conference.   Before I give out the details,  I would like to congratulate the organizers of the event for their great work on making this amazing event happen.  In particular, I would like to extend a personal thanks to Eric Hexter, and Javier Lozano.  Thanks again guys, for giving me the opportunity to speak on such an amazing digital venue.

 

The the slides and code are available at codeplex in this location.

http://mvcextensibility.codeplex.com

Presenting at MVCConf

I am excited to announce that I will be participating as a presenter at MVCConf.  MVC Conference (MVCConf) is a free virtual conference focused on one thing only, writing awesome application on top of the ASP.NET MVC applications, for more information go to http://www.mvcconf.com

 

I will be doing a presentation on MVC Extensiblity focused on plugging functionality onto your MVC application to enforce separation of concerns therefore enhancing testability. The following are the preliminary details:

 

MVC2 Extensibility

Learn the basic extensibility points of the ASP.NET MVC Framework by refactoring an existing code base and making changes that will make your application less fragile and easier to test.  The main focus will be how to leverage dependency injection, routes, action filters,  model binders, etc. to build a better all-around application.

 

Hope to see you online on the 22h of July!

MVC2 Validation and Testing – Refactored

This post is to fix a bug enhance the functionality in the original Blog Post titled “MVC2 Validation and Testing” at the following link. 
http://blog.overridethis.com/blog/post/2010/04/22/MVC2-Model-Validation-and-Testing-Scenarios.aspx


A while back I wrote about testing Controller Actions that rely on MVC2’s new Model Validation.  I had been using the technique described in the post for a while on most of my MVC2 applications without running into any significant issue until I tried to combine validators on a single property of my Model.  The following are the modified testing scenarios that would not work with the code I provided in my originally post.  Apparently, the code I wrote originally does not target all validators that are applied through Metadata.

 

namespace WebSite.Services {

    public interface ICommentService {
        void AddComment(string email, string message);
    }

}
namespace WebSite.Models {
    
    using System.ComponentModel.DataAnnotations;

    public class AddCommentRequest {
        private const string EMAIL_REGEX = 
           @"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
            + @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
            + @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
            + @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$";
        [Required, RegularExpression(EMAIL_REGEX)]
        public string Email { get; set; }
        [Required]
        public string Message { get; set; }
    }

    public class AddCommentResult {
        public bool Success { get; set; }
        public string Message { get; set; }
    }
}
namespace WebSite.Controllers {

    using System.Web.Mvc;
    using Models;
    using Services;

    public class CommentController : Controller {

        private readonly ICommentService service;

        public CommentController() {

        }

        public CommentController(ICommentService service) {
            this.service = service;
        }

        [HttpPost]
        public ActionResult AddComment(AddCommentRequest request) {
            if (ModelState.IsValid) {
                this.service.AddComment(request.Email, request.Message);
                return Json(new AddCommentResult {Success = true, Message = "Success"});
            }
            return Json(
                new AddCommentResult { 
                        Success = false, 
                        Message = "The 'message' is a required field." 
                });
        }
    }
}

 

The following Test validate the Model Validation functionality enforce and needless to say if we use the original ControllerExtension for validation we get less than ideal results.

 

[TestFixture]
public class CommentControllerTests {

    private string VALID_MESSAGE = "SOME MESSAGE!";
    private string VALID_EMAIL   = "someemail@yahoo.com";
    private string NOT_VALID_MESSAGE = string.Empty;
    private string NOT_VALID_EMAIL   = "someemail@@@@@@c";
    
    [Test]
    public void CommentControllerCanAddValidMessage() {
        Assert.IsTrue(this.HttpPostToAddComments(VALID_EMAIL, VALID_MESSAGE).Success);
    }

    [Test]
    public void CommentControllerCannotAddWithoutValidMessage() {
        Assert.IsTrue(this.HttpPostToAddComments(VALID_EMAIL, NOT_VALID_MESSAGE).Success);
    }    

    [Test]
    public void CommentControllerCannotAddWithoutRequiredEmail() {
        Assert.IsTrue(this.HttpPostToAddComments("", VALID_MESSAGE).Success);
    }    

    [Test]
    public void CommentControllerCannotAddWithourValidEmailAddress() {
        Assert.IsTrue(this.HttpPostToAddComments(NOT_VALID_EMAIL, VALID_MESSAGE).Success);
    }

    private AddCommentResult HttpPostToAddComments(string email, string message) {
        
        var mockOfICommentService = new Mock<ICommentService>();
        mockOfICommentService.Setup(m => m.AddComment(VALID_EMAIL, VALID_MESSAGE)).AtMostOnce();
        var model = new AddCommentRequest {Email = email, Message = message};
        var controller = new CommentController(mockOfICommentService.Object);
        var result = controller
		.CallWithModelValidation(c => c.AddComment(model), model);
        mockOfICommentService.Verify();
        return (AddCommentResult)((JsonResult) result).Data;
    }
}

 

This time around I feel like I did better research to find a suitable solution by looking at the ASP.NET MVC 2 source code and its tests.   In my original post, I had overlooked a couple of reusable tools already in place in the ASP.NET MVC source code that would have made my work a whole lot easier like the ModelMetadataProviders, and DataAnnotationsModelValidatorProvider.   Of course, there is always room for improvement, but the following is the refactored ControllerExtensions class that allows for all the previous tests to go green.

 

public static class ControllerExtensions {

    public static ActionResult CallWithModelValidation<C, R, T>(this C controller
           , Func<C, R> action
           , T model)
        where C : Controller
        where R : ActionResult
        where T : class {
        DataAnnotationsModelValidatorProvider provider = new DataAnnotationsModelValidatorProvider();
        IEnumerable<ModelMetadata> metadata = ModelMetadataProviders
			.Current
			.GetMetadataForProperties(model, typeof(T));
        foreach (ModelMetadata modelMetadata in metadata) {
            IEnumerable<ModelValidator> validators = provider
				.GetValidators(modelMetadata, new ControllerContext());
            foreach (ModelValidator validator in validators) {
                IEnumerable<ModelValidationResult> results = validator.Validate(model);
                foreach (ModelValidationResult result in results)
                    controller.ModelState.AddModelError(modelMetadata.PropertyName, result.Message);
            }
        }
        return action(controller);
    }
}

NHibernate Audit w/Database Server Time

Disclaimer: This articles highlights a workaround not a best practice. In fact, if you are so inclined, please provide me with your feedback on how you would have solved this problem differently.

 

At a recent consulting gig a client’s IT department made the very common request that we store classic audit trail information in all database records.  What that means, at least to me, is that every single record in the database is going to have a [CreatedBy], [CreatedDate], [UpdatedBy], and [UpdatedDate] fields.  I found a lot of help on the internet on how to implement this by using NHibernate and its Event Model introduced (ported from Hibernate) in the 2.0 version.

 

These are some of the articles I based my solution on:

http://ayende.com/Blog/archive/2009/04/29/nhibernate-ipreupdateeventlistener-amp-ipreinserteventlistener.aspx

http://nhforge.org/wikis/howtonh/creating-an-audit-log-using-nhibernate-events/revision/1.aspx

 

Both articles are pretty much self explanatory and solve all the business requirements except one, and I quote “All dates in the audit database fields must be the Database Server Time not the Application Server Time”.  Now this might sound like a simple requirement that can be solved this by defining custom SQL queries for insert, update and delete operations in the NHibernate mapping files but doing so will introduce the following problems:


1.- Writing custom xml mapping files defeats the purpose of using FluentNHibernate and its AutoMapping capability.

2.- Our integration testing strategy that relies on the use of SQLite would be become complex as we would have to define custom .hbm mapping files for testing and different ones for production ones that use T-SQL for SQL Server 2008.

 

My solution, albeit probably not a good one, was to define a named query for retrieving the Database Server Time from the application.  The following is the code for registering that named query using the NHibernate’s fluent configuration API.

 

public const string SQLSERVER_HBM_MAPPING_XML =
    @"
        <hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'>
        <sql-query name='GetServerDateTime'>
            SELECT GETDATE();
        </sql-query>
        </hibernate-mapping>
    ";

public const string SQLITE_HBM_MAPPING_XML =
    @"
        <hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'>
        <sql-query name='GetServerDateTime'>
            SELECT datetime('now');
        </sql-query>
        </hibernate-mapping>
    ";

public static void RegisterServerDateTimeMethod(Configuration cfg) {
   if (cfg.Properties["connection.driver_class"].Contains("SQLite"))
        cfg.AddXmlString(SQLITE_HBM_MAPPING_XML);                    
   else
        cfg.AddXmlString(SQLSERVER_HBM_MAPPING_XML);
}

 

The key is to call the RegisterServerDateTimeMethod which registers the named query depending on the connection driver class settings name.   Now to make the process of calling this named query easier I wrote an extension method for NHibernate’s ISession that looks like the following.

 

public static class ISessionExtensions {
    public static DateTime GetServerDateTime(this ISession session) {
        var resultFromServerDateCall = session
     		.GetNamedQuery(NhSessionFactory.SERVER_DATETIME_NAMED_QUERY)
     		.UniqueResult();
        DateTime result;
        if (resultFromServerDateCall is string)
            result = DateTime.Parse((string)resultFromServerDateCall);
        else
            result = (DateTime) resultFromServerDateCall;
        return result;
    }
}

 

The following test illustrates how to use the Extension method.
* Needless to say this test is very brittle and doesn’t really test much, but it gives me some peace of mind as it verifies that the .hbm file mapping syntax is valid.

 

[Test]
public void NhRepositoryCanRetrieveServerDateTime() {
    var serverDate = this.Session.GetServerDateTime();
    Assert.AreEqual(serverDate.Year, DateTime.Now.Year);
    Assert.AreEqual(serverDate.Month, DateTime.Now.Month);
    Assert.AreEqual(serverDate.Day, DateTime.Now.Day);
}