Fluent NHibernate не отображает дочерние строки в свойство коллекции (база данных DB2)

Справочная информация:

Я работаю над устаревшей базой данных DB2, поэтому не могу контролировать схему. Я искал на этом сайте ответы, но не нашел. Я также искал в Google и не нашел подходящего ответа.

Обе таблицы, описанные здесь, используют составные ключи. У меня есть родительская таблица, в которой хранится информация о праве на участие в каждом «случае». В дочерней таблице хранится информация о праве на участие каждого человека, связанного с этим делом. Дочерние записи различаются по номерам контактов, чтобы сделать их уникальными только в дочерней таблице.

Я использую NHibernate v3.1 с Fluent NHibernate v1.2, оба приобретены через пакеты NuGet. Сущности отображаются с помощью функции автоматического сопоставления Fluent NHibernate. Любое настраиваемое сопоставление выполняется в методе переопределения сопоставления для каждой сущности.

Еще нужно знать, что эти таблицы не имеют «первичного ключа», определенного в DB2. У них есть только «уникальные ключи», которые вы видите в приведенном ниже определении составного ключа (см. Код).

T0026_AG_ELIG - это имя родительской таблицы и соответствующего класса POCO.
T0265_AG_IN_ELIG - имя дочерней таблицы и соответствующего класса POCO.

Проблема:

Проблема в том, что когда я выполняю запрос, запрашиваются все данные, родительская запись успешно сопоставляется с классом, но возвращенные дочерние строки не сопоставляются с коллекцией родительского класса. NHibernate генерирует запросы для родительских и дочерних данных. Когда я выполняю свой собственный запрос к базе данных, возвращаются правильные данные для условий. По какой-то причине дочерние записи просто не привязаны к свойству родительского (T0026) класса.

Вопрос:

Что мне нужно сделать, чтобы несколько строк, возвращающихся из T0265_AG_IN_ELIG, были сопоставлены с их соответствующим классом и правильно загрузились в указанное свойство коллекции в родительском классе (T0026_AG_ELIG)?

Собственность родительского собрания (T0026_AG_ELIG):

Public Overridable Property IndividualEligibilityRecords As IList(Of T0265_AG_IN_ELIG)

Переопределение сопоставления для родительского элемента (T0026_AG_ELIG):

mapping.CompositeId() _
   .KeyProperty(Function(x) x.CASE_NUM) _
   .KeyProperty(Function(x) x.PROGRAM_CD) _
   .KeyProperty(Function(x) x.SUBPROGRAM_CD) _
   .KeyProperty(Function(x) x.AG_SEQ_NUM) _
   .KeyProperty(Function(x) x.CAG_ELIG_SEQ_NUM)

mapping.HasMany(Of T0265_AG_IN_ELIG)(Function(x) x.IndividualEligibilityRecords) _
   .Cascade.All() _
   .Inverse() _
   .Fetch.Join() _
   .KeyColumns.Add("CASE_NUM") _
   .KeyColumns.Add("PROGRAM_CD") _
   .KeyColumns.Add("SUBPROGRAM_CD") _
   .KeyColumns.Add("AG_SEQ_NUM") _
   .KeyColumns.Add("CAG_ELIG_SEQ_NUM") _
   .Not.LazyLoad() _
   .AsList(Function(x) x.Column("PIN_NUM"))


   mapping.IgnoreProperty(Function(x) x.IndividualEligibilityRecords)

Переопределение сопоставления для ребенка (T0265_AG_IN_ELIG):

mapping.CompositeId() _
   .KeyProperty(Function(x) x.CASE_NUM) _
   .KeyProperty(Function(x) x.PROGRAM_CD) _
   .KeyProperty(Function(x) x.SUBPROGRAM_CD) _
   .KeyProperty(Function(x) x.AG_SEQ_NUM) _
   .KeyProperty(Function(x) x.CAG_ELIG_SEQ_NUM) _
   .KeyProperty(Function(x) x.PIN_NUM)

Для выполнения запроса выполняется следующий код:

transaction = session.BeginTransaction()

query = session.CreateQuery("FROM T0026_AG_ELIG AS T0026 " _
      & "WHERE T0026.CASE_NUM = :p0 AND T0026.PROGRAM_CD = :p1 AND " _
      & "SUBPROGRAM_CD = :p2 AND AG_SEQ_NUM = :p3 AND CAG_ELIG_SEQ_NUM = :p4")

query.SetParameter("p0", caseNumber)
query.SetParameter("p1", programCode)
query.SetParameter("p2", subProgramCode)
query.SetParameter("p3", agSequenceNumber)
query.SetParameter("p4", cagEligSequenceNumber)

result = query.List()
transaction.Commit()

If result.Count = 1 Then
   Return DirectCast(result.Item(0), T0026_AG_ELIG)
End If

person Chris    schedule 04.11.2011    source источник
comment
Можете ли вы проверить sql, созданный для вашего запроса, и запустить его непосредственно в базе данных? А для чего mapping.IgnoreProperty(Function(x) x.IndividualEligibilityRecords)? Я думаю ты можешь оставить это   -  person Firo    schedule 07.11.2011
comment
Я записал запросы, которые генерирует NHibernate, и успешно выполнил эти запросы. Выполнение команды NHibernate List () генерирует 5 отдельных запросов. Первый возвращает запись верхнего уровня должным образом. Второй возвращает три дочерних записи, прикрепленные к родительской записи. 3-й, 4-й и 5-й запросы возвращают каждую дочернюю запись по отдельности, что совершенно не нужно. Я не уверен, почему NHibernate генерирует последние 3 запроса. Возможно, когда NHibernate генерирует только первый и второй запросы, коллекция будет отображаться правильно?   -  person Chris    schedule 08.11.2011
comment
Запросы являются SQL-запросами, которые генерирует NHibernate. Кроме того, я смог без проблем удалить вызов IgnoreProperty.   -  person Chris    schedule 08.11.2011
comment
вы можете попробовать var result = session.Get<T0026_AG_ELIG>(new T0026_AG_ELIG{ CASE_NUM = caseNumber, PROGRAM_CD = programCode, SUBPROGRAM_CD = subProgramCode, AG_SEQ_NUM = agSequenceNumber, CAG_ELIG_SEQ_NUM = cagEligSequenceNumber }) как VB?   -  person Firo    schedule 08.11.2011
comment
Версия VB: Dim result = session.Get (Of T0026_AG_ELIG) (New T0026_AG_ELIG () With {.CASE_NUM = caseNumber, .PROGRAM_CD = programCode, .SUBPROGRAM_CD = subProgramCode, .AG_SEQ_NUM = agSequenceNumber}, .CAG_SEQ_NUM = agSequenceNumber},. ) SQL-запросы, которые он генерирует, идентичны тем, которые генерируются кодом выполнения в исходном сообщении. Родительский объект отображается, но дочерние объекты не отображаются в свойство коллекции.   -  person Chris    schedule 08.11.2011


Ответы (2)


Я отправляю ответ, потому что его намного легче читать и редактировать.

Мое следующее предположение состоит в том, что NH сбивает с толку разное количество ключевых столбцов в хэше T0265_AG_IN_ELIG и его составном ключе. обычно этот вид ассоциации является зависимой ассоциацией, в которой дочерние объекты отображаются не отдельно, а внутри коллекции. Попробуйте исключить T0265_AG_IN_ELIG из автоматического сопоставления и сопоставить его, как это, в T0026_AG_ELIG

mapping.HasMany(Of T0265_AG_IN_ELIG)(Function(x) x.IndividualEligibilityRecords) _
   .Cascade.All() _
   .Inverse() _
   .KeyColumns.Add("CASE_NUM", "PROGRAM_CD", "SUBPROGRAM_CD", "AG_SEQ_NUM", "CAG_ELIG_SEQ_NUM") _
   .Not.LazyLoad() _
   .AsList(Function(x) x.Column("PIN_NUM"))
   .Component(Function(c) c.ParentReference(Function(x) x.T0026_AG_ELIG))

Обновлять:

В чем ценность:

DirectCast(result.Item(0), T0026_AG_ELIG).IndividualEligibilityRecords.GetType().Name

Обновление: если вы исключили T0265_AG_IN_ELIG из автоматического сопоставления, вы должны указать все столбцы в компоненте

class AutomapConfiguration : DefaultAutomappingConfiguration
{
    public override bool ShouldMap(Type type)
    {
        return type != typeof(T0265_AG_IN_ELIG);
    }
}

class T0265_AG_IN_ELIG
{
    public T0026_AG_ELIG T0026_AG_ELIG { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
}


.Component(c =>
{
    c.ParentReference(x => x.T0026_AG_ELIG);
    c.Map(x => x.Prop1));
    c.Map(x => x.Prop2));
    c.Map(x => x.Prop3));
})
person Firo    schedule 08.11.2011
comment
Единственная проблема с кодом заключается в том, что строка компонента прерывается, потому что ParentReference не возвращает значение - это подпрограмма. .Component (Функция (c) c.ParentReference (Функция (x) x.T0026_AG_ELIG)) - person Chris; 08.11.2011
comment
в C # это было бы .Component(c => c.ParentReference(x => x.T0026_AG_ELIG)), может быть, .Component(Sub(c) c.ParentReference(Function(x) x.T0026_AG_ELIG))? - person Firo; 08.11.2011
comment
Это исправило код. Теперь я получаю заполнение родительской записи, но дочерняя коллекция IndividualEligibilityRecords все еще не заполняется. NHibernate генерирует один запрос выбора для родительской записи и один запрос выбора для 3 дочерних записей. Они выбираются, но никогда не отображаются в коллекции. - person Chris; 08.11.2011
comment
Еще одна странная вещь, которую я обнаружил, заключается в том, что, хотя строка .Component(Sub(c) c.ParentReference(Function(x) x.Parent)) сокращает количество операторов выбора SQL до двух (один для T0026, один для T0265), она также предотвращает выбор всех столбцов на T0265. Я безуспешно искал способ указать все столбцы для выбора. Вы знаете, как это исправить? - person Chris; 09.11.2011
comment
Обновление: мне удалось загрузить коллекцию, если я указал в сопоставлении T0026 использовать ExtraLazyLoad() вместо отключения ленивой загрузки. Однако это означает, что мой сеанс должен оставаться открытым для запроса этих дополнительных элементов, что нежелательно. Когда я переключаю код обратно на использование Not.LazyLoad(), коллекция не загружается и выдает ошибку «Нет дочерних элементов». Также следует отметить, что в этом случае строка .Component(Sub(c) c.ParentReference(Function(x) x.Parent)) закомментирована. - person Chris; 09.11.2011
comment
Фиро, спасибо за помощь. Ваша помощь во многом помогала мне найти решение. Я опубликовал ниже то, что мы в итоге выяснили. Тем не менее, я хотел удостовериться, что предоставил кредит там, где он должен был быть. - person Chris; 15.11.2011

Я смог понять это. По сути, в NHibernate 3.1 есть проблема, которая позволяет связывать записи только при использовании Extra Lazy Loading. Даже в этом случае SQL неправильно генерируется из HQL. Решением было вернуться к использованию Fluent NHibernate 1.2 с NHibernate 2.1.2.

Ссылка для загрузки называется «Загрузить для NHibernate 2.1.2». Щелкните ниже, чтобы перейти на страницу загрузок.

Страница загрузок Fluent NHibernate

Переопределение сопоставления для T0026:

mapping.CompositeId() _
   .KeyProperty(Function(x) x.CASE_NUM) _
   .KeyProperty(Function(x) x.PROGRAM_CD) _
   .KeyProperty(Function(x) x.SUBPROGRAM_CD) _
   .KeyProperty(Function(x) x.AG_SEQ_NUM) _
   .KeyProperty(Function(x) x.CAG_ELIG_SEQ_NUM) _

mapping.HasMany(Of T0265_AG_IN_ELIG)(Function(x) x.IndividualEligibilityRecords) _
   .KeyColumns.Add("CASE_NUM", "PROGRAM_CD", "SUBPROGRAM_CD", "AG_SEQ_NUM", "CAG_ELIG_SEQ_NUM") _
   .Cascade.All() _
   .Table("PWS639TC.T0265_AG_IN_ELIG") _
   .Not.LazyLoad() _
   .AsBag()

Основные отличия в файле сопоставления заключаются в том, что мы не используем инверсию и возвращаем GenericPersistentBag из NHibernate.

Метод выполнения запроса:

transaction = session.BeginTransaction()

query = session.CreateQuery("FROM T0026_AG_ELIG AS T0026 " _
              & "WHERE T0026.CASE_NUM = :p0 " _
              & "AND T0026.PROGRAM_CD = :p1 " _
              & "AND SUBPROGRAM_CD = :p2 " _
              & "AND AG_SEQ_NUM = :p3 " _
              & "AND CAG_ELIG_SEQ_NUM = :p4")

query.SetParameter("p0", caseNumber)
query.SetParameter("p1", programCode)
query.SetParameter("p2", subProgramCode)
query.SetParameter("p3", agSequenceNumber)
query.SetParameter("p4", cagEligSequenceNumber)

result = query.List()

If result.Count = 1 Then
   returnable = DirectCast(result.Item(0), T0026_AG_ELIG)
End If

transaction.Commit()

Метод выполнения запроса идентичен.

person Chris    schedule 14.11.2011