September 2009 - Posts

This will be a quick and dirty post about how to get xVal 1.0 (http://xval.codeplex.com/) to work with the new NHibernate Validator 1.2beta (http://nhforge.org/media/p/7.aspx).

The problem is that xVal 1.0 ships with a NHibernate Validator (NHVal) Ruleset Provider which does not compile against the 1.2beta DLLs because of some API changes.  Meanwhile, S#arp Architecture uses NHVal 1.2 so using those Dlls is a requirement for me.

In the process of rewriting the NHVal ruleset provider I stripped out all XML rule definition support, so for my code to work you must be using NHVal attributes.

With those disclaimers, here is the code:

public class NHibernateValidatorRulesProvider : CachingRulesProvider
{
    private readonly ValidatorMode configMode;
    private readonly RuleEmitterList<IRuleArgs> ruleEmitters = new RuleEmitterList<IRuleArgs>();
 
    public NHibernateValidatorRulesProvider(ValidatorMode configMode)
    {
        this.configMode = configMode;
 
        ruleEmitters.AddSingle<LengthAttribute>(x => new StringLengthRule(x.Min, x.Max));
        ruleEmitters.AddSingle<MinAttribute>(x => new RangeRule(x.Value, null));
        ruleEmitters.AddSingle<MaxAttribute>(x => new RangeRule(null, x.Value));
        ruleEmitters.AddSingle<RangeAttribute>(x => new RangeRule(x.Min, x.Max));
        ruleEmitters.AddSingle<NotEmptyAttribute>(x => new RequiredRule());
        ruleEmitters.AddSingle<NotNullNotEmptyAttribute>(x => new RequiredRule());
        ruleEmitters.AddSingle<PatternAttribute>(x => new RegularExpressionRule(x.Regex, x.Flags));
        ruleEmitters.AddSingle<EmailAttribute>(x => new DataTypeRule(DataTypeRule.DataType.EmailAddress));
        ruleEmitters.AddSingle<DigitsAttribute>(MakeDigitsRule);
    }
 
    protected override RuleSet GetRulesFromTypeCore(Type type)
    {
        var classMapping = ClassMappingFactory.GetClassMapping(type, configMode);
 
        var rules = from member in type.GetMembers()
                    where member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property
                    from att in classMapping.GetMemberAttributes(member).OfType<IRuleArgs>()
                    // All NHibernate Validation validators attributes must implement this interface
                    from rule in ConvertToXValRules(att)
                    where rule != null
                    select new {MemberName = member.Name, Rule = rule};
 
        return new RuleSet(rules.ToLookup(x => x.MemberName, x => x.Rule));
    }
 
    private IEnumerable<Rule> ConvertToXValRules(IRuleArgs att)
    {
        foreach (var rule in ruleEmitters.EmitRules(att))
        {
            if (rule != null)
            {
                rule.ErrorMessage = MessageIfSpecified(att.Message);
                yield return rule;
            }
        }
    }
 
    private string MessageIfSpecified(string message)
    {
        // We don't want to display the default {validator.*} messages
        if ((message != null) && !message.StartsWith("{validator."))
            return message;
        return null;
    }
 
    private RegularExpressionRule MakeDigitsRule(DigitsAttribute att)
    {
        if (att == null) throw new ArgumentNullException("att");
        string pattern;
        if (att.FractionalDigits < 1)
            pattern = string.Format(@"\d{{0,{0}}}", att.IntegerDigits);
        else
            pattern = string.Format(@"\d{{0,{0}}}(\.\d{{1,{1}}})?", att.IntegerDigits, att.FractionalDigits);
        return new RegularExpressionRule(pattern);
    }
 
    private static class ClassMappingFactory
    {
        public static IClassMapping GetClassMapping(Type type, ValidatorMode mode)
        {
            const IClassMapping result = null;
 
            switch (mode)
            {
                case ValidatorMode.UseAttribute:
                    break;
                default:
                    throw new NotImplementedException("Only attributes are supported");
            }
 
            return result ?? new ReflectionClassMapping(type);
        }
    }
}

 

Now in the global.asax file, you just need to hook xVal and this class together like so:

xVal.ActiveRuleProviders.Providers.Add(
                new NHibernateValidatorRulesProvider(
                    ValidatorMode.UseAttribute));
 
Enjoy!

I recently setup my first project ever in CodePlex and naturally I am very excited about its tight integration with TFS since my group uses TFS internally for source control as well.  Connecting to CodePlex through the Visual Studio Team Explorer is very easy, you just use one of the tfs0x.codeplex.com servers as your target, and then give them a variation of your username and password to connect.

The prompt for your password will come up every time you open VS, and there is no option to save your credentials.  I like security as much as the next guy, but after a while this becomes tedious and it was proving to be a stumbling block to adoption among some team members.

image

How To Store Your Credentials

The solution I came up with was to use the Windows credential manager to save my CodePlex credentials.  Here are the steps:

  • Find the credential manager (Control Panel –> User Accounts –> Credential Manager)
  • Click on “Add a Windows credential”
  • Enter your codeplex login information (see the picture below)
  • Click Ok

image

The next time you open up any project on that codeplex TFS server, your credentials will automatically be passed!

Enjoy

When working in on a fresh Visual Studio 2008 SP1 install I needed to use TFS (as always) so I installed the TFS Team Explorer client from the TFS disk and I kept getting the following error whenever trying to view/edit Work Items:

Could not load type 'Microsoft.TeamFoundaton.WorkItemTracking.Client.WorkItemTypeDeniedOrNotExistException' from assembly 'Microsoft.TeamFoundation.WorkItemTracking.Client, Version=9.0.0.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

After trying several fixes on different boxes, I think it is really a simple versioning problem, where VS SP1 upgrades some TFS dlls and then the old TFS client get installed and can’t load the correct dlls.

I have not tested this, but I bet if you install TFS Team Explorer before you install VS SP1 then things will work out OK.

If You Have This Problem

It seems the solution is simple to reinstall VS 2008 SP1 and it will fix up the dlls.  Don’t worry too much about the install time, it is much faster to repair SP1 then install in fresh the first time.

I Hope this helps.

Aaaarrrrgggg

In any project where you use an ORM you often have all of your domain classes inherit from a common base class.  Among other things, your base class often contains your identity property.  Mine has a protected IdT (this is the Id type) field called id, and a public getter called ID.

   1: protected IdT id;
   2:  
   3: public virtual IdT ID
   4: {
   5:     get { return id; }
   6: }

One important and complex question is how to handle equality of two domain objects.  This becomes very important for caching, using HashSets, and more.

Before I share my solution I will mention that there are many implementations of base class equality out there, with S#arp Architecture’s standing out in my mind as a great though complex (maybe enterprise-level is a better word) variation.

Defining Equality

My idea of equality is that the base class will do its best to define equality that will work for most domain objects, but if a subclass would like to override Equals() then they are more than welcome.

Overall I would like domain object equality to use the following general criteria:

Two domain objects are equal if they are the same object (by reference).

If they are not the same object, one cannot be null, and they must be castable to each other [This one is tricky since we can’t use type equality because one might be a proxy type].

If the above criteria validate, then the Ids of the two objects must match but be non-default [So two transient object are always non-equal, and a persisted object can never equal a transient object].

Testing Equality Assumptions

Before I implement equals, I’ll write tests which verify all of my assumptions.

   1: [TestClass]
   2: public class EqualityTests
   3: {
   4:     [TestMethod]
   5:     public void EqualsWithTwoNullObjectsReturnsTrue()
   6:     {
   7:         const SimpleDomainObject obj1 = null;
   8:         const SimpleDomainObject obj2 = null;
   9:  
  10:         var equality = Equals(obj1, obj2);
  11:  
  12:         Assert.AreEqual(true, equality);
  13:     }
  14:  
  15:     [TestMethod]
  16:     public void EqualsWithNullObjectReturnsFalse()
  17:     {
  18:         const SimpleDomainObject obj1 = null;
  19:         var obj2 = new SimpleDomainObject();
  20:  
  21:         var equality = Equals(obj1, obj2);
  22:  
  23:         Assert.AreEqual(false, equality);
  24:     }
  25:  
  26:     [TestMethod]
  27:     public void EqualsWithTransientObjectsReturnsFalse()
  28:     {
  29:         var obj1 = new SimpleDomainObject();
  30:         var obj2 = new SimpleDomainObject();
  31:  
  32:         var equality = Equals(obj1, obj2);
  33:  
  34:         Assert.AreEqual(false, equality);
  35:     }
  36:  
  37:     [TestMethod]
  38:     public void EqualsWithOneTransientObjectReturnsFalse()
  39:     {
  40:         var obj1 = new SimpleDomainObject();
  41:         var obj2 = new SimpleDomainObject();
  42:  
  43:         obj1.SetId(1);
  44:  
  45:         var equality = Equals(obj1, obj2);
  46:  
  47:         Assert.AreEqual(false, equality);
  48:     }
  49:  
  50:     [TestMethod]
  51:     public void EqualsWithDifferentIdsReturnsFalse()
  52:     {
  53:         var obj1 = new SimpleDomainObject();
  54:         var obj2 = new SimpleDomainObject();
  55:  
  56:         obj1.SetId(1);
  57:         obj2.SetId(2);
  58:  
  59:         var equality = Equals(obj1, obj2);
  60:  
  61:         Assert.AreEqual(false, equality);
  62:     }
  63:  
  64:     [TestMethod]
  65:     public void EqualsWithSameIdsReturnsTrue()
  66:     {
  67:         var obj1 = new SimpleDomainObject();
  68:         var obj2 = new SimpleDomainObject();
  69:  
  70:         obj1.SetId(1);
  71:         obj2.SetId(1);
  72:  
  73:         var equality = Equals(obj1, obj2);
  74:  
  75:         Assert.AreEqual(true, equality);
  76:     }
  77:  
  78:     [TestMethod]
  79:     public void EqualsWithSameIdsInSubclassReturnsTrue()
  80:     {
  81:         var obj1 = new SimpleDomainObject();
  82:         var obj2 = new SubSimpleDomainObject();
  83:  
  84:         obj1.SetId(1);
  85:         obj2.SetId(1);
  86:  
  87:         var equality = Equals(obj1, obj2);
  88:  
  89:         Assert.AreEqual(true, equality);
  90:     }
  91:  
  92:     [TestMethod]
  93:     public void EqualsWithDifferentIdsInDisparateClassesReturnsFalse()
  94:     {
  95:         var obj1 = new SimpleDomainObject();
  96:         var obj2 = new OtherSimpleDomainObject();
  97:  
  98:         obj1.SetId(1);
  99:         obj2.SetId(2);
 100:  
 101:         var equality = Equals(obj1, obj2);
 102:  
 103:         Assert.AreEqual(false, equality);
 104:     }
 105:  
 106:     [TestMethod]
 107:     public void EqualsWithSameIdsInDisparateClassesReturnsFalse()
 108:     {
 109:         var obj1 = new SimpleDomainObject();
 110:         var obj2 = new OtherSimpleDomainObject();
 111:  
 112:         obj1.SetId(1);
 113:         obj2.SetId(1);
 114:  
 115:         var equality = Equals(obj1, obj2);
 116:  
 117:         Assert.AreEqual(false, equality);
 118:     }
 119: }
 120:  
 121: public class SimpleDomainObject : DomainObject<SimpleDomainObject,int>
 122: {
 123:     public void SetId(int ident)
 124:     {
 125:         id = ident;
 126:     }
 127: }
 128:  
 129: public class SubSimpleDomainObject : SimpleDomainObject{}
 130:  
 131: public class OtherSimpleDomainObject : DomainObject<OtherSimpleDomainObject,int>
 132: {
 133:     public void SetId(int ident)
 134:     {
 135:         id = ident;
 136:     }
 137: }

This is a lot of code but if you look at each test by itself you’ll see that if all the tests pass I will have equality implemented correctly, or at least correctly as far as I defined it above.

If you run the tests now you will see that the tests fail when you are trying to test equality as two objects having the same Ids.  Let’s implement an Equals() method and see what we can come up with.

Implementing Equals

This implementation is based off of an existing DomainObject<T,IdT> base class that is similar to the one proposed in the NHibernate Best Practices article.

   1: public override bool Equals(object other)
   2: {
   3:     if (ReferenceEquals(this, other)) return true;
   4:  
   5:     if (other == null || other is T == false) return false; //is returns true if other is castable to T
   6:  
   7:     return Equals(other as DomainObject<T, IdT>);
   8: }
   9:  
  10: private bool Equals(DomainObject<T, IdT> other)
  11: {
  12:     if (ReferenceEquals(null, other)) return false;
  13:  
  14:     //Domain objects are equal if their ids are equal and non-default
  15:     if (Equals(id, default(IdT))) return false;
  16:  
  17:     return Equals(id, other.id); 
  18: }

Now if we run the tests we get all greens!

image

Getting the HashCode

Whenever you override Equals(), it is recommended that you override GetHashCode().  In our case this is pretty easy, since we just want to use the id’s HashCode (or the base HashCode if id is null/default).

UPDATE: Modified GetHashCode() to include the base class’ hash code so we get less collisions between disparate classes.

   1: public override int GetHashCode()
   2: {
   3:     return Equals(id, default(IdT)) ? base.GetHashCode() : (base.GetHashCode() * 31) + id.GetHashCode();
   4: }

 

   1: public override int GetHashCode()
   2: {
   3:     return Equals(id, default(IdT)) ? base.GetHashCode() : id.GetHashCode();
   4: }

 

Enjoy!

ASP.NET MVC comes with a UrlHelper class in the System.Web.Mvc.Controller.Url namespace, which you can access through the Url property of any controller.  This provides some handy methods to get the url of an action or route, among other things.  For example, Url.Action(“About”, “Home”) will return the string “/Home/About”, which is the relative url of the About action on the Home controller.

I recently had a need to generate an absolute Url (to put in an email), so I wrote a quick extension method to the UrlHelper class that I thought might be useful to someone else.  The code is as follows:

   1: public static class UrlExtensions
   2: {
   3:     public static string AbsoluteAction(this UrlHelper url, string action, string controller, object routeValues)
   4:     {
   5:         Uri requestUrl = url.RequestContext.HttpContext.Request.Url;
   6:  
   7:         string absoluteAction = string.Format("{0}{1}",
   8:                                               requestUrl.GetLeftPart(UriPartial.Authority),
   9:                                               url.Action(action, controller, routeValues));
  10:  
  11:         return absoluteAction;
  12:     }
  13: }

Basically is takes the same values as Url.Action (or at least one of the Url.Action signatures) and adds the “left part” of the authority (see the MSDN Library GetLeftPart Docs).

So if you write the following code:

   1: Url.AbsoluteAction("Edit", "Users", new {id="username"});

You should get something like the following result if you are running it locally:

http://localhost:50717/Users/Edit/username

Enjoy!

This is the fourth and final post of a multi-part post series on writing simple auditing functionality for an ASP.NET application using NHibernate.  The requirement was that every object modification event in the system should be logged by username and date.  Specifically I don’t need to know exactly which properties were changed (just that a user was updated by whom at what time), but if you do need to save the changed properties there are plenty of hooks to do that.

I’ll update this post with links to the next parts when they become available

Hooking up the NHibernate IInterceptor

Now that we have written our IInterceptor, implemented as the AuditInterceptor class, we need to tell NHibernate to use it.  This part will vary depending on how NHibernate is exposed to your application, but the basic idea is that when you open a session (using OpenSession() on your NHibernate session factory) you will pass along the interceptor as a parameter.  If you have no interceptor, you can just call OpenSession().

Extending the NHibernate Session Manager

Our session manager class follows loosely with the thread-safe, lazy singleton patterned NHibernateSessionManager described in the NHibernate Best Practices Article.  Adding hooks for a persistent interceptor was pretty easy – first I wrote a method for registering an interceptor:

   1: /// <summary>
   2: /// Allows you to set the interceptor which will be used in all subsequent sessions
   3: /// </summary>
   4: public void RegisterInterceptor(IInterceptor interceptor)
   5: {
   6:     Check.Require(interceptor != null, "interceptor may not be null");
   7:  
   8:     _registeredInterceptor = interceptor;
   9: }

This just sets a local static IInterceptor property called _registeredInterceptor.  This is used whenever opening a session, as follows:

   1: /// <summary>
   2: /// Shortcut which just calls into the GetSession overload with the current registered interceptor.
   3: /// If no interceptor has been registered, it will be null
   4: /// </summary>
   5: public ISession GetSession()
   6: {
   7:     return GetSession(_registeredInterceptor);
   8: }
   9:  
  10: /// <summary>
  11: /// Gets a session with or without an interceptor.  This method is not called directly; instead,
  12: /// it gets invoked from GetSession.
  13: /// </summary>
  14: private ISession GetSession(IInterceptor interceptor)
  15: {
  16:     ISession session = ThreadSession;
  17:  
  18:     if (session == null)
  19:     {
  20:         session = interceptor != null ? sessionFactory.OpenSession(interceptor) : sessionFactory.OpenSession();
  21:  
  22:         ThreadSession = session;
  23:     }
  24:  
  25:     Check.Ensure(session != null, "session was null");
  26:  
  27:     return session;
  28: }

So once the IInterceptor is registered, all future calls to GetSession() will open a session using that interceptor (of course you could null it out again later, though I am not sure why you would want to).

Registering the Interceptor

On Application_Start in the global.asax file I call into the NHibernateSessionManager’s RegisterInterceptor method (seen above).  The interceptor I want to register will be resolved by Castle Windsor through IoC/DI.

   1: //Configure the audit interceptor
   2: NHibernateSessionManager.Instance.RegisterInterceptor(windsorContainer.Resolve<IInterceptor>());

To tell Castle Windsor to use the AuditInterceptor, we add the following line to the IoC container registration:

   1: container.AddComponent("auditInterceptor", typeof (NHibernate.IInterceptor), typeof (AuditInterceptor));
 
And we are done – The interceptor has now been written, tested, and integrated into the NHibernate pipeline.  Now we get to try it out.

Trying It Out

We are going to add a new “project” instance to our database.  Basically in the code ASP.NET MVC creates a new Project instance with a Name property set, and makes sure it is Active by default.  The code is kind of beside the point but I’ll post it anyway just make clear how transparent this interceptor is:

   1: [AcceptPost]
   2: public ActionResult CreateProject(Project newProject)
   3: {
   4:     CreateEntity<Project, int>(newProject, "Project");
   5:  
   6:     return this.RedirectToAction(a => a.Projects());
   7: }
   8:  
   9: private void CreateEntity<T, IdT>(T entity, string type) where T : DomainObject<T, IdT>
  10: {
  11:     var lookupEntity = entity as LookupObject<T, IdT>; // If this is a lookup entity we want to make sure isActive is true
  12:  
  13:     if (lookupEntity != null) lookupEntity.IsActive = true;
  14:  
  15:     ValidationHelper<T>.Validate(entity, ModelState);
  16:  
  17:     if (!ModelState.IsValid)
  18:     {
  19:         return;
  20:     }
  21:  
  22:     //Add the new entity
  23:     Repository.OfType<T>().EnsurePersistent(entity);
  24: }

Let’s see (using NH Profiler) what happened to the database:

Audit

Perfect!  We have two Insert Into statements wrapped in a transaction.  The first statement saves the Project (I didn’t show it, but it is a simple Insert Into Projects with name = “NewProject”), and then second statement saves the audit record.  You can see in the Details tab below that the record shows a Project was created by ‘srkirkland’ and saves the exact time of the action.

An update an delete look pretty much the same – here’s a quick screenshot of an update record being audited:

auditUpdate

Ok – everything looks good.  In the last four posts we have created a fully transparent auditing solution for recording all create/update/delete actions without our application.  Pretty powerful stuff!

This is the third post of a multi-part post series on writing simple auditing functionality for an ASP.NET application using NHibernate.  The requirement was that every object modification event in the system should be logged by username and date.  Specifically I don’t need to know exactly which properties were changed (just that a user was updated by whom at what time), but if you do need to save the changed properties there are plenty of hooks to do that.

I’ll update this post with links to the next parts when they become available

The AuditObjectModification Method

In Part 2 we wrote a bunch of tests for the AuditObjectModification method in the AuditInterceptor.cs class.  This method currently just throws a NotImplementedException:

   1: public void AuditObjectModification(object entity, object id, AuditActionType auditActionType)
   2: {
   3:     throw new NotImplementedException();
   4: }

 

I won’t go through each test individually (just the first one as an example), but when writing this iteratively you might want to go through each test and get them to pass one at a time.

Satisfying the First Test

Let’s start making by sure that the first test – AuditObjectModificationSouldNotSaveAuditEntity – can pass by making sure we don’t save audit objects:

   1: public void AuditObjectModification(object entity, object id, AuditActionType auditActionType)
   2: {
   3:     if (entity is Audit) return;
   4: }

 

And after running the tests:

AuditInterceptorTestsFailing

And now the first test passes!

Implementing the Rest

All that is left is to create an instance of the Audit class, fill out some properties, and save it. My implementation is below:

   1: public void AuditObjectModification(object entity, object id, AuditActionType auditActionType)
   2: {
   3:     if (entity is Audit) return;
   4:  
   5:     var audit = new Audit
   6:     {
   7:         AuditDate = DateTime.Now,
   8:         ObjectName = entity.GetType().Name,
   9:         ObjectId = id == null ? null : id.ToString(),
  10:         Username = UserAuth.CurrentUserName
  11:     };
  12:  
  13:     audit.SetActionCode(auditActionType);
  14:  
  15:     AuditRepository.EnsurePersistent(audit);
  16: }

 

Now we rerun the tests:

AuditInterceptorTestsSuccess

Now that’s what I like to see!

Calling the AuditObjectModification method

No we need to call our newly implemented method from the empty overrides of the AuditInterceptor method:

   1: public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
   2: {
   3:     AuditObjectModification(entity, id, AuditActionType.Delete);
   4:  
   5:     base.OnDelete(entity, id, state, propertyNames, types);
   6: }
   7:  
   8: public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
   9: {
  10:     AuditObjectModification(entity, id, AuditActionType.Create);
  11:  
  12:     return base.OnSave(entity, id, state, propertyNames, types);
  13: }
  14:  
  15: public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
  16: {
  17:     AuditObjectModification(entity, id, AuditActionType.Update);
  18:  
  19:     return base.OnFlushDirty(entity, id, currentState, previousState, propertyNames, types);
  20: }

And we are now finished with our Audit Interceptor!

Next time we’ll hook the interceptor into NHibernate and look at the fruits of our labor.

I’m currently working on a project that uses ASP.NET MVC and jQuery to do some Ajax magic, and I ran into a minor (but maybe not obvious) issue when using $.post() against an MVC action that returns a JsonResult.

The core issue is that calling $.get or $.post does not by default treat the returned data as Json, which $.getJSON does. [Also, there is no $.postJSON]

First, let’s setup a little test case using a simple get request.

Calling An Action From jQuery – $.getJSON

   1: function DoAjaxCall() {
   2:     var url = '<%= Url.Action("AjaxTest", "Lookup") %>';
   3:  
   4:     $.getJSON(url,
   5:         null,
   6:         function(data) {
   7:             alert(data.name);
   8:         }
   9:     );
  10: }

Our JavaScript here simple calls $.getJSON(), passing along the action url and expecting back some data with a name property.  The action is implemented as follows:

   1: public ActionResult AjaxTest()
   2: {
   3:     var data = new { name = "TestName"};
   4:  
   5:     return Json(data);
   6: }
 
When we run this, we get the expected result, a little popup that says “TestName”.

Calling the Action using $.post

Let’s make a few changes so that we are posting data to the action and expecting back Json.  The JavaScript changes slightly, with $.getJSON turning into $.post.

   1: function DoAjaxCall() {
   2:     var url = '<%= Url.Action("AjaxTest", "Lookup") %>';
   3:  
   4:     $.post(url,
   5:         null,
   6:         function(data) {
   7:             alert(data.name);
   8:         }
   9:     );
  10: }

All we need to do to the action method is to add an attribute so that it will restrict itself to accepting post requests.

   1: [AcceptPost]
   2: public ActionResult AjaxTest()
   3: {
   4:     var data = new { name = "TestName"};
   5:  
   6:     return Json(data);
   7: }

Note that [AcceptPost] is the same thing as [AcceptVerbs(HttpVerbs.Post)].  It can be found in MVC Contrib.

When we run this example, we get a post to the correct method, but our alert box shows only ‘undefined’.

A trip into FireBug shows that our response was the following: {"name":"TestName"}

Everything looks good there, but the problem is obviously that $.post is not interpreting the return value as JSON.  If you debug into the $.post callback you see that the value of the data parameter is this: "{\"name\":\"TestName\"}".

So it appears the result was stringified.  The fix is to specify the type of data to be returned to the callback function as the fourth parameter to $.post().  Possible values are: "xml", "html", "script", "json", "jsonp", or "text".

Let’s try specifying the type parameter and setting it to ‘json’:

   1: function DoAjaxCall() {
   2:     var url = '<%= Url.Action("AjaxTest", "Lookup") %>';
   3:  
   4:     $.post(url,
   5:         null,
   6:         function(data) {
   7:             alert(data.name);
   8:         },
   9:         'json'
  10:     );
  11: }

Success!  If we look at the POST response it is the same as before, but now we get the proper alert message to display “TestName”.  If we debug through with firebug we see data is now an object with a single property of name (that has the value “TestName”).

AlertJson

What I learned

jQuery’s $.get and $.post do not natively parse the result as JSON.  $.get has a helper called $.getJSON which will do the job for you, though you can achieve the same result by using $.get and passing the string ‘json’ as the fourth “type” parameter.  Since there is no $.postJSON, if you are doing a jQuery ajax post and expecting a JsonResult, you must always pass ‘json’ as the fourth parameter to $.post.

This is the second post of a multi-part post series on writing simple auditing functionality for an ASP.NET application using NHibernate.  The requirement was that every object modification event in the system should be logged by username and date.  Specifically I don’t need to know exactly which properties were changed (just that a user was updated by whom at what time), but if you do need to save the changed properties there are plenty of hooks to do that.

I’ll update this post with links to the next parts when they become available

  • Part 1
  • Part 2 (you are here)
  • ……
  • Part N

The Audit Interceptor Class

In order to create an Interceptor and hook into the NHibernate entity lifecycle, you have a few options.  You can implement IInterceptor (NHibernate.IInterceptory) directly but I believe it is considered best practice instead to derive your class from EmptyInterceptor since the IInterceptor interface can (and will) change between versions.

   1: public class AuditInterceptor : EmptyInterceptor { }

From this empty AuditInterceptor class you can override a number of methods from the EmptyInterceptor base class.  We want to hook into create/update/delete events.  Create and Delete are easy to find – you just override the OnSave() and OnDelete() methods.  For updates you want the OnFlushDirty() method, which makes sense once you think about it.

So now our class looks like this:

   1: public class AuditInterceptor : EmptyInterceptor
   2: {
   3:     public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
   4:     {
   5:         return base.OnSave(entity, id, state, propertyNames, types);
   6:     }
   7:  
   8:     public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
   9:     {
  10:         return base.OnFlushDirty(entity, id, currentState, previousState, propertyNames, types);
  11:     }
  12:  
  13:     public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
  14:     {
  15:         base.OnDelete(entity, id, state, propertyNames, types);
  16:     }
  17: }

Now we’ll create a general method for auditing object modifications that each of the above methods will call:

   1: public void AuditObjectModification(object entity, object id, AuditActionType auditActionType)
   2: {
   3:     throw new NotImplementedException();
   4: }

So now you have the methods exposed – time to test for our expected behaviors.

Testing The Audit Interceptor

The Audit Interceptor has a constructor that takes two arguments: IUserAuth, which is a wrapper around the User.Identity.Name context, and IRepository<Audit> which provides strongly typed access into the NHibernate repository.  These arguments (dependencies) are injected using Castle Windsor in production, but while testing will will provide stubs of them directly.

   1: public AuditInterceptor AuditInterceptor { get; set; }
   2:  
   3: public IUserAuth UserAuth { get; set; }
   4: public IRepository<Audit> AuditRepository { get; set; }
   5:  
   6: public AuditInterceptorTests()
   7: {
   8:     UserAuth = MockRepository.GenerateStub<IUserAuth>();
   9:     AuditRepository = MockRepository.GenerateStub<IRepository<Audit>>();
  10:  
  11:     AuditInterceptor = new AuditInterceptor(UserAuth, AuditRepository);
  12: }

Using Rhino Mocks to stub out the IUserAuth and IRepository<Audit> we can now begin testing the audit interceptor.

Tests:

*AuditObjectModificationSouldNotSaveAuditEntity – First we’ll test that the AuditObjectModification method will not save an audit object.  If we did then it would create an infinite loop of self auditing and I’d rather not crash the web server.  Basically we will just pass an audit object and make sure that the save method (repository.EnsurePersistent()) was not called.

   1: [TestMethod]
   2: public void AuditObjectModificationSouldNotSaveAuditEntity()
   3: {
   4:     AuditInterceptor.UserAuth.Expect(a => a.CurrentUserName).Return("currentUser");
   5:  
   6:     AuditInterceptor.AuditObjectModification(new Audit(), null, AuditActionType.Update);
   7:  
   8:     AuditRepository.AssertWasNotCalled(a => a.EnsurePersistent(Arg<Audit>.Is.Anything));
   9: }

*AuditObjectModificationSavesTheCurrentUser – Next we want to make sure that the current user is saved along with an audit entry.

   1: [TestMethod]
   2: public void AuditObjectModificationSavesTheCurrentUser()
   3: {
   4:     Audit audit = null;
   5:  
   6:     AuditInterceptor.UserAuth.Expect(a => a.CurrentUserName).Return("currentUser");
   7:  
   8:     AuditRepository
   9:         .Expect(a => a.EnsurePersistent(Arg<Audit>.Is.Anything))
  10:         .WhenCalled(a => audit = (Audit)a.Arguments.First());
  11:  
  12:     AuditInterceptor.AuditObjectModification(new object(), null, AuditActionType.Update);
  13:  
  14:     Assert.AreEqual("currentUser", audit.Username);
  15: }

*AuditObjectModificationLeavesObjectIdNull – Continuing along this path we now want the audit method to be able to handle null ids since newly created objects will not yet have ids.

   1: [TestMethod]
   2: public void AuditObjectModificationLeavesObjectIdNull()
   3: {
   4:     Audit audit = null;
   5:  
   6:     AuditInterceptor.UserAuth.Expect(a => a.CurrentUserName).Return("currentUser");
   7:  
   8:     AuditRepository
   9:         .Expect(a => a.EnsurePersistent(Arg<Audit>.Is.Anything))
  10:         .WhenCalled(a => audit = (Audit)a.Arguments.First());
  11:  
  12:     AuditInterceptor.AuditObjectModification(new object(), null, AuditActionType.Update);
  13:  
  14:     Assert.IsNull(audit.ObjectId);
  15: }

*AuditObjectModificationSetsObjectName – Now I want to make sure that the object passed in has its type saved in the audit class’s ObjectName property.

   1: [TestMethod]
   2: public void AuditObjectModificationSetsObjectName()
   3: {
   4:     var sampleObject = new RouteConfigurator();
   5:  
   6:     Audit audit = null;
   7:  
   8:     AuditInterceptor.UserAuth.Expect(a => a.CurrentUserName).Return("currentUser");
   9:  
  10:     AuditRepository
  11:         .Expect(a => a.EnsurePersistent(Arg<Audit>.Is.Anything))
  12:         .WhenCalled(a => audit = (Audit)a.Arguments.First());
  13:  
  14:     AuditInterceptor.AuditObjectModification(sampleObject, null, AuditActionType.Update);
  15:  
  16:     Assert.AreEqual("RouteConfigurator", audit.ObjectName);
  17: }

*AuditObjectModificationCallsEnsurePersistant – The last test will just make sure that when everything goes ok, a new audit instance will be saved.

   1: [TestMethod]
   2: public void AuditObjectModificationCallsEnsurePersistant()
   3: {
   4:     AuditInterceptor.UserAuth.Expect(a => a.CurrentUserName).Return("currentUser");
   5:  
   6:     AuditRepository.Expect(a => a.EnsurePersistent(Arg<Audit>.Is.Anything));
   7:  
   8:     AuditInterceptor.AuditObjectModification(new object(), null, AuditActionType.Update);
   9:  
  10:     AuditRepository.AssertWasCalled(a=>a.EnsurePersistent(Arg<Audit>.Is.Anything));
  11: }

Results

Of course since the AuditObjectModification method just throws a NotImplementedException(), all of these tests are going to currently fail.

AuditInterceptorTestsFailing

So the next step is to implement AuditObjectModification() and get all of the tests to turn green.  We’ll work on that next time!

This is the beginning of a multi-part post series on writing simple auditing functionality for an ASP.NET application using NHibernate.  The requirement was that every object modification event in the system should be logged by username and date.  Specifically I don’t need to know exactly which properties were changed (just that a user was updated by whom at what time), but if you do need to save the changed properties there are plenty of hooks to do that.

I’ll update this post with links to the next parts when they become available

The Audit Class

The Audit class will inherit from a base DomainObject<> which basically provides an Id property for all objects and helper methods for validation and transient checks (this is pretty common architecture when using NHibernate).  I’m using EntLib validation on the class and to satisfy a particular requirement I am going to set the action type to C/U/D depending on the action.  Here’s the code:

   1: public class Audit : DomainObject<Audit,Guid>
   2: {
   3:     [RequiredValidator]
   4:     [StringLengthValidator(50)]
   5:     public virtual string ObjectName { get; set; }
   6:  
   7:     [IgnoreNulls]
   8:     [StringLengthValidator(50)]
   9:     public virtual string ObjectId { get; set; }
  10:  
  11:     [RequiredValidator]
  12:     [StringLengthValidator(1)]
  13:     public virtual string AuditActionTypeId { get; set; }
  14:  
  15:     [RequiredValidator]
  16:     [StringLengthValidator(256)]
  17:     public virtual string Username { get; set; }
  18:  
  19:     public virtual DateTime AuditDate { get; set; }
  20:  
  21:     public virtual void SetActionCode(AuditActionType auditActionType)
  22:     {
  23:         switch (auditActionType)
  24:         {
  25:             case AuditActionType.Create:
  26:                 AuditActionTypeId = "C";
  27:                 break;
  28:             case AuditActionType.Update:
  29:                 AuditActionTypeId = "U";
  30:                 break;
  31:             case AuditActionType.Delete:
  32:                 AuditActionTypeId = "D";
  33:                 break;
  34:             default:
  35:                 throw new ArgumentOutOfRangeException("auditActionType");
  36:         }
  37:     }
  38: }
  39:  
  40: public enum AuditActionType
  41: {
  42:     Create, Update, Delete
  43: }

Audit Repository Testing

Before I move on to creating the interceptor I am going to run a few tests against a SQLite repository to make sure the Audit class can save and validate properly.

   1: [TestMethod]
   2: public void ValidationAuditWithoutSettingActionCodeShouldFail()
   3: {
   4:     var audit = new Audit
   5:     {
   6:         AuditDate = DateTime.Now,
   7:         ObjectName = "ValidObject",
   8:         Username = "ValidUsername"
   9:     };
  10:  
  11:     var isValid = ValidateBusinessObject<Audit>.IsValid(audit);
  12:  
  13:     Assert.AreEqual(false, isValid, "An audit requires an action code");
  14: }
  15:  
  16: [TestMethod]
  17: public void ValidationBlankAuditShouldFail()
  18: {
  19:     var audit = new Audit();
  20:  
  21:     var isValid = ValidateBusinessObject<Audit>.IsValid(audit);
  22:  
  23:     Assert.AreEqual(false, isValid, "A blank audit should not be valid");
  24: }

And to test that saving works properly:

   1: [TestMethod]
   2: public void CanSaveCompleteAndValidAudit()
   3: {
   4:     var audit = new Audit
   5:                     {
   6:                         AuditDate = DateTime.Now,
   7:                         ObjectName = "ValidObject",
   8:                         ObjectId = "ValidId",
   9:                         Username = "ValidUsername"
  10:                     };
  11:     
  12:     audit.SetActionCode(AuditActionType.Update);
  13:  
  14:     using (var ts = new TransactionScope())
  15:     {
  16:         _auditRepository.EnsurePersistent(audit);
  17:  
  18:         ts.CommitTransaction();
  19:     }
  20:  
  21:     Assert.AreEqual(false, audit.IsTransient(), "Audit should have been saved");
  22: }
  23:  
  24: [TestMethod]
  25: public void CanSaveAuditWithoutObjectId()
  26: {
  27:     var audit = new Audit
  28:     {
  29:         AuditDate = DateTime.Now,
  30:         ObjectName = "ValidObject",
  31:         Username = "ValidUsername"
  32:     };
  33:  
  34:     audit.SetActionCode(AuditActionType.Update);
  35:  
  36:     using (var ts = new TransactionScope())
  37:     {
  38:         _auditRepository.EnsurePersistent(audit);
  39:  
  40:         ts.CommitTransaction();
  41:     }
  42:  
  43:     Assert.AreEqual(false, audit.IsTransient(), "Audit should have been saved");
  44: }

What Did This Accomplish?

Not much yet.  We have an audit class that is behaving how we want, and eventually we are going to use it to save audit records.  Next time we’ll create an IInterceptor which will hook into create/update/delete events in NHibernate.

More Posts Next page »