Linq to SharePoint. Часть 4. Dynamic LINQ
 Часть 1. First()/FirstOrDefault(), T-SQL IN, Path
  Часть 2. Count(), Take(), Skip(), JOIN
  Часть 3. Анонимный доступ, Получение списка по URL'у, Cross-Site запросы
  Часть 4. SPListItem -> LINQ, Dynamic Linq to SharePoint
  Часть 5. Поля Choice и MultiChoice в Linq to SharePoint
  Часть 6. Сравнение производительности Linq to SharePoint и Camlex.NET
Все примеры в этом посте будут основаны на модели данных, описанной мною во второй части (исходные коды можно скачать здесь).
Linq to SharePoint и SPListItem
Предположим, что у нас есть очень сложный CAML-запрос, который откуда только не выбирает данные с безумным фильтром. Или, что более реально, у нас есть SPListItem в EventReceiver'е. И в обоих случаях мы хотим получить объект ZhukBlogLinqExamples.Model.Employee.
Создадим конструктор в базовом классе, чтобы эта возможность появилась во всех дочерних классах и, чтобы, в случае создания новых классов, нам не приходилось писать новый код:
- public class ZhukDataItem : ITrackEntityState, ITrackOriginalValues,
-                             INotifyPropertyChanged, INotifyPropertyChanging
- {
-     public ZhukDataItem(SPItem item)
-     {
-     }
- }
Чтобы заполнить свойства будем использовать атрибут ColumnAttribute наших свойств: 
Дополнительно можно проверять на реализацию объектом интерфейса ICustomMapping и вызывать метод MapFrom. Объект мы получили, теперь надо присоединить его к контексту данных. Для этого используем метод Attach:
- using (var ctx = new ZhukDataContext(siteUrl))
- {
-     // Получаем объект SPListItem
-     SPListItem employeeListItem = GetEmployeeListItem(employeeId);
-     // Создаем экземпляр класс Employee
-     var entity = new Employee(employeeListItem);
-     // Присоединяем объект к контексту
-     ctx.Employees.Attach(entity);
-     
-     // Проводим необходимые манипуляции
-  
-     // Сохраняем изменения
-     ctx.SubmitChanges();
- }
Dynamic Linq to SharePoint
В первой части я писал о том, что Linq to SharePoint не поддерживает Contains(). Теперь я покажу способ это ограничение обойти. Для этого напишем метод расширитель, который будет строить выражения для каждого значения из массива (тот самый IN из T-SQL) и затем объединять их:
- public static Expression<Func<T, bool>> EqualsAny<T, TValue>(this Expression<Func<T, TValue>> selector, 
-     IEnumerable<TValue> values)
- {
-     // Если значений нет, то возвращаем выражение x=> false
-     if (!values.Any()) return x => false;
-     // Аналогично поступаем в случае, когда кол-во параметров не равно одному
-     if (selector.Parameters.Count != 1) return x => false;
-     // Берем параметр селектора.
-     // Он понадобиться для построения выражений
-     var p = selector.Parameters.First();
-     // Для каждого значения строим выражение
-     var equals = values
-         .Select(v => (Expression)Expression.Equal(selector.Body, 
-                                  Expression.Constant(v, typeof(TValue))));
-     // Объдиняем получившиеся выражения
-     var body = equals.Aggregate(Expression.Or);
-     // Возвращаям получившиеся выражение
-     return Expression.Lambda<Func<T, bool>>(body, p);
- }
Аналогично можно сделать методы для текстовых значение (StartsWitAny, ContainsAny и др.). А вот объединять произвольные выражения у меня не получилось. Чтобы их объединить приходилось пользоваться методом Expression.Invoke. После чего SPLinqProvider выбрасывал исключение: Lambda Parameter not in scope. Аналога методов WhereAny и WhereAll из CamleX'а у меня не получилось.
Использовать новый метод можно даже в достаточно сложной конструкции:
- using (var ctx = new ZhukDataContext(siteUrl))
- {
-     var ids = new int?[] { 1, 3, 5, 7, 9, 11, 13, 15 };
-     var predicate = EqualsAny<Employee, int?>(emp => emp.Id, ids);
-     var employees = ctx.Employees
-         .ScopeToFolder(string.Empty, true)
-         .Where(emp => emp.ManagerId == 2)
-         .Where(predicate)
-         .Where(emp => emp.AccessLevel > 2)
-         .OrderBy(emp => emp.Title)
-         .Take(5)
-         .ToList();
-     //...
- }
В итоге получается большой CAML-запрос, позволяющий получать только те данные, которые необходимы и тем самым повысить производительность:
- <View Scope='RecursiveAll'>
-   <Query>
-     <Where>
-       <And>
-         <And>
-           <And>
-             <BeginsWith>
-               <FieldRef Name="ContentTypeId" />
-               <Value Type="ContentTypeId">0x010078B0DD38574940478CF9E129FCD65E9B</Value>
-             </BeginsWith>
-             <Eq><FieldRef Name="Manager" LookupId="TRUE" /><Value Type="Lookup">2</Value></Eq>
-           </And>
-           <Or>
-             <Or>
-               <Or>
-                 <Or>
-                   <Or>
-                     <Or>
-                       <Or>
-                         <Eq><FieldRef Name="ID" /><Value Type="Counter">1</Value></Eq>
-                         <Eq><FieldRef Name="ID" /><Value Type="Counter">3</Value></Eq>
-                       </Or>
-                       <Eq><FieldRef Name="ID" /><Value Type="Counter">5</Value></Eq>
-                     </Or>
-                     <Eq><FieldRef Name="ID" /><Value Type="Counter">7</Value></Eq>
-                   </Or>
-                   <Eq><FieldRef Name="ID" /><Value Type="Counter">9</Value></Eq>
-                 </Or>
-                 <Eq><FieldRef Name="ID" /><Value Type="Counter">11</Value></Eq>
-               </Or>
-               <Eq><FieldRef Name="ID" /><Value Type="Counter">13</Value></Eq>
-             </Or>
-             <Eq><FieldRef Name="ID" /><Value Type="Counter">15</Value></Eq>
-           </Or>
-         </And>
-         <Gt><FieldRef Name="AccessLevel" /><Value Type="Integer">2</Value></Gt>
-       </And>
-     </Where>
-     <OrderBy Override="TRUE"><FieldRef Name="Title" /></OrderBy>
-   </Query>
-   <ViewFields>
-     <FieldRef Name="CellPhone" />
-     <FieldRef Name="AccessLevel" />
-     <FieldRef Name="Manager" />
-     <FieldRef Name="Department" />
-     <FieldRef Name="ID" />
-     <FieldRef Name="owshiddenversion" />
-     <FieldRef Name="FileDirRef" />
-     <FieldRef Name="Title" />
-     <FieldRef Name="Author" />
-     <FieldRef Name="Editor" />
-   </ViewFields>
-   <RowLimit Paged="TRUE">5</RowLimit>
- </View>
Осталось, пожалуй, только описать работу с оптимистическими блокировками в Linq to SharePoint. В ближайшее время напишу.
 
 









































