(Ab)Using Conventions To Enforce Good Practices

15 commentsWritten on January 26th, 2012 by
Categories: NHibernate, Performance

I always tell people to explicitly specify the lengths of their string properties in their NHibernate mappings for performance reasons. If you don't specify them, the ADO.NET parameter lengths of those strings will always be set to the length of the actual string value that's been assigned to the parameter. This is a problem for SQL Server, because it can't cache those statements as efficiently as it would if the parameter lengths were always the same for a given statement. Simply put, if you don't specify the lengths, SQL Server's statement cache gets polluted with a bunch of statements that are often the same, but they're considered to be different simply because of the lengths of those string parameters. And this can really hurt the performance of your application.

Of course, not everyone remembers to set those lengths, so I thought it'd be great if I could force people to do this. With a little creative use of Fluent NHibernate's conventions, it's quite easy to enforce this:

public class StringsMustHaveLengthConvention: IPropertyConvention, IPropertyConventionAcceptance
{
    public void Apply(IPropertyInstance instance)
    {
        var msg = string.Format("The string property '{0}' of type '{1}' does not have a length value specified, " +
            "which is required for performance reasons. Add something like this to your mapping override:\r\n" + 
            "\tmapping.Map(e => e.{0}).Length(50); // with an appropriate length for this property",
            instance.Property.Name, instance.EntityType.Name);

        throw new MappingException(msg);
    }

    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Type == typeof(string)).Expect(x => x.Length == 0);
    }
}

With that convention in place, you won't even be able to run your code until you've specified the string lengths :)

  • Boyd D Garrett Sr

    How about a column of type nvarchar(MAX)?

    • http://davybrion.com Davy Brion

      the DBA’s aren’t gonna like that

      • jdn

        Well, perhaps.  I’m a DBA, I sometimes like it.

        • http://davybrion.com Davy Brion

          Sometimes being the key word in that sentence? :)

          Nvarchar(MAX) makes sense in some situations, but I really can’t imagine it being a good idea to use that as the default for all string columns, which i’ve seen some teams do.

          • Boyd D Garrett Sr

            It sure wouldn’t be a good idea for all string columns, but in some cases it’s the best path to follow.

            So I’m guessing that there’s no SQL Server caching mechanism available in this circumstance, right?

  • http://realfiction.net Frank Quednau

    Length issues bit me in a different way in that when not specifying the length for an nvarchar(max) column, the string would be capped at 4000. So yes, forcing to specify the length is a nice idea :)

  • Dan Kennedy

    It might help someone but I use DataAnnotations attributes on the domain model for validation and following convention picks up the StringLength attribute for each property and uses that.

    public class LengthValidationAttributeToMappingConvention : IPropertyConvention, IPropertyConventionAcceptance
        {
            public void Apply(IPropertyInstance instance)
            {
                var attribute =
                    instance.Property.MemberInfo.GetCustomAttributes(typeof(StringLengthAttribute), false)
                        .FirstOrDefault() as StringLengthAttribute;

                if (attribute != null)
                    instance.Length(attribute.MaximumLength);
            }

            public void Accept(IAcceptanceCriteria criteria)
            {
                criteria.Expect(x => x.Length, Is.Not.Set);
                criteria.Expect(
                    x => x.Property.MemberInfo.GetCustomAttributes(typeof(StringLengthAttribute), false).Length > 0);
            }
        }

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1031

  • http://profiles.google.com/joe.marquardt Joe Marquardt

    Weird, I assumed it’d just use the (FNH or is it NH’s) default length of 255.

    • OLOLO

      Me too.

  • Koen Verheyen

    Next post: concatenating inside string.Format()?

    • http://davybrion.com Davy Brion

      glad to see you’ve still got your priorities straight ;)

  • Anonymous

    Is not specifying a length for string parameters only an issue for SQL Server, or applies this to other databases as well?

    • http://davybrion.com Davy Brion

      not sure if it’s also an issue on other databases… really depends on how they do their statement caching i guess

  • Antanas Spartanas

    “not everyone remembers to set those lengths, so I thought it’d be great if I could force people to do this”StringsMustHaveLengthConvention : the aspect assurance class text follows…If I remember correctly it was one line of SQL like “fieldName nvarchar(50)” to enforce all this stuff in old days.What was wrong with SQL then?