Linq to NHibernate ThenFetch несколько свойств
У меня есть этот граф объектов:
// Lots of stuff omitted for brevity; these are all virtual properties and there
// are other properties which aren't shown on all classes.
class A {
B b;
C c;
DateTime timestamp;
}
class B {
X x;
Y y;
}
class X {
int id;
}
class C { }
class Y { }
or to put it more simply,
a = {
b: {
x { id: int },
y: { }
},
c: { },
timestamp: DateTime
}
Сейчас я делаю запрос, где я собираюсь вернуть список A
и мне нужны все их B
s, C
s, X
с и Y
s. Я также собираюсь сгруппировать их по B для поиска.
ILookup<B, A> GetData(List<int> ids) {
using (ISession session = OpenSession()) {
var query = from a in session.Query<A>()
where ids.Contains(a.b.x.id)
orderby A.timestamp descending
select a;
query = query
.Fetch(a => a.b)
.ThenFetch(b => b.x)
.Fetch(a => a.b)
.ThenFetch(b => b.y)
.Fetch(a => a.c);
return query.ToLookup(a => a.b);
}
}
Несколько вещей, на которые стоит обратить внимание:
- Это отчет, в котором все данные должны быть возвращены - неограниченные результаты не являются проблемой.
- Я делаю группировку с помощью
ToLookup
потому что с помощьюgroup by
кажется более сложным, когда вам нужны все фактические значения - вам нужно запросить базу данных для групп, а затем для их фактических значений.
У меня вопрос, как правильно указать стратегию получения. То, как я это сделал, - единственный способ, с помощью которого я нашел это (получив все значения bx и по значениям), но он выдает SQL, который кажется неправильным:
select /* snipped - every mapped field from a0, b1, x2, b3, y4, c5 - but not b6 */
from [A] a0
left outer join [B] b1
on a0.B_id = b1.BId
left outer join [X] x2
on b1.X_id = x2.XId
left outer join [B] b3
on a0.B_id = b3.BId
left outer join [Y] y4
on b3.Y_id = y4.YId
left outer join [C] c5
on a0.C_id = c5.CId,
[B] b6
where a0.B_id = b6.BId
and (b6.X_id in (1, 2, 3, 4, 5))
order by a0.timestamp desc
Как вы можете видеть, он получает значение для a.b
три раза - b1
а также b3
для получения, и b6
для пункта, где.
- Я предполагаю, что это негативно влияет на производительность БД - я прав?
- Есть ли способ изменить мой
.Fetch
звонки, так что только выборкиa.b
один раз? - Это хороший подход к моей проблеме?
1 ответ
Если вы сделаете несколько выборок свойств "один ко многим" в одном запросе, вы получите декартово произведение. NHibernate не справляется с этим - AFAIK, это было сделано намеренно, чтобы заставить его вести себя как настоящее соединение SQL. HQL делает то же самое.
Вам не нужно делать все выборки за один раз. Разделите запрос и выполните каждую выборку / объединение "один ко многим" в отдельном запросе. Каждый из них кеширует свои данные в сеансе и правильно связывает все ссылки на объекты. (Примечание: я никогда не пробовал это с LINQ, но он работает на HQL, и принцип тот же)
С моей головы это может выглядеть примерно так:
ILookup<B, A> GetData(List<int> ids) {
using (ISession session = OpenSession()) {
var query = from a in session.Query<A>()
where ids.Contains(a.b.x.id)
orderby A.timestamp descending
select a;
query
.Fetch(a => a.b)
.ThenFetch(b => b.x)
.ToList();
query
.Fetch(a => a.b)
.ThenFetch(b => b.y)
.Fetch(a => a.c)
.ToList();
return query.ToLookup(a => a.b);
}
Есть еще одна оптимизация, которую вы могли бы сделать, используя метод ToFuture() вместо ToList()... Я не уверен, как он работает с методами LINQ и ToLookup, но он не должен быть слишком сложным, чтобы понять это правильно. ToFuture() поставит запросы в очередь и выполнит их все сразу, вместо того, чтобы делать отдельные подключения к базе данных для каждого.