views:

18

answers:

1

I'm trying to map a collection of enums in NHibernate using Fluent NHibernate, and then perform queries against the contents of that enum collection, but the system throws exceptions every time.

I have a Widget class, mapped to the Widget table. There is also a WidgetType enum, and a single instance of Widget can have many WidgetTypes, mapped through the WidgetTypes property. The property needs to be mapped to a separate table, WidgetTypeRef, with two integer columns: WidgetId and WidgetType.

public class Widget
{
  /* omitted */
  public IList<WidgetType> WidgetTypes { get; set; }
}
public enum WidgetType
{
  SomeType = 0,
  SomeOtherType = 1,
  YetOneMoreType = 2
}
public partial class WidgetMapping : IAutoMappingOverride<Widget>
{
  public void Override(AutoMapping<Widget> mapping)
  {
    /* omitted */
    mapping.HasMany(w => w.WidgetTypes)
      .Table("WidgetTypeRef")
      .KeyColumn("WidgetId")
      .Element("WidgetType");
  }
}

I have no control over the DB schema; the schema cannot be changed. The schema must store the integer values of the WidgetTypes associated with the Widget, and cannot be converted to match the string version of the enum. I'm very much trying to keep the strong typing of the enum, and am avoiding creating a new entity for the Ref table.

Other classes have enum-based type properties that work with the ".CustomType(typeof(someTypeEnum)" configuration, but there is no CustomType property on a HasMany mapping. Using this HasMany mapping above, queries into the collection throw a "Could not determine member type" exception.

Is this even possible? How should the property be set up? How should the Fluent mapping be configured? How do I query in to the collection (my query only needs the equivalent of an Any or Contains)?

A: 
public class EnumToIntConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum);
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}

Using that convention, having the following Enum:

public enum Status
{
    Inactive = 0,
    Active = 1,
    Canceled = 2
}

And setting it up like this (should work with Fluent mappings too):

var cfg = Fluently.Configure()
                .Database(configurer)
                .Mappings(m =>
                              {
                                  m.AutoMappings.Add(AutoMap.Assemblies(Assembly.GetExecutingAssembly())
                                                         .Where(type => AutomapAssemblies.Contains(type.Namespace))
                                                         .Conventions.Add<EnumToIntConvention>()  // Magic code goes here!
                                                         .Conventions.Add());
                              });

Will save the integer value instead of the string value.

Rafael Belliard