Обновить
4

Пользователь

2
Подписчики
Отправить сообщение

Думаю жаловаться/предлагать надо в Github, толку больше.

Интересует скорость выполнения на сотнях миллионах записей, остальное в документации описано да и не далеко от стандарта ушло или даже не убежало.

Согласен с предыдущим постом. Orleans именно то что здесь нужно.

Они хотят заменить .NET Framework на .NET 5.
Значит надо все WinForms, WPF и тд. тянуть на себя. Никто от вас линукс не забирает, но также и никто не будет WinForms портировать под него — просто не ставьте не кросплатформенные библиотеки.

Для балансировки нагрузки и масштабирования я имею опыт использования ApacheMq

Посмотрел, что-то больно на Java мир завязано. И разве это не брокер сообщений?
Я бы его больше к этому проекту сравнивал: https://github.com/dapr/dapr


В общем-то те же фичи что описаны Вами для Microsoft Orleans, но плюс еще кросс-платформенность

.NET Core давно кросплатформенный как бы.

Мне иетересно, пробовали ли вы Microsoft Orleans? Как на меня отличнейшее решение для 90% случаев. Но тут я пока только как теоретик, пару раз кластер подымал — проще ничего нету.
Первое что в голову приходит


  • Строгая типизация реквестов через интерфейсы со скоростной сериализацией
  • Автоматическая балансировка вплоть до миграции воркера на другую машину с состоянием (естественно надо persistanse писать)
  • Автоматический failover
  • Поддержка кластерного singleton по умолчанию. Удобно, знеаете ли, знать что три машины не колбасят ту же задачу или данные.
  • Distributed cache в силу того что работает предыдущий пункт
  • Встроенные события/потоки/таймеры/remainders
  • Discovery

Можно еще покопать, но как на меня профит очевиден: пишите себе на чистом .NET забыв о swagger. Расслабляетесь от балансировки "микросервисов", от толпы не нужных инстансов. Не конфигурируете кеши, очереди (ну без них не всегда можно).


Сумбурно как-то так

items.Count()

Не затянули ли вы всю таблицу чтобы подсчитать количество записей?
Ну в общем то что заметил, разве что сложные отчеты это не к EF :).

Не все упирается в выборку, хотя тут linq2db даст фору по качеству SQL.
Например изменение записи в EF это два обращения к базе данных, и то желательно в транзакции.


  • Достать обьект из базы полностью
  • Изменить свойство
  • SaveChanges

В linq2db это однин стейтмент и ничего на клиент тянуть не пришлось


   db.Products
     .Where(p => p.ProductId == dto.ProductId)
     .Set(p => p.SoldDate, () => Sql.CurrentTimestamp)
     .Set(p => p.IsSold, true)
     .Update();

Конечно возможно приаттачить обьект к ChangeTracker, только делают это редко.
Также из примера видно что мы использовали текущую дату сервера. Что в EF проблематично.


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

Это риторический вопрос?
На всякий случай вот dotnet.github.io/orleans
> Не может этот запрос быть быстрее обычного count без оконной функции

Как бы очевидно.
Но план запроса строится один раз в отличии от двух запросов в одном раундтрипе. И мало какой ADO.NET провайдер такое поддерживает. Сказал же в полтора раза быстрее.
Если все-же, количество записей важно, не забываем об оконных функциях.

SELECT COUNT(*) OVER () as TotalCount, * 
FROM Sales.SalesOrderHeader
ORDER BY OrderDate DESC
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY


Теперь в каждой записи будет зашито реальное количество записей. Достаточно прочитать из первой записи это значение, а дальше игнорировать. На вскидку, получается в полтора раза быстрее и один запрос к базе.

Естественно это будет работать только если вы не применяли другие техники выбора страницы.
Ну, можно присмотреться к этой библиотеке, человеку очень не хватало монад и функционального в C#. Все никак не соберусь попробовать, пугает на сколько может упасть скорость работы и что все-таки компилятор с JIT соптимизируют.
github.com/louthy/language-ext
Да, есть библиотека для бескровного склеивания двух миров. Асинки вот только конфликтуют, для решения этой ситуации добавлены дополнительные экстеншины.
github.com/linq2db/linq2db.EntityFrameworkCore
В названии статьи отсутствует LINQ by EF. Так как оно не отражает полную картину. EF всегда был калекой в трансляции дерева выражений в SQL.

Практически все эти детские болезни мы полечили в linq2db. Клеить строки не надо, Dapper не надо, делать хранимки на каждый чих не надо.

  • Хинты можно использовать
  • CTE из каробки, вместе с рекурсивными CTE
  • Union, Concat собирает все что надо, хоть завались ими
  • INSERT FROM, INSERT INTO, UPDATE FROM...
  • Менять названия таблиц налету
  • Использовать временные таблицы
  • Оконные функции из каробки
  • BulkCopy на все поддерживаемые базы данных
  • CRUD по CQRS паттерну, ничего лишнего вплоть до единственного поля
  • ...


“Но они продолжали жевать кактус и дискутировать какой generic repository лучше и когда стартовать UoW.”
Я на визиторы смотрю как на ужас. Наверное наша ORM давно бы погрязла в куче кассов и непонятных иерархий.
Простая, постоянно требующя решения задача подмены параметра в теле лямбды превращается в армагеддон. Да она решается созданием одного ReplacingExpressionVisitor (такой себе класик)
И такого вызова:

var newParam = Expression.Parameter(someType); 
var newBody = ReplacingExpressionVisitor.Replace(lambda.Paramters[0], newParam, lambda.Body);

А еще она решаться может и вот так:
var newParam = Expression.Parameter(someType); 
var newBody = lambda.Body.Transform(e => e == lambda.Paramters[0] ? newParam : e);


Вроде одинаково, но гипотетически усложним задачу, мало того, что надо подменить параметр, так и прибавить единички ко всем целочисленным константам. Тут уже надо городить новые классы или даже пропускать через несколько визиторов, а мы в лямбде для Transform просто усложняем условие. Это может быть ощутимо заметно на гигантском дереве, такие иногда получаются когда запрос к базе содержит монструидальный Where, созданный динамичиски какой-то UI библиотекой.

Минус Transform в том что если вы придумываете кастомные Expression, тут уже сильно не потрансформируешь, или придется расширять метод.

Если интересна реализация, ее можно найти в библиотеке CodeJam или в нашей ORM linq2db.

В защиту данного подхода скажу так, за 5 лет мне ни разу не пришлось писать новый Visitor.
Вы не понимаете что такое энумераторы и роль yield return в коде парсера.

Я бы советовал подучить эту часть.
Как только, кто-то начинает энумерацию, создается стейт машина (класс в который превращается ParseCsv метод компилятором) и при вызове метода Next в энумераторе из файла читается одна линия, не больше. Когда файл заканчивается Next вернет false — все мы перебрали все линии.
Не учить же вас как пользоваться C#. Вот примерная реализация парсера. Пишу на коленке, важен подход.
public IEnumerable<string[]> ParseCsv(string fileName)
{
   using (var file = new System.IO.StreamReader(fileName))
   {
       string line;
       while((line = file.ReadLine()) != null)  
       {  
            yield return line.Split(‘\t’);
       }
    }
}

linq2db внутри считывает из этого энумератора только 1000 записей и запихивает это в BulkCopy (для MSSQL) или готовит компактный SQL с параметрами на всю 1000 записей. Объекты больше не нужны и их подберет GC.
Процесс повторяется пока до конца файла не дойдем. Энумератор диспозится и файл закрывается.

Точно также можно взять данные IEnumerable из одной базы и закинуть в другую. Не важно сколько записей в исходной таблице, хоть миллионы.
Вся магия внутри, по умолчанию обрабатываются пачки по 1000 записей, и это конфигурируется. Догадываетесь почему?

Вы пропустили мое описание что данные должны представляться как IEnumerable — это не все записи сразу, ни в коем случае.
Могу подкинуть другой вариант:
Берется linq2db
Берется любая библиотека которая CSV парсит и потоково возвращает IEnumerable<string[]>, тут я не помощник.

И пишется что-то такое
var csvItems = CSVParser.Parse("filename");

using (var db = new DataConnection(...))
{
    db.BulkCopy(
       csvItems.Select(csv => new DestinationEntity{
          Field1 = int.Parse(csv[0]),
          Field2 = double.Parse(csv[1]),
          LongStr = csv[2],
          ...
       })
    );
}

Гарантирую, что если отключите индексы, данные влетят, даже чай не успеете заварить.
Для баз данных, которые поддерживают такие вставки, и того быстрее. Будут посланы параметризированные запросы и не надо беспокоиться о длине полей.
1) Возможно, специфика есть специфика
2) Так и делают. Тянут ORM на клиент, хотя в случае EF Core это толпа зависимостей. По этому часто используют Remote LINQ или что-то в этом роде.

О случае когда вам IQueryable не хватило можно поподробнее?

p.s. код лучше было оформить в виде ссылки на codebin или что-то вроде того

Возможно, но так как писано на коленке, тогда и не задумывался.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность