Возможно ли создать экземпляр пустого IQueryable без вызова базы данных?

#c# #linq-to-sql #iqueryable

#c# #linq-to-sql #iqueryable

Вопрос:

Вызов datacontext.Things вернет an IQueryable<Thing> . Но в моем случае активный пользователь может не иметь прав на просмотр каких-либо объектов. В этом случае я хотел бы вернуть пустой IQueryable вещей из моего репозитория. До сих пор я использовал

 return new Enumerable.Empty<Thing>.AsQueryable();
  

чтобы выполнить это, и это сработало нормально.

Мы внесли некоторые изменения, и теперь, позже, запрашиваемый объект соединяется с другим IQueryable. Выполнение этого вызывает следующее исключение:

 An IQueryable that returns a self-referencing Constant expression is not supported.
  

Это происходит потому, что linq не может соединить фактический IQueryable с чем-то, что действует как IQueryable, но на самом деле является перечислимым.

Я искал решение этой проблемы в SO, и единственной альтернативой, которую я нашел, было использование

 return datacontext.Things.Take(0);
  

Несмотря на то, что это устраняет вышеуказанную проблему, поскольку возвращает фактический IQueryable, я предполагаю, что он также фактически выполняет вызов базы данных, что кажется ненужной тратой времени, поскольку я заранее знаю, что вызов не вернет никаких записей. Или Linq2SQL достаточно умен, чтобы фактически не выполнять этот запрос?

Есть ли способ создать пустой IQueryable, который все еще можно использовать в join без вызова базы данных?

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

1. » Я предполагаю, что он также фактически выполняет вызов базы данных, что кажется ненужной тратой времени, поскольку я заранее знаю, что вызов не вернет никаких записей» зачем предполагать? Почему бы вам просто не проверить, был ли выполнен какой-либо запрос? SSMS позволяет это, например

2. Похоже, вы пытаетесь заставить EF делать то, для чего он не предназначен. Также IQueryable такой обход является странным. Если люди не должны получать доступ к определенной области, то я уверен, что есть лучшие способы добиться этого.

3. О, и да, Take(0) все равно попадет в базу данных, но с WHERE 1 = 0 предложением (cc @CamiloTerevinto)

4. Вы действительно используете linq-to-sql или EF? На данный момент EF в значительной степени заменил linq-to-sql.

5. Я использую linq-to-sql. Не EF.

Ответ №1:

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

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

Это если вы иногда не повторяете это возвращаемое значение, а иногда составляете его в более сложные запросы. В этом случае вам, вероятно, нужно просто … не делать этого. Используемая вами платформа запросов позволит вам составлять запросы, которые при повторении будут фактически запрашивать базу данных. Вы, конечно, могли бы написать свою собственную структуру запросов, которая вела бы себя по-другому, но это было бы очень нетривиально.

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

1. «потенциально взаимодействующий с базой данных, если запрос был определен так, чтобы он нуждался в этом» (…) «Используемая вами структура запросов позволит вам составлять запросы, которые при повторении будут фактически запрашивать базу данных» — противоречат ли эти два утверждения? Сначала вы заявляете, что запрос может не запускаться в БД, но затем вы заявляете, что используемая мной структура этого не позволит. Тогда эта возможность, предложенная в первом утверждении, применима только к другим фреймворкам? Или я неправильно это понимаю?

2. @Fuzzy Можно написать an IQueryable , который может использовать или не использовать базу данных. Используемая вами структура запросов не позволит вам встраивать подзапрос, если только этот подзапрос не будет запрашивать ту же базу данных. Вы можете делать другие вещи с IQueryable помощью though помимо встраивания их в подзапрос запроса конкретного поставщика запросов. В основном обычно выполняется без встраивания в другой запрос.