33

I am following the http://wiki.fluentnhibernate.org/Getting_started tutorial to create my first NHibernate project with Fluent NHibernate

I have 2 tables

1) Account with fields

Id
AccountHolderName
AccountTypeId

2) AccountType with fields

Id
AccountTypeName

Right now the account types can be Savings or Current So the table AccountTypes stores 2 rows 1 - Savings 2 - Current

For AccoutType table I have defined enum

public enum AccountType {
    Savings=1,
    Current=2
}

For Account table I define the entity class

public class Account {
    public virtual int Id {get; private set;}
    public virtual string AccountHolderName {get; set;}
    public virtual string AccountType {get; set;}
}

The fluent nhibernate mappings are:

public AgencyMap() {
    Id(o => o.Id);
    Map(o => o.AccountHolderName);
    Map(o => o.AccountType);
}

When I try to run the solution, it gives an exception - InnerException = {"(XmlDocument)(2,4): XML validation error: The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has incomplete content. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'ur...

I guess that is because I have not speciofied any mapping for AccountType.

The questions are:

  1. How can I use AccountType enum instead of a AccountType class?
  2. Maybe I am going on wrong track. Is there a better way to do this?

Thanks!

3 Answers 3

62

The following apparently no longer works https://stackoverflow.com/a/503327/189412

How about just doing this:

public AgencyMap() {
    Id(o => o.Id);
    Map(o => o.AccountHolderName);
    Map(o => o.AccountType).CustomType<AccountType>();
}

The custom type handles everything :)

Sign up to request clarification or add additional context in comments.

4 Comments

Aha! I was using CustomSqlType and it wasn't working! What I needed was CustomType!
AcountType is an Enum so all the entities will be dirty after loading. See stackoverflow.com/questions/3531937/…
This seems right, but generates "Phantom" updates. The correct mapping would be: CustomType<AccountType>. Otherwise, like schoetbi said, the entities are dirty after loading. Source: groups.google.com/forum/#!searchin/fluent-nhibernate/enum/…
This no longer works! You need to use .CustomType<GenericEnumMapper<T>>() in more recent versions of FNH. See stackoverflow.com/a/503327/189412
37
public class Account {
    public virtual int Id {get; private set;}
    public virtual string AccountHolderName {get; set;}
    public virtual AccountType AccountType {get; set;}
}

public AgencyMap() {
    Id(o => o.Id);
    Map(o => o.AccountHolderName);
    Map(o => o.AccountType);
}

Fluent NHibernate saves enum values as string by default if you want to override that you need to supply a convention for it. Something like:

public class EnumConvention :
    IPropertyConvention, 
    IPropertyConventionAcceptance
{
    #region IPropertyConvention Members

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

    #endregion

    #region IPropertyConventionAcceptance Members

    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum ||
        (x.Property.PropertyType.IsGenericType && 
         x.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
         x.Property.PropertyType.GetGenericArguments()[0].IsEnum)
        );
    }

    #endregion
}

Almost forgot that you need to add the convention to your fluent config as well. You do that at the same place you add the mappings:

.Mappings(m => m.FluentMappings.AddFromAssemblyOf<BillingRecordMap>()
.Conventions.AddFromAssemblyOf<EnumConvention>()

2 Comments

See this post/comment on how to extend this to support nullable enums: stackoverflow.com/questions/439003/…
I updated the code with your suggestion Mustafa. Thanks a lot!
1

A Great way to do this, is implement the interface IUserType and Create a CustomType with the rules to write and read, that´s a example to boolean:

 public class CharToBoolean : IUserType
{
    public SqlType[] SqlTypes => new[] { NHibernateUtil.String.SqlType };

    public Type ReturnedType => typeof(bool);

    public bool IsMutable =>true;

    public object Assemble(object cached, object owner)
    {
        return (cached);
    }

    public object DeepCopy(object value)
    {
        return (value);
    }

    public object Disassemble(object value)
    {
        return (value);
    }

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y)) return true;

        var firstObject = x as string;
        var secondObject = y as string;

        if (string.IsNullOrEmpty(firstObject) || string.IsNullOrEmpty(secondObject)) return false;

        if (firstObject == secondObject) return true;
        return false;
    }

    public int GetHashCode(object x)
    {
        return ((x != null) ? x.GetHashCode() : 0);
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);

        if (obj == null) return null;

        var value = (string)obj;

        return value.ToBoolean();
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        if(value != null)
        {
            if ((bool)value)
            {
                ((IDataParameter)cmd.Parameters[index]).Value = "S";
            }
            else
            {
                ((IDataParameter)cmd.Parameters[index]).Value = "N";
            }
        }
        else
        {
            ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
        }
    }
    public object Replace(object original, object target, object owner)
    {
        return original;
    }
}

}

the mapping:

  this.Map(x => x.DominioGenerico).Column("fldominiogen").CustomType<CharToBoolean>();

It´s a sample but you can do this with other types.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.