Если у вас есть веб сервисы, который дергаются на клиенте, то хочется чтоб они выполнялись быстро, чтоб продолжить выполнение каких-то операций на клиенте. А если у вас AJAX и Rich UI, то тем более хочется чтоб отклик был как можно быстрей. Конечно, это относится не только к сервисам, а и к приложению в целом.
Если у вас есть запросы, которые выполняются часто, например GetCustomerByName, GetPostsByAuthor и тп, и вы знаете что в запросе не будут добавляться фильтры то их можно оптимизировать. Под оптимизацией я понимаю кэширование процесса построения запроса, а именно скомпилировать запрос. Другими словами вы получаете хранимую процедуру в виде LINQ запроса в которую вы просто передаете параметры.
Рассмотри пример. У вас есть форма, в который вы ищете страну, чтоб показать её на карте или тп., запрос будет выглядеть примерно так:
using (DBContext db = new DBContext()) { var countries = (from country in db.Countries where country.Name.ToLower() == key.ToLower() select country).ToList() // do actions with result. }
Компилируется запрос так:
CompiledQuery.Compile<DBContext, string, IQueryable<Country>>( (db, key) => (from country in db.Countries where country.Name.ToLower() == key.ToLower() select country));
Compile принмиает Func<>, можете передать контекст, дополнительные параметры и возвращаемый результат. Потом по коду можно использовать его так:
IQueryable<Country> countries = GetCountryByName.Invoke(db, txtCountry.Text);
Если у вас web приложение можно сохранить запрос в статическую переменную. В другом случае - "шило на мыло".
public Func<DBContext, string, IQueryable<Country>> GetCountryByName = CompiledQuery.Compile<DBContext, string, IQueryable<Country>>( (db, key) => (from country in db.Countries where country.Name.ToLower() == key.ToLower() select country));Если у вас есть Repository классы, можно компилировать запрос при первом обращении. Для того чтоб код не "вонял", можно вынести запрос в отдельный класс Queries. При желании можно создать и подклассы, например Queries.Location.GetCountryByName:
public static IQueryable<Country> GetCountryByName(int name) { if (Queries.Location.GetCountryByName == null) { Queries.Location.GetCountryByName = CompiledQuery.Compile<DBContext, string, IQueryable<Country>>( (db, key) => (from country in db.Countries where country.Name.ToLower() == key.ToLower() select country)); } return Queries.Location.GetCountryByName.Invoke(_context, name); }
Если у вас в результате запроса возвращается сложный тип (анонимный), нужно создать его проекцию, например CountryDTO.
С LINQ to SQL есть проблемы: вы не можете догрузить смежные таблицы используя один DataContext. Например у вас есть выборка по постам блога и вы хотите подтянуть авторов и коментарии, это можно сделать через DataLoadOptions. Но LoadOptions можно задать только один раз для одного экземпляра DataContext, в другом случае получите ошибку. Если вы используете DataContext атомарно для запроса, то проблем нет. Но если у вас DataContext создаете на один HttpContext - то провал. Как вариант, конкретно для методов где используется компилированый запрос, вы можете использовать DataContext атомарно.
В LINQ to Entities эта проблема решена с помощью Include, где вы можете прям в запросе указать, какие таблицы вы хотите загрузить сразу, а не по требованию.
Полезные ссылки: