NHibernate 3.0 IQueryable выбор столбца-призрака

#linq #nhibernate

#linq #nhibernate

Вопрос:

Я получаю странную ошибку при запуске того, что кажется простым запросом.

 return (from x in session.Query<Contact>()
                .Where(x => x.Id == 10)
               select new ContactIndexViewModel
               {
                   Id = x.Id,
                   Name = x.BasicInfo.FirstName   " "   x.BasicInfo.LastName,
                   Filters = x.Filters
               }).FirstOrDefault();
  

Генерирует следующий SQL

 select 
    contact0_.[Id] as col_0_0_, 
    contact0_.[BasicInfoFirstName] as col_1_0_, 
    contact0_.[BasicInfoLastName] as col_2_0_, 
    . as col_3_0_,
    filters1_.[Id] as column1_16_, 
    filters1_.Criteria1 as Criteria2_16_, 
    // .. .more filters1_ fields
    filters1_.ContactId as ContactId16_ 
from 
    [MyServer].[dbo].[Contact] contact0_ 
    inner join [MyServer].[dbo].[Filter] filters1_ 
        on contact0_.[Id]=filters1_.ContactId
where
    contact0_.[Id]=@p0
  

Обратите внимание, что выбран четвертый столбец. basicInfo является компонентом, и выбор (в запросе) включает все поля, определенные в ViewModel.

У меня не возникает никаких других проблем с объектами Contact или Filter в других частях приложения. Контакт -> Фильтр имеет отношение «один ко многим».

Есть идеи о том, как отлаживать или что может вызвать это?

Обновить

Если я удалю ссылку на фильтры в select , проблема исчезнет.

ОБНОВИТЬ соответствующие сопоставления

Контакты

 public partial class ContactMap : ClassMap<Contact>
{
    /// <summary>Initializes a new instance of the <see cref="ContactMap"/> class.</summary>
    public ContactMap()
    {
        Table("[MyServer].[dbo].[Contact]");
        OptimisticLock.Version();
        DynamicUpdate();
        LazyLoad();

        Id(x=>x.Id)
            .Access.CamelCaseField(Prefix.Underscore)
            .Column("[Id]")
            .GeneratedBy.Identity();
        Version(x=>x.RecordVersion)
            .Access.CamelCaseField(Prefix.Underscore)
            .Column("[RecordVersion]")
                .CustomSqlType("timestamp")
                .Not.Nullable()
            .UnsavedValue("null")
            .CustomType("BinaryBlob")
            .Generated.Always();
        Map(x=>x.Active).Access.CamelCaseField(Prefix.Underscore);
        // other scalar properties
        Component(x0=>x0.BasicInfo, m0=>
        {
            m0.Map(x1=>x1.FirstName).Column("[BasicInfoFirstName]").Access.CamelCaseField(Prefix.Underscore);
            m0.Map(x1=>x1.LastName).Column("[BasicInfoLastName]").Access.CamelCaseField(Prefix.Underscore);
            // other scalar properties
        });

        // other relationships

        HasMany(x=>x.Searches)
            .Access.CamelCaseField(Prefix.Underscore)
            .Cascade.AllDeleteOrphan()
            .Fetch.Select()
            .Inverse()
            .LazyLoad()
            .KeyColumns.Add("ContactId");           
    } 
} 
  

Поиск

 public partial class SearchMap : ClassMap<Search>
{
    public SearchMap()
    {
        Table("[MyServer].[dbo].[Search]");
        OptimisticLock.Version();
        DynamicUpdate();
        LazyLoad();

        Id(x=>x.Id)
            .Access.CamelCaseField(Prefix.Underscore)
            .Column("[Id]")
            .GeneratedBy.Identity();
        Map(x=>x.Controller).Not.Nullable().Access.CamelCaseField(Prefix.Underscore);
        Map(x=>x.Module).Not.Nullable().Access.CamelCaseField(Prefix.Underscore);
        Map(x=>x.Name).Column("[Name]").Not.Nullable().Access.CamelCaseField(Prefix.Underscore);

        References(x=>x.Contact)
            .Access.CamelCaseField(Prefix.Underscore)
            .Cascade.All()
            .Fetch.Select()
            .Columns("ContactId");
        HasMany(x=>x.DataFilters)
            .Access.CamelCaseField(Prefix.Underscore)
            .Cascade.AllDeleteOrphan()
            .Fetch.Select()
            .Inverse()
            .LazyLoad()
            .KeyColumns.Add("SearchId");
    } 
} 
  

Комментарии:

1. В NHibernate 3.0 было много ошибок в поставщике Linq. Вы пробовали 3.1 (или 3.2 Beta2, которая была выпущена вчера)?

2. @cremor Я обновился до 3.1, и проблема не устранена. Если у меня будет время, я попробую 3.2

3. это может быть пространство, с которым вы выполняете конкатенацию. просто попробуйте получить имя и фамилию отдельно.

4. также было бы полезно просмотреть ваши сопоставления.

5. И… что именно представляет собой «странная ошибка»?

Ответ №1:

Сопоставили ли вы фильтры с помощью FetchMode.Join?

Кстати, может быть проще создать ContactIndexViewModel в памяти, с тем компромиссом, что он извлекает слишком много столбцов из базы данных. С другой стороны, Get не очищает сеанс, что может иметь отношение к производительности.

 var contact = session.Get<Contact>(10);

return new ContactIndexViewModel
{
     Id = contact.Id,
     Name = contact.BasicInfo.FirstName   " "   contact.BasicInfo.LastName,
     Filters = contact.Filters
};
  

Комментарии:

1. Я изменил режим выборки на .Join() , но это не исправило проблему. Предлагаемое вами решение — это обходной путь, который я использую. Я знаю, что .Get() попытается использовать кеш, но мне неясно, будет ли .Query() это делать. Вы хотите сказать, что этого не произойдет?

2. @Steve: Я имею в виду, что FetchMode.Join это может привести к ошибке, вы не должны этого делать. Ни один запрос никогда не попадает напрямую в кэш. Только Get и Load , которые получают идентификатор в качестве аргумента.

3. Как предполагает @Jamie Ide, я подозреваю, что это ошибка. Я попытаюсь собрать повторяемый проект и связаться с сотрудниками NHibernate. Я принимаю этот ответ, потому что это обходной путь, который я в конечном итоге использовал.

Ответ №2:

Ваше отображение для таблицы необычно для меня.

 Table("[MyServer].[dbo].[Contact]");
  

Обычно имя сервера указывается во время настройки, схема указывается отдельно, а разделители («[…]») устанавливаются NHibernate. Я бы сопоставил его как:

 Schema("dbo");
Table("Contact");
  

Это может вызывать проблему синтаксического анализа, приводящую к нечетному выбору. Если это не так, то я думаю, что это ошибка — NHibernate никогда не должен выдавать select без псевдонима таблицы и имени столбца.

Комментарии:

1. Спасибо. Это может показаться странным, потому что я использую LLBLGen. Это все сгенерированный код. Я вручную изменю сопоставление, как вы предлагаете, и посмотрю, решит ли это проблему.

2. Добавление вызова Schema не изменило сгенерированный SQL.

3. Тогда я думаю, что это ошибка. Поставщик LINQ, как известно, глючит (но значительно улучшен). Я использую Criteria API и QueryOver только по этой причине. Я удивлен, что LLBGen генерирует подобный код.