2009-08-28 3 views
3

Я использую Fluent NHibernate версии 1.0.0.579 (последняя версия на эту дату). У меня есть абстрактный класс Activity и несколько наследующих классов, например. DummyActivity. Все они используют одну и ту же таблицу «Действия», и все они имеют значение дискриминатора на основе интегрального типа, который указывает на сопоставление в проекте (а не в FK в базе данных).Свободная таблица NHibernate для задачи сопоставления иерархии

Мы построили отображение так:

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 => { }); 
     } 
    } 

Сгенерированный файл 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> 

По моему мнению, это выглядит как действительный файл hbm.xml, идентичны структуры с примером, приведенным в официальном справочном документе NHibernate, то есть

<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> 

Производим ли мы ошибку в нашем картографировании? Кроме того, кто-то может указать мне новую реализацию рекомендованной Fluent (с использованием SubClass с колонкой дискриминатора, что-то вроде

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??? 
} 

?)

Стек трассировки

[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 

ответ

2

Я понял это в случае перечислений.

Учитывая этот тип перечисления:

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

где EnumKey и EnumDescription являются (популярной) метода расширения, я переопределять активность как

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; } 
    } 

Файл отображения выглядит следующим образом:

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()); 
     } 
    } 

Сгенерированный файл hbm выглядит так:

<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> 

Это работает как шарм!

0

ли вы понять это как использовать перечисления вместо целых чисел? С целыми числами он работает, как вы описали, но с перечислениями, которых нет. Даже если я их передал int

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

+0

Нет, я не сделал этого. Посмотрите http://code.google.com/p/fluent-nhibernate/issues/detail?id=240, это открытая проблема, и я поставил вопрос тоже смотрите: http://groups.google.com/group/fluent-nhibernate/browse_thread/thread/4d38111df91e72b9. Я использую целое число точно так же, как перечисление, возможно, скоро приму его. – DaeMoohn

+0

Как только У меня будет концепция, я тоже попробую перечислить, и я дам вам n ответ. – DaeMoohn

+0

Ваша первая ссылка не работает. – Astaar

0

Я покажу еще одну реализацию, с которой я пришел.

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 
} 

При этом, отображение становится

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

ActivityType выглядит

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

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

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

     } 

С этим, в коде я могу использовать "ActivityType.das", но при сохраняющихся, это сохранялся «3». Опять же, при чтении из базы данных я читаю «3», но я преобразую это в «ActivityType».дас».

Я думаю, что это более правильный ответ, который объясняет поведение, упомянутое в предыдущем ответе.

Еще раз, EnumKey, EnumDescription, EnumParseKey т.д. вещи, которые можно легко найти в Интернете