Twitter Feed Popout byInfofru

OverrideThis.com

Adventures in .NET Software Craftsmanship!

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);
}

MVC2 Model Validation and Testing Scenarios

The solution described in this blog doesn't work for all scenarios.
Navigate to the following post for a better solution:
http://blog.overridethis.com/blog/post/2010/07/08/MVC2-Validation-and-Testing-e28093-Refactored.aspx

* TIP: If you don’t know what MVC2 Model Validation is you can check out this video here.

 

Lately, I have begun using ASP.NET MVC2 Model validation as much as possible and I have noticed a significant drop in the amount of plumbing code for validating a user’s request, which is making my controller action’s lighter, more readable and easier to manage. 

 

The one big drawback of using MVC2 Model Validation is that you are no longer able to test the functionality of your Controller’s action methods without having to mock or stub the validation process.  This is due to the fact that validation occurs during the Model binding process and as such is done by the ModelBinder, which doesn’t get called if you test your controller action’s by calling them directly.  We could write code to ensure that the Model binding process happens but it would be painful as we would have to mock around with the ControllerContext which is not an easy endeavor.

 

The following is my outline for a possible solution to this problem inspired by this post.  

 

First, lets establish a context to solve the problem, the following code snippet shows a Controller, Model, and Service Interface that all are direct dependencies of the CommentController we want to test.

 

Exhibit A – Very Simple Controller, Service, and Model:

namespace WebSite.Services {

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

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

    public class AddCommentRequest {
        [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.Message);
                return Json(new AddCommentResult {Success = true, Message = "Success"});
            }
            return Json(
                new AddCommentResult { 
                        Success = false, 
                        Message = "The 'message' is a required field." 
                });
        }
    }
}

 

Now, if we run the following tests the invalid request scenario would fail due to the validation not being triggered and as such the ModelState.IsValid property would always be true.

 

Exhibit B – The Tests

[TestFixture]
public class CommentControllerTests {

    private string VALID_MESSAGE = "SOME MESSAGE!";
    private string NOT_VALID_MESSAGE = string.Empty;

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

    [Test]
    public void CommentControllerCannotAddValidMessage() {
        Assert.IsFalse(this.HttpPostToAddComments(NOT_VALID_MESSAGE).Success);
    }

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

 

The best option to solve this issue and keep our tests light would be to write an extension method that would allow us to call the Controller Action elegantly while adding the additional validation that needs to be stubbed out.  The following, is a possible implementation of that extension method.  One thing to note is that this implementation would only work in .NET 4.0 scenarios that are using System.Component.DataAnnotations attributes.  You could implement a similar solution in .NET 3.5 but would not have the ValidationContext or ValidationResult objects to leverage.

 

Exhibit C – The Controller Extension for validating System.Component.DataAnnotations

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 {

        var validationContext = new ValidationContext(model, null, null);
        var validationResults = new List<ValidationResult>();
        Validator.TryValidateObject(model, validationContext, validationResults);
        foreach (var validationResult in validationResults) {
            controller
                .ModelState
                .AddModelError(validationResult.MemberNames.First(), 
                    validationResult.ErrorMessage);
        }

        return action(controller);
    }
}

 

Having this extension method in place would allow us to rewrite just a single line of code in the tests to ensure everything is working as expected.

 

Exhibit D – The corrected test scenarios

[TestFixture]
public class CommentControllerTests {

    private string VALID_MESSAGE = "SOME MESSAGE!";
    private string NOT_VALID_MESSAGE = string.Empty;

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

    [Test]
    public void CommentControllerCannotAddValidMessage() {
        Assert.IsFalse(this.HttpPostToAddComments(NOT_VALID_MESSAGE).Success);
    }

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

Security Trimming an ASP.NET SiteMapCollection

Yes, I know what everybody is thinking right now, Don't SiteMaps already provide functionality for Security Trimming?   Well they do and they don't.  This functionality is available only if you are using an implementation of the ASP.NET MembershipProvider and RoleProvider.  In the applications that I have to work with in a day to day basis that is rarely the case, I always have to develop a custom security provider using forms or windows authentication that support some far fetched business requirements.   I still however love to use SiteMaps and have rarely seen the need to customize the base SiteMap provider for any other reason.    So, in order to have security trimming and still use and bind the information from my SiteMap I wrote the following block of code that just begged to be re-factored.

Exhibit A - Code that needs (begs) for refactoring.

// do security trimming.
List<SiteMapNode> nodes = new List<SiteMapNode>();
foreach (SiteMapNode node in SiteMap.RootNode.ChildNodes) {
    if (node.Roles.Count == 0) {
        // just add.
        nodes.Add(node);
        continue;
    } else {
        // validate security.
        foreach (var role in node.Roles) {
             if (this.Page.User.IsInRole(role.ToString())) {
                 nodes.Add(node);
                 break;
             }
        }
   }
}

// return list.
this.ListViewTopMenu.DataSource = SiteMap.RootNode.ChildNodes;
this.ListViewTopMenu.DataBind();


I decided that, because I was going to need to reuse this functionality in several places and I wanted to avoid creating a class that would perform this task specifically,  that I should add a new method to the SiteMapCollection class using a C# 3.0 Extension Method. 

Step 1 - Extension Method

public static class SiteMapNodeCollectionExtension {
	
	public static IList FilterByRoles(
		this SiteMapNodeCollection nodeColl, 
		IPrincipal user) {
		
		// collection used to store array values to return to browser
		List nodes = new List();

		// iterate through the node collection to return the valid nodes.
		foreach (SiteMapNode node in nodeColl) {
			if (node.Roles.Count == 0) {
				// just add.
				nodes.Add(node);
				continue;

			} else {
				// validate security.
				foreach (var role in node.Roles) {
					if (user.IsInRole(role.ToString())) {
						// add to collection.
						nodes.Add(node);
						break;
					}
				}
			}
		}
		return nodes;
	}
	
}

 

This blog is not intended to be a tutorial, but I would like to point out the facts that C# Extension methods have to be defined in a static class as a static method and the first parameter must be the class to be extended (Notice the use of the 'this' keyword on the fist parameter).

Step 2 - Refactoring of the client code.

// bind list.
this.ListViewTopMenu.DataSource = SiteMap.RootNode.ChildNodes.FilterByRoles(this.Page.User);
this.ListViewTopMenu.DataBind();

Thank you for reading,

Roberto Hernandez

Caching Abstraction Solution - .NET 3.5

Today, as I was writing the frontend to an ASP.NET web application I noticed a trend in my code.  I had the following block of code in one form or another in several places.

protected string MemberFullName() {

    // build the cache key.
    string cacheKey = string.Format("Member@FullName@{0}"
        , this.Page.User.Identity.Name);

    // build cache entry.
    if (this.Cache[cacheKey] == null) {

        // get membership service.
        var membershipService = DependencyFactory.MembershipService();

        // get member.
        var member = membershipService
            .MemberGetByUsername(this.Page.User.Identity.Name);

        // get full name.
        string fullName = string.Format("{0}, {1}", 
            member.Lastname, 
            member.Firstname);

        // add to cache.
        this.Cache.Insert(cacheKey, 
            fullName, 
            null, 
            DateTime.Now.AddMinutes(5), 
            TimeSpan.Zero);
    }

    // return value.
    return (string)this.Cache[cacheKey];
}

 
Immediately I decided to address the issues that could arise from letting this behavior continue. First of all, I wanted to decouple the direct dependency to the ASP.NET Caching implementation, that way if in the future I decided to cache using another provider I would be able to make the switch without huge problems. I also wanted to use some of the cool features that have been available since the release of the .NET 3.0 Framework like Lambdas, and some oldies but goodies, like Generics and Anonymous Methods.

Step 1 - I coded a basic enum to use as the type of information to be cached.

public enum CacheKey {
    MembershipRolesByUsername,
    MembershipFullNameByUsername,
}


Step 2 -  I built the following interface that will provide the abstraction.

public interface ICacheProvider<T> {
    T GetItemFromCache(CacheKey type, string key, int minutes, Func<T> builderFunction);
}


Generics is used to add flexibility to the Cache provider while retaining the elegance and compile time advantages of strongly typed code. It has a couple of parameters but the one that should really catch your eye is the builderFunction (Func<T>) which allows us to pass in the function used to create the item to cache by using a Lambda expression or an Anonymous Method.

Step 3 - Implement the cache provider.

public class CacheProvider<T> : ICacheProvider<T> {

    #region ICacheProvider Members

    public T GetItemFromCache(CacheKey type, 
                 string key, 
                 int minutes, 
                 Func<T> builderFunction) {

        // build the cache key.
        string cacheKey = string.Format(string.Concat(type.ToString(), "{0}"), key);

        // get current cache.
        var context = HttpContext.Current;

        // build cache entry.
        if (context.Cache[cacheKey] == null)
            context.Cache.Insert(cacheKey, 
                 builderFunction.Invoke(), 
                 null, 
                 DateTime.Now.AddMinutes(minutes), 
                 TimeSpan.Zero);

        // return value.
        return (T)context.Cache[cacheKey];
    }

    #endregion
}


Nothing real fancy to explain here, the only thing I would point out is that the function to build the item doesn't get called if the item is already in the cache which is the whole point of caching.

Step 4 - Use the Cache provider
Option A - With an anonymous method.

// anonymous function.
Func<string> memberFullNameFunction = delegate() {
     var member = DependencyFactory.MembershipService()
          .MemberGetByUsername(this.Page.User.Identity.Name);
     return string.Format("{0},{1}", member.Lastname, member.Firstname);
};

// return value.
return new CacheProvider<string>()
     .GetItemFromCache(CacheKey.MembershipFullNameByUsername
          , this.Page.User.Identity.Name
          , 5
          , memberFullNameFunction);


Option B - With a Lambda Expression

// return value.
return new CacheProvider()
    .GetItemFromCache(CacheKey.MembershipFullNameByUsername
        , this.Page.User.Identity.Name
        , 5
        , ()  => {
                var member = DependencyFactory.MembershipService()
                    .MemberGetByUsername(this.Page.User.Identity.Name);
                return string.Format("{0},{1}", 
                    member.Lastname, 
                    member.Firstname);
        });


Depending on personal taste I guess one way of calling the provider might seem simpler than the other.

Thanks for reading,

Roberto Hernandez