I wrote a post about a month ago about using xVal with NHibernate Validator 1.2 which solved a problem I was having upgrading the xVal ‘in-the-box’ provider to work with a newer version of NHibernate Validator. There was a caveat that my solution only worked for ValidatorMode.UseAttribute and I wouldn’t catch XML or Loquacious (or other?) validation.  This seemed to work OK, but Fabio Maulo wrote a comment to that post saying NHV has metadata which should be the same no matter which validation mode was used.

So I decided to investigate how I could get the metadata without resorting to the NHibernate.Validator.Mappings validationMode specific engines (I was using ReflectionClassMapping, but there are others like XmlClassMapping).

I think I found the solution (at least, it does work…) in the ValidatorEngine’s GetClassValidator method.  GetClassValidator takes in a System.Type and returns an IClassValidator for that Type.  Once you have the class validator, you can get constraints for any member of that class by using GetMemberConstraints(memberName).

After getting the constraints you are onto the easy part, which is basically just mapping the rules into xVal rules and the code is much the same as before.

So without further ado, here is the new ValidatorRulesProvider implementation:

public class ValidatorRulesProvider : CachingRulesProvider
{
    private readonly RuleEmitterList<IRuleArgs> _ruleEmitters;
 
    public ValidatorRulesProvider()
    {
        _ruleEmitters = new RuleEmitterList<IRuleArgs>();
 
        _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<NotNullAttribute>(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 = new ValidatorEngine().GetClassValidator(type);
 
        var rules = from member in type.GetMembers()
                    where member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property
                    from constraint in classMapping.GetMemberConstraints(member.Name).OfType<IRuleArgs>()
                    // All NHibernate Validation validators attributes must implement this interface
                    from rule in ConvertToXValRules(constraint)
                    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 ruleArgs)
    {
        foreach (var rule in _ruleEmitters.EmitRules(ruleArgs))
        {
            if (rule != null)
            {
                rule.ErrorMessage = MessageIfSpecified(ruleArgs.Message);
                yield return rule;
            }
        }
    }
 
    private static 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 string MessageIfSpecified(string message)
    {
        // We don't want to display the default {validator.*} messages
        if ((message != null) && !message.StartsWith("{validator."))
            return message;
        return null;
    }
}

Of course 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));

If you aren’t using xVal I would highly recommend it, and if you are using ASP.NET MVC and not using xVal, what are you waiting for?  They are a great match!

Enjoy!

I recently setup my first continuous integration build server using JetBrains’ TeamCity product, and it couldn’t have been much simpler.  However I kept running into an issue with my test projects whenever I was using NHibernate or SQLite (very useful or regression tests against an in-memory database).

The Problem

Everything would run correctly locally using MSTest, but when TeamCity ran the tests I would get the following error:

Unable to load type 'NHibernate.ByteCode.Castle.ProxyFactoryFactory,
NHibernate.ByteCode.Castle' during configuration of proxy factory
class.
Possible causes are:
- The NHibernate.Bytecode provider assembly was not deployed.
- The typeName used to initialize the 'proxyfactory.factory_class'
property of the session-factory section is not well formed.

Solution:
Confirm that your deployment folder contains one of the following
assemblies:

Of course I’ve seen this error before and I double checked that I had a reference to the NHibernate bytecode provider and that my NHibernate.config file was configured correctly.  I thought maybe this was a x86 vs x64 issue, but that also did not seem to be the case.

Eventually running MSTest locally through the command line reproduced the error, and after I searched around for a while I found that NHibernate.ByteCode.Castle.dll was not be deployed into the specific TestProject\bin\Release\ folder (and neither was SQLite).

I made sure my test project had a reference to both DLLs, that is was set to copy local, and still they were not copied to my “deployment” directory.

The Reason

From what I could gather, msbuild was being ‘smart’ and optimizing my build such that the bytecode and SQLite DLLs were not being included since they were never directly referenced in code.  This is because they are referenced only in the NHibernate.config file.

The Solution (aka Major Hack)

In order to get the proper DLLs included in the build I had to reference them somewhere in code, so I made a totally unreachable class that should never be instantiated, as follows:

/// <summary>
/// NOTE: This is a hack to make sure that the castle 
/// and SQLLite assemblies are copied into the Test deployemnt directory
/// This class never gets called.
/// </summary>
public class ContinuousIntegrationDeploymentHack
{
    public ContinuousIntegrationDeploymentHack()
    {
        new NHibernate.ByteCode.Castle.ProxyFactoryFactory();
        new System.Data.SQLite.SQLiteException();
 
        throw new Exception("This class should never be called or instantiated");
    }
}

Adding this class anywhere into my test project does the trick – the castle bytecode and SQLite DLLs are now copied into the deployment directory and all tests run correctly.

The Future

I am sure the proper way to solve this was to use MSBuild or NAnt and copy the files correctly, but I am currently using the easy TeamCity sln2008 runner and thus there are no custom tasks that I can run.

I’ve recently switched from the Enterprise Library Validation Application Block to using NHibernate Validators. If you are not familiar with the NHibernate Validator project, they are part of the NHibernate Contrib project and offer Validation constraints, and validation runner, and tight integration with NHibernate (especially great if you use NHibernate to generate your DB).

One of the main reasons for my switch was integration with xVal (which EntLib Validation’s implementation prevents), and also to get some advanced but common validators like Email, Size, NotNullNotEmpty.

Anyway, I would recommend that you look at NHibernate Validator if you use NHibernate and are looking for a validation framework.  However, this post is about writing a new validator that will integrate with NHibernate Validators – the Required validator.

Motivation for the Required Validator

Many of the varchar/nvarchar columns in my database are set to NotNull, and I also want to go a step further and make sure that they can’t be empty strings or just spaces.

There does exist a NotNull validator and a NotEmpty validator which work correctly on their own or combined, and there is a NotNullNotEmpty validator as well.

However, the NotNullNotEmpty validator only works on IEnumerable types, even though the NotNull and NotEmpty validator work on strings.

So What Can We Do?

First, and simplest, is to put both a NotNull and a NotEmpty validator on each string property.  In my case, our team was already familiar with a required validator and I found they were confused when the NotNullNotEmpty validator didn’t combine the two.

Option 2 is to write your own validator, which I did with the Required validator.

Required Validator

In order to make a custom NHibernate Validtor with accompanying Attribute, you should write two classes.

The first is the validator class, which should implement IValidator (NHibernate.Validator.Engine.IValidator).  This just defines one method, IsValid() which returns a bool.  The whole implementation is as follows:

/// <summary>
/// Required Validator means the value can not be null or empty (or only spaces)
/// </summary>
public class RequiredValidator : IValidator
{
    public bool IsValid(object value, IConstraintValidatorContext constraintValidatorContext)
    {
        if (value == null)
        {
            return false;
        }
 
        var check = value as string;
        if (check != null)
        {
            return !string.Empty.Equals(check.Trim());
        }
 
        var ev = value as IEnumerable;
        if (ev != null)
        {
            return ev.GetEnumerator().MoveNext();
        }
 
        return false;
    }
}

Basically this first checks if the value is null – if it is we return that this object is not valid.  After that we just copy the code from the NotEmpty validator and check to see if a trimmed version of the string is not equal to string.Empty.  It also can check for empty Enumerables just like NotNullNotEmpty.

Required Attribute

The second class to write is an attribute which handles the parameters and message, and uses the ValidatorClass[] attribute to tell the validation runner which validator it executes.

[Serializable]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
[ValidatorClass(typeof(RequiredValidator))]
public class RequiredAttribute : Attribute, IRuleArgs
{
    public string Message
    {
        get { return _message; }
        set { _message = value; }
    }
 
    private string _message = "{validator.notNullNotEmpty}"; //Use the not empty language by default
}

Usage

Of course you can use this just like any other NHibernate Validator and even side by side with existing validators:

[Length(50)]
[Required]
public virtual string FirstName { get; set; }
 
[Length(50)]
[Required]
public virtual string LastName { get; set; }

Integration with xVal

Integration with xVal just takes one more line inside the validation rules provider (see my post on using xVal with NHibernate Validator)

ruleEmitters.AddSingle<RequiredAttribute>(x=>new RequiredRule());
 

Conclusion

We now have a Required Validator that we can use in our project, and it even integrates with xVal.  Now this was a really simple change, but I’ll write soon about some more complex custom validators which might prove helpful.

I’ve been playing with Visual Studio 2010 Beta a little and one of my favorite new features (and there are many) is the new web.config transformation feature. 

Web.config transformations are setup so there is one configuration “delta” for each build configuration that you have (default are Debug and Release).  When you build your solution (as a package, when you publish, etc) your original web.config is transformed according to the settings in your web.debug.config file (assuming the debug configuration).

There are plenty of blogs and msdn references out there for you to look at but I thought I’d write a quick post that would get you up to date on how to do the some common web.config transformations.

First, A Tiny Iota (That guy was great!) of XDT Review

As it pertains to web.config replacements, you need to know that every XML element can have two xdt attributes: xdt:Tranform and xdt:Locator

  • Transform: What you want to do with the XML element
    • You might want to Replace it, set an attribute (SetAttribute), remove an attribute (RemoveAttribute), etc.
  • Locator: Where is the element that needs transformation?
    • You probably want to transform an element that matches (Match) a specific attribute value

Case 1: Replacing all AppSettings

This is the overkill case where you just want to replace an entire section of the web.config.  In this case I will replace all AppSettings in the web.config will new settings in web.release.config.  This is my baseline web.config appSettings:

<appSettings>
  <add key="KeyA" value="ValA"/>
  <add key="KeyB" value="ValB"/>
</appSettings>

Now in my web.release.config file, I am going to create a appSettings section except I will include the attribute xdt:Transform=”Replace” since I want to just replace the entire element.  I did not have to use xdt:Locator because there is nothing to locate – I just want to wipe the slate clean and replace everything.

<appSettings xdt:Transform="Replace">
  <add key="ProdKeyA" value="ProdValA"/>
  <add key="ProdKeyB" value="ProdValB"/>
  <add key="ProdKeyC" value="ProdValC"/>
</appSettings>

 

Note that in the web.release.config file my appSettings section has three keys instead of two, and the keys aren’t even the same.  Now let’s look at the generated web.config file what happens when we publish:

<appSettings>
  <add key="ProdKeyA" value="ProdValA"/>
  <add key="ProdKeyB" value="ProdValB"/>
  <add key="ProdKeyC" value="ProdValC"/>
</appSettings>

Just as we expected – the web.config appSettings were completely replaced by the values in web.release config.  That was easy!

Case 2: Replacing a specific AppSetting’s value

Ok, so case 1 was the nuclear option, what about doing something a little more practical?  Let’s go back to our original AppSettings web.config example:

<appSettings>
  <add key="KeyA" value="ValA"/>
  <add key="KeyB" value="ValB"/>
</appSettings>

 

Let’s say that we just want to replace KeyB’s value to something more suited for a production environment.  This time we get to use both xdt:Transform and xdt:Locator.

Our strategy will be to define (in web.release.config) an appSettings section that has just the replacements we want to make.  It will initially look like this:

<appSettings>
  <add key="KeyB" value="ProdValA" />
</appSettings>

Now we want to add in the transforms so that we replace any appSetting that matches this key (KeyB).

<appSettings>
  <add key="KeyB" value="ProdValA" xdt:Transform="Replace" xdt:Locator="Match(key)" />
</appSettings>

 

Once we publish our resulting web.config file looks like this:

<appSettings>
  <add key="KeyA" value="ValA"/>
  <add key="KeyB" value="ProdValA"/>
</appSettings>

 

Perfect – We replaced KeyB but left KeyA (and any other keys if they were to exist) alone.

Case 3: Replace Compilation Debug=”true”

This is a simple one because Microsoft gives it to us out of the box – but I’ll reproduce it here anyway because it illustrates a common scenario and shows that there are more transforms then just Replace:

<system.web>
  <compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>

There are also ways to SetAttributes, Remove elements, Insert elements, and much more

To Infinity – And Beyond

We have obviously just scratched the surface, but for now that’s as deep as I need to go.  Until next time, you can look at the MSDN reference for Web.Config transformations at http://msdn.microsoft.com/en-us/library/dd465326%28VS.100%29.aspx.

Enjoy!

When using ASP.NET MVC you will eventually want to do a select (drop down) or even a multiple select list, and your first though might be to use <%= Html.DropDownList %>.  Unfortunately you will soon notice that ASP.NET MVC always looks for a match between the name of the dropdown and a property on the model, and if it finds a match, it OVERRIDES the selected value(s) of the select list.  Now of course not being able to reliably set the selected value(s) is a major problem – if you Google this you will get a ton of results and most people solve the issue by just changing the name of the Html.DropDownList(“name”) to something that doesn’t match a model property.

Of course we can put this into the “hack” or “workaround” category, and I don’t like doing that on my nice shiny MVC project.  However, the biggest problem is if you are doing advanced model binding (I have my own impl, but as an example look at SharpArchitecture), changing the name of the drop down will break model binding and model state errors.

So – how do we use drop down lists and maintain our control over naming and selected items?  Enter MVCContrib’s FluentHTML:

MVCContrib FluentHTML Select and MultiSelect

Once you copy the MVCContrib.FluentHTML DLL into your project and resolve the namespace, you get a whole bunch of nice Html helpers right on the base View Data Container.  We want to look at the Select and MultiSelect options, which you would use as follows:

<%= this.MultiSelect("User.Projects").Options(Model.Projects) %>

Now moving past the default case, you’ll want to choose which properties to bind and maybe which values are selected.  The following is how I attempted to do this at first:

<%= this.MultiSelect("User.Projects")
.Options(new MultiSelectList(Model.Projects, "ID", "Name",
Model.User.Projects.Select(a=>a.ID)))%>

There is also another option which involves calling .Selected() instead of putting the selected values directly in the MultiSelectList ctor.
 
<%= this.MultiSelect("User.Projects")
.Options(new MultiSelectList(Model.Projects, "ID", "Name"))
.Selected(Model.User.Projects.Select(a=>a.ID)) %>

The Problem

When using NHibernate, or likely any other ORM, you run into a subtle problem.  Occasionally you will get the following errors when binding MultiSelect (note this problem also happens using the Select):

Object does not match target type.

Exception Details: System.Reflection.TargetException: Object does not match target type.

It takes a lot of debugging, but you eventually discover that this only occurs when binding to proxied instances.  So for this example, I had NHibernate get me a list of all Projects and since lazy-load was set to true (the default), some of these came back as proxies.  When trying to get the ID value of these proxies, you get the exception thrown from within System.Reflection.
 

The Solution

The solution here is to use an overload of Options() to put the valueField and textField into the method call and NOT into the constructor of a SelectList/MultiSelectList.  You can either use strings of selectors, as you can see below:

<%= this.MultiSelect("User.Projects")
.Options(Model.Projects, "ID", "Name")
.Selected(Model.User.Projects.Select(a=>a.ID)) %>
 
OR

<%= this.MultiSelect("User.Projects")
.Options(Model.Projects, a=>a.ID, a=>a.Name)
.Selected(Model.User.Projects.Select(a=>a.ID)) %>
 
Now, there is no reflection errors.  Enjoy!

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!

More Posts Next page »