简体   繁体   中英

How to map an enum as a foreign key in (Fluent) NHibernate?

How do I map an enum property as an integer foreign key in (Fluent) NHibernate?

The enum property serves as a type field which is an integer in the database, but it also needs to be a foreign key to a table which contains all the allowable values of that enum (reporting purposes).

Specifically, I need to have a Communication entity with a Type field of type enum CommunicationType mapped as an integer foreign key to the CommunicationType table. Code below.

NOTE : Answers for NHibernate (without Fluent) are accepted also.


public enum CommunicationType {
    General = 1,
    ServicesContactEnquiry = 2,
    ChangePassword = 3,
    OrderDetails = 4,  
}

// This is basically the Enum represented in the database for reporting purposes
public class CommunicationTypeRecord {
    public virtual int ID { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
}

public class Communication {
    public virtual CommunicationType Type { get; set; }
    // rest of fields ommitted
}

public class CommunicationMap : ClassMap<Communication> {
    public CommunicationMap() {
        Map(x => x.Type).CustomType<CommunicationType>.Not.Nullable();  // Needs to be FK -> [CommunicationType].[ID]
        // rest of fields ommitted
    }
}

public class CommunicationTypeRecordMap : ClassMap<CommunicationTypeRecord> {
    public CommunicationTypeRecordMap () {
        Table("CommunicationType");
        Id(x => x.ID).Column("ID").GeneratedBy.Assigned();
        Map(x => x.Name).Column("Name").Not.Nullable().CustomType("AnsiString");
        Map(x => x.Description).Column("Description").Nullable();
    }
}

Attempt 1

I tried the following which works for generating the DB correctly, but it doesn't work with hydrating/dehydrating data.

References<CommunicationTypeRecord>(x => x.CommunicationType).ForeignKey().Not.Nullable();

Exception:

[ArgumentException: There is a problem with your mappings.  You are probably trying to map a System.ValueType to a <class> which NHibernate does not allow or you are incorrectly using the IDictionary that is mapped to a <set>.  

A ValueType (CommunicationType) can not be used with IdentityKey.  The thread at google has a good description about what happens with boxing and unboxing ValueTypes and why they can not be used as an IdentityKey: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=bds2rm%24ruc%241%40charly.heeg.de&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26q%3DSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode%26sa%3DN%26tab%3Dwg (Parameter 'key')]
Data:
StackTrace:
   at NHibernate.Util.IdentityMap.VerifyValidKey(Object obj)
   at NHibernate.Util.IdentityMap.set_Item(Object key, Object value)
   at NHibernate.Engine.StatefulPersistenceContext.AddChildParent(Object child, Object parent)
   at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, String propertyName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.AbstractSaveEventListener.CascadeBeforeSave(IEventSource source, IEntityPersister persister, Object entity, Object anything)
   at NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.AbstractSaveEventListener.PerformSave(Object entity, Object id, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)

this should work. can't test it right now.

public class CommunicationTypeRecord {
    public virtual CommunicationType ID { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
}

public class Communication {
    public virtual CommunicationTypeRecord Type { get; set; }
    // rest of fields ommitted
}

public class CommunicationMap : ClassMap<Communication> {
    public CommunicationMap() {
        CompositeId().KeyReference(x => x.Type);
        // rest of fields ommitted
    }
}

public class CommunicationTypeRecordMap : ClassMap<CommunicationTypeRecord> {
    public CommunicationTypeRecordMap () {
        Table("CommunicationType");
        Id(x => x.ID).Column("ID").CustomType<CommunicationType>().GeneratedBy.Assigned();
        Map(x => x.Name).Column("Name").Not.Nullable().CustomType("AnsiString");
        Map(x => x.Description).Column("Description").Nullable();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM