Как по умолчанию использовать фильтр запроса свойства навигации Entity Framework в базе данных?

dbcontext API, кажется, устанавливает свойства навигации в ICollections (для * концов ассоциаций). Обычный способ получить запрашиваемые объекты (например, если вы хотите подсчитать), кажется,

int count = dbcontext.Entry(entry).Collection(c => c.navprop).Query().Count();

но это неудобно, если вы хотите часто фильтровать в БД. Что еще более важно, это также легко забыть. Если кто-то случайно скажет

int count = entry.navprop.Count();

затем он получает все данные на сервере и производит там подсчет, что происходит медленно.

То же самое верно и для типа EntityCollection, который ObjectContext использует по умолчанию.

int count = entry.navprop.CreateSourceQuery().Count();

Есть ли способ установить в модели или где-то еще, что типом коллекции по умолчанию для свойства навигации является IQueryable, ObjectQuery или какой-либо запрашиваемый тип?

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


person bdwain    schedule 27.03.2013    source источник


Ответы (2)


Я придумал решение, которое решает большинство моих проблем. В итоге я использовал API ObjectContext и в своей модели и сделал свойство навигации, доступ к которому занимал много времени (для получения и настройки). Вы можете сделать это в файле edmx, щелкнув правой кнопкой мыши свойство nav.

Затем я сделал файл частичного класса для класса, который содержал свойство частной навигации, и добавил что-то в этом роде.

public ObjectQuery<NavPropType> NavPropName
{
   get
   {
      if(privateNavProp != null) //in case lazy loading is disabled or something
         return privateNavProp.CreateSourceQuery();
      else
         return null;
   }
}

Теперь ни один пользователь класса не сможет случайно попытаться вытащить все элементы navprop, которых очень много. И также легко сделать запрос в навигационной опоре вместо того, чтобы каждый раз вызывать CreateSourceQuery.

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

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

РЕДАКТИРОВАТЬ: один из недостатков приватности, с которым я столкнулся позже, заключается в том, что я больше не мог делать

db.EntryTable.OrderBy(e => e.privateNavProp.Count())

хотя было бы достаточно умно, чтобы не получить все Сущности

person bdwain    schedule 28.03.2013
comment
кто-нибудь знает, достаточно ли медленно CreateSourceQuery, чтобы стоило иметь закрытый член класса, который был установлен при первом вызове геттера и использовался в последующих вызовах вместо того, чтобы каждый раз вызывать CreateSourceQuery? - person bdwain; 28.03.2013

Нет. IQueryable не является коллекцией; IQueryable — это интерфейс, реализация которого оценивает запросы к источнику данных (коллекция будет источником данных).

Однако вы можете загрузить счетчик при загрузке объекта сущности, если вам не нравится встраивание некоторых безобидных вызовов методов:

from e in EntityA
<optional where clause for entity>
select new
{
    Entity = e,
    filteredNavPropCount = e.navprop.Where( np => <optional where clause for collection> ).Count()
}
person Moho    schedule 27.03.2013
comment
Хотя я нахожу удивительным и раздражающим, что количество вызовов в свойстве навигации не будет учитываться в базе данных, моя большая проблема заключается в том, что люди увидят, что счетчик вызовов в навигационной опоре работает, и больше ничего об этом не подумают. даже если они извлекают дополнительные данные из БД. - person bdwain; 27.03.2013
comment
Вы должны использовать его, если хотите отфильтровать коллекцию свойств навигации или каким-либо образом агрегировать ее, не извлекая все объекты из БД, как вы указали в своем вопросе. - person Moho; 27.03.2013
comment
ORM — это не высокая производительность, а простота использования. Ничто не сравнится с производительностью хранимой процедуры с пользовательским набором результатов, но это больше работы по кодированию. - person Moho; 27.03.2013
comment
не будет ли вызов e.navprop.where(whatever).count() получать каждый объект в e.navprop из БД? - person bdwain; 27.03.2013
comment
Не в этом примере, потому что запрос будет преобразован в оператор SQL и выполнен в базе данных. - person Moho; 27.03.2013