#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 генерирует подобный код.