У меня есть репозиторий PinballMachines, который возвращает гидратированный PinballMachine
. У этого есть частная собственность, которая является списком игр, играемых на этой машине.
У пинбольной машины могут быть миллионы игр, записанных против нее. Из PinballMachine
, я хочу получить высокие PinballMachine
для показа, это 10 лучших игроков.
public class PinballMachine
{
private IList<Game> _games = new List<Game>();
public virtual int ID { get; protected set; }
public virtual IEnumerable<Game> GetTop10Games()
{
return _games
.AsQueryable()
.OrderByDescending(g => g.Score)
.Take(10)
.ToList();
}
}
public class Game
{
public virtual Guid ID { get; protected set; }
public virtual string Name { get; set; }
public virtual int Score { get; set; }
public virtual decimal AmountPaid { get; set; }
}
Свойство PinballMachine
_games
отображается как Bag
.
Bag<Game>("_games", m =>
{
m.Key(k => k.Column("PinballMachineID"));
m.Access(Accessor.Field);
m.Cascade(Cascade.All);
}, r => r.OneToMany());
Следующий код работает правильно, однако NHibernate выполняет очень наивный предикат в таблице игр и выполняет сортировку и фильтрацию в памяти.
-- SLOW! 1,000,000 records
SELECT ...
FROM Games
WHERE PinballMachineID = 123
Это очень субоптимально, так как база данных передает миллионы записей, когда мне нужно всего 10.
В идеале я хочу, чтобы NHibernate генерировал запрос, который выглядит следующим образом:
-- FAST! 10 records
SELECT TOP 10 ...
FROM Games
WHERE PinballMachineID = 123
ORDER BY Score DESC
Возможно ли настроить мое сопоставление, чтобы я мог выполнять дополнительные запросы (в базе данных) на гидратированных объектах.
Я знаю, что я могу использовать сеанс NHibernate для выполнения запроса linq, но я хочу, чтобы эта логика была частью моей сущности.
К сожалению, NHibernate этого не поддерживает.
Когда вы создали сопоставление для PinballMachine
, вы определили, что отношения "один ко многим" в столбце "Идентификатор", который извлекает (лениво или нетерпеливо) все соответствующие объекты " Game
.
Одна вещь, которую я хотел бы предложить, заключается в том, что GetTop10Games
выглядит так, будто он должен принадлежать классу репозитория, а не быть членом объекта. Это одна из причин использования шаблона репозитория - он инкапсулирует всю логику доступа к данным и, в свою очередь, даже позволяет вам писать конкретные исполнительные запросы, когда они действительно нужны, каждый раз в то время. Это (к сожалению или нет) проблема с большинством структур ORM; вы никогда не знаете, когда какой-то поставщик LINQ будет работать плохо или вообще не сможет перевести на SQL, поэтому вы хотите, чтобы ваши варианты открывались.
Я бы, конечно, сделал этот метод членом IGameRepository
или IPinballMachineRepository
и реализовал его как-то вроде:
public IList<Games> GetTopGamesForMachine(PinballMachine machine, int maxItems)
{
return Session
.Query<Games>()
.Where(g => g.PinballMachine == machine)
.OrderByDescending(g => g.Score)
.Take(maxItems)
.ToList();
}