Skip to content

Latest commit

 

History

History
263 lines (194 loc) · 10.1 KB

10-linq.md

File metadata and controls

263 lines (194 loc) · 10.1 KB

LINQ

LINQ to

Пачка linq запросов к различным сущностям

  • LINQ to Objects: операции с массивами и коллекциями
  • LINQ to Entities: используется при обращении к базам данных через технологию Entity Framework
  • [Deprecated] LINQ to Sql: технология доступа к данным в MS SQL Server
  • LINQ to XML: операции с XML
  • Parallel LINQ (PLINQ): параллельные запросы

По сути это набор extension методов для коллекций разного вида и QueryExpression синтаксис для них

Methods

Отложенные выборки:

  • Select: определяет проекцию выбранных значений
  • Where: определяет фильтр выборки
  • OrderBy: упорядочивает элементы по возрастанию
  • OrderByDescending: упорядочивает элементы по убыванию
  • Reverse: располагает элементы в обратном порядке
  • Distinct: удаляет дублирующиеся элементы из коллекции
  • Take, Skip, TakeWhile, SkipWhile - пейджинг в выборке

Отложенные объединения и группировки:

  • Join: соединяет две коллекции по определенному признаку
  • GroupBy: группирует элементы по ключу
  • ToLookup: группирует элементы по ключу, при этом все элементы добавляются в словарь
  • Concat: объединяет две коллекции
  • Zip: объединяет две коллекции в соответствии с определенным условием
  • Except: возвращает разность двух коллекцию, то есть те элементы, которые содератся только в одной коллекции
  • Union: объединяет две однородные коллекции
  • Intersect: возвращает пересечение двух коллекций, то есть те элементы, которые встречаются в обоих коллекциях

Неотложенные:

  • All: определяет, все ли элементы коллекции удовлятворяют определенному условию
  • Any: определяет, удовлетворяет хотя бы один элемент коллекции определенному условию
  • Contains: определяет, содержит ли коллекция определенный элемент
  • Count - количество элементов коллекции, которые удовлетворяют определенному условию
  • Sum, Average, Min, Max - арифметические вычисления

Немедленная выборка конкретного элемента:

  • First: выбирает первый элемент коллекции
  • FirstOrDefault: выбирает первый элемент коллекции или возвращает значение по умолчанию
  • Single: выбирает единственный элемент коллекции, если коллекция содердит больше или меньше одного элемента, то генерируется исключение
  • SingleOrDefault: выбирает первый элемент коллекции или возвращает значение по умолчанию
  • ElementAt: выбирает элемент последовательности по определенному индексу
  • ElementAtOrDefault: выбирает элемент коллекции по определенному индексу или возвращает значение по умолчанию, если индекс вне допустимого диапазона
  • Last: выбирает последний элемент коллекции
  • LastOrDefault: выбирает последний элемент коллекции или возвращает значение по умолчанию
var list = new List<string> { "First", "Second", "Third", "Fourth", "Fifth"};

IEnumerable<string> query = list.Where(x => x.Contains("i");
int count = query.Count();
string element = query.LastOrDefault();
var ordered = list.OrderBy(x => x).ToList();

Пример, как можно писать в функциональном стиле :)

FizzBuzz - Counting from 1 to 100, and every time a number is divisible by 3 calling "Fizz", every time a number is divisible by 5 calling "Buzz" and every time a number is divisible by 3 and 5, calling "FizzBuzz instead of the number

public void DoFizzBuzz()
{
    for (int i = 1; i <= 100; i++)
    {
        bool fizz = i % 3 == 0;
        bool buzz = i % 5 == 0;
        if (fizz && buzz)
            Console.WriteLine ("FizzBuzz");
        else if (fizz)
            Console.WriteLine ("Fizz");
        else if (buzz)
            Console.WriteLine ("Buzz");
        else
            Console.WriteLine (i);
    }
}
Enumerable.Range(1, 100)
    .Select(x =>
        (x % 15 == 0) ? "FIZZBUZZ"
        : (x % 5 == 0) ? "BUZZ"
        : (x % 3 == 0) ? "FIZZ"
        : x.ToString()
    ).ToList().ForEach(console.WriteLine);

Использование метода ForEach(x=> ...) вместо цикла foreach считается не очень

  • ухудшает читаемость
  • появляются замыкания
var list = new List<string> { "First", "Second", "Third", "Fourth", "Fifth"};

// лучше не так
list.ForEach(x=> Console.WriteLine(x));

// Делайте так:
foreach(var x in list)
    Console.WriteLine(x);

Deferred, Immediate Query Execution

Запросы делятся на отложенные и неотложенные:

  • Отложенные запросы Where, OrderBy, Join, etc не выполняются сразу
  • Реально выполнение запроса будет происходить только при вызове неотложенных запросов (Count, SingleOrDefault, First, ToList, etc) или при переборе foreach
string[] teams = { "First", "Second", "Third", "Fourth", "Fifth"};

IEnumerable<string> query = teams.Where(x=>x.Contains("i"));

Console.WriteLine(query.Count());  // 3
teams[1] = "Linq";
Console.WriteLine(query.Count());  // 4

Можно фильтровать, не выполняя запрос:

IEnumerable<User> users = getAllUsers;

if (filter.Type != CredentialType.Undefined)
    users = users.Where(c => c.CredentialType == credentialType);

if (! string.IsNullOrEmpty(filter.Credential))
    users = users.Where(c => c.Credential == filter.Credential);

users = users.Skip(10).Take(6);
users = users.ToList();             // Реальное выполнение только здесь

Подробно как выполняются отложенные запросы:

void Main()
{
    var query = GetInts().Skip(5);

    Console.WriteLine("== First iteration:");
    Console.WriteLine(string.Join(",", query.Take(3).ToList()));

    Console.WriteLine("== Second iteration:");
    // вот здесь он по новой создаст автомат и прогенерирует заново
    Console.WriteLine(string.Join(",", query.Take(3).ToList()));
}

private IEnumerable<int> GetInts()
{
    var i = 0;
    while (true)
    {
        Console.WriteLine($"yield return {i}");
        yield return i++;
    }
}

Стандартный и Query Expressions синтакис запросов

  • Помимо вызова Extension методов можно использоть Query Expressions синтаксис, который отдаленно напоминает tsql
  • Используется для более наглядного написания join'ов

Inner join:

from l in db.Licenses
join il in db.InstanceLicenses on l.Id equals il.LicenseId
where il.InstanceId == instanceId
select l

left outer join SOF:

var userQuery =
    from user in context.GetTable<User>()
    from email in context.GetTable<UserEmail>().Where(x => x.EmailId == userProfile.EmailId).DefaultIfEmpty()
    where userProfile.UserId == userId
    select new { User = user, Email = email};

var result = userQuery.SingleOrDefault();

multiple group by SOF

var consolidated =
    from x in elementList
    group x by new
    {
        x.Type,
        x.Key,
        x.Value,
    } into gcs
    select new
    {
        Type = gcs.Key.Type,
        Key = gcs.Key.Key,
        Value = gcs.Key.Value,
        List = gcs.ToList(),
    };