Они хотят заменить .NET Framework на .NET 5.
Значит надо все WinForms, WPF и тд. тянуть на себя. Никто от вас линукс не забирает, но также и никто не будет WinForms портировать под него — просто не ставьте не кросплатформенные библиотеки.
Для балансировки нагрузки и масштабирования я имею опыт использования ApacheMq
Посмотрел, что-то больно на Java мир завязано. И разве это не брокер сообщений?
Я бы его больше к этому проекту сравнивал: https://github.com/dapr/dapr
В общем-то те же фичи что описаны Вами для Microsoft Orleans, но плюс еще кросс-платформенность
Мне иетересно, пробовали ли вы Microsoft Orleans? Как на меня отличнейшее решение для 90% случаев. Но тут я пока только как теоретик, пару раз кластер подымал — проще ничего нету.
Первое что в голову приходит
Строгая типизация реквестов через интерфейсы со скоростной сериализацией
Автоматическая балансировка вплоть до миграции воркера на другую машину с состоянием (естественно надо persistanse писать)
Автоматический failover
Поддержка кластерного singleton по умолчанию. Удобно, знеаете ли, знать что три машины не колбасят ту же задачу или данные.
Distributed cache в силу того что работает предыдущий пункт
Встроенные события/потоки/таймеры/remainders
Discovery
Можно еще покопать, но как на меня профит очевиден: пишите себе на чистом .NET забыв о swagger. Расслабляетесь от балансировки "микросервисов", от толпы не нужных инстансов. Не конфигурируете кеши, очереди (ну без них не всегда можно).
Не все упирается в выборку, хотя тут linq2db даст фору по качеству SQL.
Например изменение записи в EF это два обращения к базе данных, и то желательно в транзакции.
Достать обьект из базы полностью
Изменить свойство
SaveChanges
В linq2db это однин стейтмент и ничего на клиент тянуть не пришлось
Конечно возможно приаттачить обьект к ChangeTracker, только делают это редко.
Также из примера видно что мы использовали текущую дату сервера. Что в EF проблематично.
Вот из таких кирпичиков и растет перформанс. Чем дальше мы от SQL, тем больше мы полагаемся на архитектуру всемогутора, а она не всегда эфективна.
> Не может этот запрос быть быстрее обычного 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
Я на визиторы смотрю как на ужас. Наверное наша 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 из одной базы и закинуть в другую. Не важно сколько записей в исходной таблице, хоть миллионы.
Могу подкинуть другой вариант:
Берется 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 или что-то вроде того
Возможно, но так как писано на коленке, тогда и не задумывался.
Думаю жаловаться/предлагать надо в Github, толку больше.
Интересует скорость выполнения на сотнях миллионах записей, остальное в документации описано да и не далеко от стандарта ушло или даже не убежало.
Согласен с предыдущим постом. Orleans именно то что здесь нужно.
Они хотят заменить .NET Framework на .NET 5.
Значит надо все WinForms, WPF и тд. тянуть на себя. Никто от вас линукс не забирает, но также и никто не будет WinForms портировать под него — просто не ставьте не кросплатформенные библиотеки.
Посмотрел, что-то больно на Java мир завязано. И разве это не брокер сообщений?
Я бы его больше к этому проекту сравнивал: https://github.com/dapr/dapr
.NET Core давно кросплатформенный как бы.
Мне иетересно, пробовали ли вы Microsoft Orleans? Как на меня отличнейшее решение для 90% случаев. Но тут я пока только как теоретик, пару раз кластер подымал — проще ничего нету.
Первое что в голову приходит
Можно еще покопать, но как на меня профит очевиден: пишите себе на чистом .NET забыв о swagger. Расслабляетесь от балансировки "микросервисов", от толпы не нужных инстансов. Не конфигурируете кеши, очереди (ну без них не всегда можно).
Сумбурно как-то так
Не затянули ли вы всю таблицу чтобы подсчитать количество записей?
Ну в общем то что заметил, разве что сложные отчеты это не к EF :).
Не все упирается в выборку, хотя тут linq2db даст фору по качеству SQL.
Например изменение записи в EF это два обращения к базе данных, и то желательно в транзакции.
В linq2db это однин стейтмент и ничего на клиент тянуть не пришлось
Конечно возможно приаттачить обьект к ChangeTracker, только делают это редко.
Также из примера видно что мы использовали текущую дату сервера. Что в EF проблематично.
Вот из таких кирпичиков и растет перформанс. Чем дальше мы от SQL, тем больше мы полагаемся на архитектуру всемогутора, а она не всегда эфективна.
На всякий случай вот dotnet.github.io/orleans
Как бы очевидно.
Но план запроса строится один раз в отличии от двух запросов в одном раундтрипе. И мало какой ADO.NET провайдер такое поддерживает. Сказал же в полтора раза быстрее.
Теперь в каждой записи будет зашито реальное количество записей. Достаточно прочитать из первой записи это значение, а дальше игнорировать. На вскидку, получается в полтора раза быстрее и один запрос к базе.
Естественно это будет работать только если вы не применяли другие техники выбора страницы.
github.com/louthy/language-ext
github.com/linq2db/linq2db.EntityFrameworkCore
Практически все эти детские болезни мы полечили в linq2db. Клеить строки не надо, Dapper не надо, делать хранимки на каждый чих не надо.
“Но они продолжали жевать кактус и дискутировать какой generic repository лучше и когда стартовать UoW.”
Простая, постоянно требующя решения задача подмены параметра в теле лямбды превращается в армагеддон. Да она решается созданием одного ReplacingExpressionVisitor (такой себе класик)
И такого вызова:
А еще она решаться может и вот так:
Вроде одинаково, но гипотетически усложним задачу, мало того, что надо подменить параметр, так и прибавить единички ко всем целочисленным константам. Тут уже надо городить новые классы или даже пропускать через несколько визиторов, а мы в лямбде для Transform просто усложняем условие. Это может быть ощутимо заметно на гигантском дереве, такие иногда получаются когда запрос к базе содержит монструидальный Where, созданный динамичиски какой-то UI библиотекой.
Минус Transform в том что если вы придумываете кастомные Expression, тут уже сильно не потрансформируешь, или придется расширять метод.
Если интересна реализация, ее можно найти в библиотеке CodeJam или в нашей ORM linq2db.
В защиту данного подхода скажу так, за 5 лет мне ни разу не пришлось писать новый Visitor.
Я бы советовал подучить эту часть.
Как только, кто-то начинает энумерацию, создается стейт машина (класс в который превращается ParseCsv метод компилятором) и при вызове метода Next в энумераторе из файла читается одна линия, не больше. Когда файл заканчивается Next вернет false — все мы перебрали все линии.
linq2db внутри считывает из этого энумератора только 1000 записей и запихивает это в BulkCopy (для MSSQL) или готовит компактный SQL с параметрами на всю 1000 записей. Объекты больше не нужны и их подберет GC.
Процесс повторяется пока до конца файла не дойдем. Энумератор диспозится и файл закрывается.
Точно также можно взять данные IEnumerable из одной базы и закинуть в другую. Не важно сколько записей в исходной таблице, хоть миллионы.
Вы пропустили мое описание что данные должны представляться как IEnumerable — это не все записи сразу, ни в коем случае.
Берется linq2db
Берется любая библиотека которая CSV парсит и потоково возвращает IEnumerable<string[]>, тут я не помощник.
И пишется что-то такое
Гарантирую, что если отключите индексы, данные влетят, даже чай не успеете заварить.
Для баз данных, которые поддерживают такие вставки, и того быстрее. Будут посланы параметризированные запросы и не надо беспокоиться о длине полей.
2) Так и делают. Тянут ORM на клиент, хотя в случае EF Core это толпа зависимостей. По этому часто используют Remote LINQ или что-то в этом роде.
О случае когда вам IQueryable не хватило можно поподробнее?
Возможно, но так как писано на коленке, тогда и не задумывался.