Повторное использование объединения в LINQ - PullRequest
3 голосов
/ 10 октября 2011

У меня есть несколько запросов (в проекте C #), которые все используют один и тот же синтаксис объединения, есть ли простой способ переместить объединение из запроса в «функцию» или «шаблон» (из-за отсутствияболее подходящее слово).

Текущий запрос выглядит следующим образом (rc - мой ObjectContext)

var x =
from b in rc.Pressures
join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
where g.UserId == userId
orderby b.Recorded descending
select b;

Я хотел бы иметь возможность взять объединение и поместить его в 'функция 'называется что-то вроде' PressuresJoin '

from b in rc.Pressures
join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
where g.UserId == userId

, а затем используется что-то вроде

var x = PressuresJoin()
orderby b.Recorded descending
select b;

Обновление: Следуя указаниям @Xanatos', я создал функцию IQueryable, напримерthis.

public static IQueryable<Pressure> PressuresJoin(ObjectContext rc, Guid userId)
{
    return from b in rc.Pressures
           join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
           where g.UserId == userId
           select b;
}

, который я затем использовал следующим образом

 IQueryable<Pressure> q = PressuresJoin(rc, userId);
            var y =
                from b in q
                orderby b.Recorded descending
                select b;

, поэтому использование IQueryable теперь означает, что мне не нужно писать один и тот же код соединения снова и снова, и при этомМне нужно убедиться, что значения давления, поступающие из БД, ограничены правильным пользователем (так как PressuresJoin гарантирует, что это всегда происходит).

Обратите внимание, что мне не нужно было создавать дополнительный класс (как это предлагается), потому чтоМне не нужен GUIDIdentityMaps information ПОСЛЕ объединения.

Очень аккуратно и опрятно - спасибо всем участникам

Обновление II: После ответа, предоставленного Кирком, я отошел от 'Синтаксис ручного соединения и просмотр с использованием свойства навигации.

Это привело меня к нескольким вариантам выбора. Первым было использовать .Include так

public static IQueryable<Pressure> PressuresJoinInclude(ObjectContext rc, Guid userId)
        {
            return from b in rc.Pressures.Include("GuidIdentityMap")
                   where b.GUIDIdentityMap.UserId == userId
                   select b;
        }

и называть его так:this

IQueryable<Pressure> q = PressuresJoinInclude(rc, userId);
var y =
from b in q
orderby b.Recorded descending
select b;

Это работает просто великолепно (поэтому ответ Киркс также является ответом), но мне не очень нравится синтаксис .Include (string), и он поймал меня, поскольку я неправильно написалGUIDIdentityMap.

Затем я посмотрел на способы избежать использования .Include (строка) и придумал

public static IQueryable<Pressure> PressuresJoinDirect(ObjectContext rc, Guid userId)
{
     return from b in rc.GUIDIdentityMaps.Single(i => i.UserId == userId).Pressures.AsQueryable()
     select b;
}

замечать AsQueryable () в конце (приведение его из IEnumerable обратно).в IQueryable) Это называется как

IQueryable<Pressure> q = PressuresJoinDirect(rc, userId);
            var y =
                from b in q
                orderby b.Recorded descending
                select b;

Пока что я думаю, что это мой любимый, хотя было бы лучше, если бы Pressures был «первым» объектом в выражении вместо GUIDIdentityMaps, но я непроверил, чтобы увидеть, что SQПроизводится L или какие есть различия в производительности (если есть)

Теперь я собираюсь посмотреть на решение Jims. (Точка).

Обновление III: Надеюсь, я правильно понял точечную запись, и вот мое «объединение» с использованием навигации, уже встроенной в объекты.Моя функция IQueryable выглядит следующим образом

   public static IQueryable<Pressure> PressuresJoinDot(ObjectContext rc, Guid userId)
   {
       return from b in rc.Pressures
              where b.GUIDIdentityMap.UserId == userId
              select b;
   }

, и использовать ее так же просто, как и другие

 IQueryable<Pressure> q = PressuresJoinDot(rc, userId);
            var y =
                from b in q
                orderby b.Recorded descending
                select b;

В конце всего этого я делаю Пропустить / Взять вот так

    IQueryable<Pressure> z = y.Skip(startRow).Take(rows);
    List<Pressure> l = z.ToList();

Все четыре вышеуказанные опции работают, и конечным результатом любого из этих запросов является один и тот же набор записей.К моему удивлению, они не все генерируют один и тот же SQL, и они не все работают с одинаковой скоростью (поэтому я разделил Skip / Take и ToList ()).

Простой старый Join иТочечная версия работает с одинаковыми скоростями, с простым присоединением, возможно, немного быстрее.Версия «Включить» работает хорошо, но количество операций чтения из БД увеличивается при переходе к списку записей.Прямой метод очень медленный, более чем в 10 раз по сравнению с другими методами, и я уверен, что Skip / Take выполняется для сгенерированного List (), а не для базы данных.

Конечно, мои глаза открылисьнасколько легко было бы выбрать неправильный LINQ и, конечно, выбрать его на основании того, что он выглядит «самым красивым», метод Direct, не будет лучшей идеей.

Ответы [ 3 ]

3 голосов
/ 10 октября 2011

То, что вы видите как join, действительно Queryable.Join.Таким образом, любой метод, который возвращает IQueryable<TResult>, может использоваться как PressuresJoin.

. Если вы явно работаете с linq-to-objects, вам нужно только вернуть IEnumerable<TResult>

class PressuresGUIDIdentityMap
{
    public Pressure Pressure;
    public GUIDIdentityMap GUIDIdentityMap;
}

public static IQueryable<PressuresGUIDIdentityMap> PressuresJoin(ObjectContext rc)
{
    return from b in rc.Pressures
        join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
        select new PressuresGUIDIdentityMap { Pressure = b, GUIDIdentityMap = g };
}

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

1 голос
/ 10 октября 2011

Вы должны просто использовать навигационные свойства. Если вы правильно сопоставили свои сущности, то Entity Framework должен позаботиться о «объединениях» для вас.

См. Мой вопрос Зачем использовать LINQ Join в простых отношениях один-много?

Вместо

from b in rc.Pressures
join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
where g.UserId == userId

просто позвоните

rc.Pressures.Include("GUIDIdentityMaps")

или, если вы предпочитаете,

from b rc.Pressures.Include("GUIDIdentityMaps")
select b
1 голос
/ 10 октября 2011

Вы можете взглянуть на использование скомпилированного запроса для повышения производительности. Цитата:

"Если у вас есть приложение, которое выполняет структурно подобные запросы много раз в Entity Framework, вы можете часто повышать производительность, компилируя запрос один раз и выполняя его несколько раз с различными параметрами. Например, приложению может потребоваться получить все клиенты в определенном городе; город указывается пользователем во время выполнения в форме. LINQ to Entities поддерживает использование для этой цели скомпилированных запросов. "

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...