2009-08-28 9 views
3

J'utilise Fluent NHibernate version 1.0.0.579 (dernière version à cette date). J'ai une classe d'activité abstraite et plusieurs classes héritières, par exemple. DummyActivity. Tous utilisent la même table Activités, et tous ont une valeur de discriminateur basée sur un type intégral qui pointe vers un mappage dans le projet (pas un FK dans la base de données).Fluent NHibernate table par problème de mappage de hiérarchie

Nous avons construit la mise en correspondance comme ceci:

public class ActivityMap : ClassMap<Activity> 
    { 
     public ActivityMap() 
     { 
      Table("Activities"); 
      Id(x => x.Id).Column("ID").GeneratedBy.Guid(); 
      Map(x => x.ActivityName).Not.Nullable().Length(50); 
      HasMany(x => x.ActivityParameters) 
       .KeyColumn("ActivityID") 
       .AsMap<string>(idx => idx.Column("ParameterName"), elem => elem.Column("ParameterValue")) 
       .Not.LazyLoad() 
       .Cascade.Delete() 
       .Table("ActivityParameters"); 

      DiscriminateSubClassesOnColumn<int>("ActivityType") 
       .SubClass<DummyActivity>(1, c => { }); 
     } 
    } 

Le fichier généré est hbm.xml:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true"> 
    <class xmlns="urn:nhibernate-mapping-2.2" name="***.Activity, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Activities"> 
    <id name="Id" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ID" /> 
     <generator class="guid" /> 
    </id> 
    <discriminator column="ActivityType" type="Int32" insert="true" not-null="true" /> 
    <property name="ActivityName" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ActivityName" length="50" not-null="true" /> 
    </property> 
    <map cascade="delete" lazy="false" name="ActivityParameters" table="ActivityParameters"> 
     <key> 
     <column name="ActivityID" /> 
     </key> 
     <index type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ParameterName" /> 
     </index> 
     <element type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ParameterValue" /> 
     </element> 
    </map> 
    <subclass name="***.DummyActivity, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1" /> 
    </class> 
</hibernate-mapping> 

Selon ma conviction, cela ressemble à un fichier hbm.xml valide, identique structure avec l'exemple donné dans le document de référence officiel de NHibernate, à savoir

<class name="IPayment" table="PAYMENT"> 
<id name="Id" type="Int64" column="PAYMENT_ID"> 
<generator class="native"/> 
</id> 
<discriminator column="PAYMENT_TYPE" type="String"/> 
<property name="Amount" column="AMOUNT"/> 
... 
<subclass name="CreditCardPayment" discriminator-value="CREDIT"> 
... 
</subclass> 
<subclass name="CashPayment" discriminator-value="CASH"> 
... 
</subclass> 
<subclass name="ChequePayment" discriminator-value="CHEQUE"> 
... 
</subclass> 
</class> 

Faisons-nous une erreur dans notre cartographie? De plus, quelqu'un peut me montrer la nouvelle implémentation recommandée par Fluent (en utilisant SubClass avec colonne discriminante, quelque chose comme

public class ActivityMap : ClassMap<Activity> 
    { 
     public ActivityMap() 
     { 
      Table("Activities"); 
      Id(x => x.Id).Column("ID").GeneratedBy.Guid(); 
      Map(x => x.ActivityName).Not.Nullable().Length(50); 
      HasMany(x => x.ActivityParameters) 
       .KeyColumn("ActivityID") 
       .AsMap<string>(idx => idx.Column("ParameterName"), elem => elem.Column("ParameterValue")) 
       .Not.LazyLoad() 
       .Cascade.Delete() 
       .Table("ActivityParameters"); 

      DiscriminateSubClassesOnColumn<int>("ActivityType"); 
     } 
    } 

public class DummyActivityMap : SubClass<DummyActivity> 
{ 
    ///discriminator value here how??? 
} 

?)

La trace de la pile est

[FormatException: Input string was not in a correct format.] 
    System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) +7469351 
    System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) +119 
    NHibernate.Type.Int32Type.FromStringValue(String xml) +36 
    NHibernate.Type.Int32Type.StringToObject(String xml) +10 
    NHibernate.Persister.Entity.SingleTableEntityPersister..ctor(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping) +7824 

[MappingException: Could not format discriminator value to SQL string of entity ***.Activity] 
    NHibernate.Persister.Entity.SingleTableEntityPersister..ctor(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping) +8183 
    NHibernate.Persister.PersisterFactory.CreateClassPersister(PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping cfg) +68 
    NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners) +1468 
    NHibernate.Cfg.Configuration.BuildSessionFactory() +87 
    FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() in d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentConfiguration.cs:93 

[FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail. 

] 
    ***.Container.ConfigureNHibernate() in ***.Unity\Container.cs:92 
    ***.Container.ConfigureContainer() in ***.Unity\Container.cs:60 
    ***.Container.GetInstance() in ***.Unity\Container.cs:45 
    ***.Global.CreateContainer() in ***\Global.asax.cs:72 
    ***.Global.Application_Start(Object sender, EventArgs e) in ***\Global.asax.cs:44 

Répondre

2

Je l'ai compris dans le cas d'enums.

Considérant ce type enum:

public enum ActivityType 
{ 
    [EnumKey("1")] 
    [EnumDescription("ImportFromFile")] 
    ImportFromFile, 
} 

où EnumKey et EnumDescription sont (populaires) les méthodes d'extension, je redéfinissent l'activité comme

public abstract class Activity 
    { 
     public virtual Guid Id { get; set; } 
     public virtual ActivityExecutionResult ExecutionResult { get; private set; } 

     public virtual ActivityExecutionStatus ExecutionStatus {get;private set;} 

     public abstract ActivityExecutionStatus Execute(); 

     public virtual string ActivityName { get; private set; } 

     public virtual IDictionary<string, string> ActivityParameters { get; private set; } 

     public virtual ActivityType ActivityType { get; private set; } 
    } 

Le fichier de mappage ressemble à ceci:

public class ActivityMap : ClassMap<Activity> 
    { 
     public ActivityMap() 
     { 
      Table("Activities"); 
      Id(x => x.Id).Column("ID").GeneratedBy.Guid(); 
      Map(x => x.ActivityName).Not.Nullable().Length(50); 
      Map(x => x.ActivityType).CustomType<int>().Column("ActivityType").Not.Nullable(); 
      HasMany(x => x.ActivityParameters) 
       .KeyColumn("ActivityID") 
       .AsMap<string>(idx => idx.Column("ParameterName"), elem => elem.Column("ParameterValue")) 
       .Not.LazyLoad() 
       .Cascade.Delete() 
       .Table("ActivityParameters"); 

      DiscriminateSubClassesOnColumn("ActivityType"); 
     } 
    } 

    public class ImportActivityFromFileMap : SubclassMap<ImportActivityFromFile> 
    { 
     public ImportActivityFromFileMap() 
     { 
      DiscriminatorValue(ActivityType.ImportFromFile.GetKey()); 
     } 
    } 

Le fichier hbm généré ressemble à:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true"> 
    <class xmlns="urn:nhibernate-mapping-2.2" name="***.Activity, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Activities"> 
    <id name="Id" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ID" /> 
     <generator class="guid" /> 
    </id> 
    <discriminator column="ActivityType" type="String" insert="true" not-null="true" /> 
    <property name="ActivityName" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ActivityName" length="50" not-null="true" /> 
    </property> 
    <property name="ActivityType" type="Int32"> 
     <column name="ActivityType" not-null="true" /> 
    </property> 
    <map cascade="delete" lazy="false" name="ActivityParameters" table="ActivityParameters"> 
     <key> 
     <column name="ActivityID" /> 
     </key> 
     <index type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ParameterName" /> 
     </index> 
     <element type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="ParameterValue" /> 
     </element> 
    </map> 
    <subclass name="***.ImportActivityFromFile, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1" /> 
    </class> 
</hibernate-mapping> 

Cela fonctionne comme un charme!

0

avez-vous trouvé dehors comment utiliser enums au lieu d'entiers? Avec des entiers, cela fonctionne comme vous l'avez décrit mais avec des énumérations, ce n'est pas le cas. Pas même si je les mets à int

E.g. DiscriminatorValue ((int) SomeEnum.SomeVaue))

+0

Non, je n'ai pas regardé http://code.google.com/p/fluent-nhibernate/issues/detail?id=240, c'est un problème ouvert, et j'ai posé une question Regardez aussi: http://groups.google.com/group/fluent-nhibernate/browse_thread/thread/4d38111df91e72b9 J'utilise l'entier comme une énumération, peut-être que je l'adopterai bientôt – DaeMoohn

+0

Dès que Je vais avoir un concept qui fonctionne, je vais essayer avec une énumération aussi et je vais vous donner un n réponse – DaeMoohn

+0

Votre premier lien est cassé. – Astaar

0

Je vais montrer une autre implémentation que j'ai imaginée.

public class SmartEnumMapping<T> : IUserType 
    { 
     #region IUserType Members 

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

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

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

     public int GetHashCode(object x) 
     { 
      return x.GetHashCode(); 
     } 

     public bool IsMutable 
     { 
      get { return false; } 
     } 

     public new bool Equals(object x, object y) 
     { 
      return object.Equals(x, y); 
     } 

     public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner) 
     { 
      int index0 = rs.GetOrdinal(names[0]); 
      if (rs.IsDBNull(index0)) 
      { 
       return null; 
      } 
      string key = rs.GetString(index0); 
      return EnumExtensions.EnumParseKey<T>(key, false, true); 
     } 

     public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) 
     { 
      if (value == null) 
      { 
       ((IDbDataParameter)cmd.Parameters[index]).Value = DBNull.Value; 
      } 
      else 
      { 
       T enumValue = (T)Enum.Parse(typeof(T), value.ToString()); 
       ((IDbDataParameter)cmd.Parameters[index]).Value = enumValue.GetKey(); 
      } 
     } 

     public object Replace(object original, object target, object owner) 
     { 
      return original; 
     } 

     public Type ReturnedType 
     { 
      get { return typeof(T); } 
     } 

     public global::NHibernate.SqlTypes.SqlType[] SqlTypes 
     { 
      get { return new SqlType[] { SqlTypeFactory.GetString(4096) }; } 
     } 

     #endregion 
} 

Avec cela, la cartographie devient

Map(x => x.ActivityType).CustomType<SmartEnumMapping<ActivityType>>().Column("ActivityType").Not.Nullable(); 

ActivityType ressemble

public enum ActivityType 
    { 
     [EnumKey("1")] 
     [EnumDescription("dada")] 
     dad, 

     [EnumKey("2")] 
     [EnumDescription("da")] 
     ImportCalculAtasateSfarsitLuna, 

     [EnumKey("3")] 
     [EnumDescription("da")] 
     das, 

     } 

Avec cela, dans le code je peux utiliser "ActivityType.das", mais quand persistant, il est persisté "3". Encore une fois, lors de la lecture de la base de données, je lis "3", mais je vais transformer cela en "ActivityType".das ».

Je suppose que cela est une réponse plus appropriée qui explique le comportement mentionné dans la réponse précédente.

Encore une fois, le EnumKey, EnumDescription, EnumParseKey etc. sont des choses qui peuvent être facilement trouvés sur l'Internet

Questions connexes