R – SubSonic 3 ActiveRecord generated code with warnings

activerecordsubsonicsubsonic3

While using SubSonic 3 with ActiveRecord T4 templates, the generated code shows many warnings about CLS-compliance, unused items, and lack of GetHashCode() implementation.

In order to avoid them, I did the following modifications:

// Structs.tt
[CLSCompliant(false)]                                    // added
public class <#=tbl.CleanName#>Table: DatabaseTable
{ ...

// ActiveRecord.tt
[CLSCompliant(false)]                                    // added
public partial class <#=tbl.ClassName#>: IActiveRecord
{
    #region Built-in testing
    #pragma warning disable 0169                         // added
    static IList<<#=tbl.ClassName#>> TestItems;
    #pragma warning restore 0169                         // added
    ...

    public override Int32 GetHashCode()                  // added
    {
      return this.KeyValue().GetHashCode();
    }

    ...

Is there a better way to get rid of the warnings? Or a better GetHashCode() implementation?

Best Answer

Currently, the only way to get rid of the warnings is to update your t4 templates and submit a bug/fix to Rob. Or wait until somebody else does.

As for the GetHashCode implementation, I don't think you're going to find a good way to do this through templates. Hash code generation is very dependent on what state your object contains. And people with lots of letters after their name work long and hard to come up with hash code algorithms that are fast and return results with low chances of collision. Doing this from within a template that may generate a class with millions of different permutations of the state it may hold is a tall order to fill.

Probably the best thing Rob could have done would be to provide a default implementation that calls out to a partial method, checks the result and returns it if found. Here's an example:

public partial class Foo
{
    public override int GetHashCode()
    {
        int? result = null;
        TryGetHashCode(ref result);
        if (result.HasValue)
            return result.Value;
        return new Random().Next();
    }

    partial void TryGetHashCode(ref int? result);
}

public partial class Foo
{
    partial void TryGetHashCode(ref int? result)
    {
        result = 5;
    }
}

If you compile this without the implementation of TryGetHashCode, the compiler completely omits the call to TryGetHashCode and you go from the declaration of result to the check to see if it has value, which it never will, so the default implementation of the hash code is returned.