powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Собрать конвейер
20 сообщений из 20, страница 1 из 1
Собрать конвейер
    #39691203
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Есть несколько делегатов. Нужно собрать из них один, причем по условиям.

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
// на входе есть:
Func<IQueryable<MyClass>, IQueryable<MyClass>> func1
Func<IQueryable<MyClass>, IQueryable<MyClass>> func2
Func<IQueryable<MyClass>, IQueryable<MyClass>> func3
Func<IQueryable<MyClass>, IQueryable<MyClass>> func4

// нужно их собрать в один
Func<IQueryable<MyClass>, IQueryable<MyClass>> funcResult;

// причем по условию. как-то так:
funcResult = func1;
if (...)
  funcResult = n => func2(funcResult(n));
if (...)
  funcResult = n => func3(funcResult(n));
if (...)
  funcResult = n => func4(funcResult(n));



Этот код приведет к Stack Overflow, а как по-другому собрать, что-то не могу сообразить, хотя, кажется, лежит на поверхности.
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691251
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,

похоже на бесконечную рекурсию. в твоём же коде ошибки нет.
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691253
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ошибки компиляции нет. Будет рекурсия из-за конвейера, потому что я присваиваю вызов одной и той же переменной.
А мне надо собрать несколько функций друг за другом, но не все, а некоторые по условию
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691263
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,

да, не заметил n =>

зачем этот JavaScript-style?

у тебя уже функция выполняет сборку и выполнение, зачем собирать в делегат?
может хочешь expression tree? )
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691273
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну получилось так
на входе в функцию есть делегат
в процессе обработки добавляются (или не добавляются) еще несколько
после чего вызывается еще одна функция, в которую этот результирующий делегат передается и которая в конце-концов применяется для запроса к БД.

Впрочем, ты прав, выражение ж нужно )
Впрочем, вопрос-то все равно останется тем же...
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691281
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,

для построения новых функций тебе нужны деревья выражений
ну или emit ))

если хочешь комбинировать, тебе нужны спецификации
шаблон chain-of-responsibility

а ты пытаешься делать как в JavaScript, да и там замыкание функции тоже приведёт к рекурсии

если тебе просто надо исправить этот код, то так:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
// на входе есть:
Func<IQueryable<MyClass>, IQueryable<MyClass>> func1
Func<IQueryable<MyClass>, IQueryable<MyClass>> func2
Func<IQueryable<MyClass>, IQueryable<MyClass>> func3
Func<IQueryable<MyClass>, IQueryable<MyClass>> func4

// нужно их собрать в один
Func<IQueryable<MyClass>, IQueryable<MyClass>> funcResult;

// причем по условию. как-то так:
funcResult = func1;
if (...)
{
  var f = funcResult;
  funcResult = n => func2(f(n));
}
if (...)
{
  var f = funcResult;
  funcResult = n => func3(f(n));
}
if (...)
{
  var f = funcResult;
  funcResult = n => func4(f(n));
}



но это не выглядит как нормальное решение )
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691293
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Согласен, не выглядит.
Я попытался реализовать что-то типа того:
Код: c#
1.
2.
3.
4.
5.
6.
var query = db.Set<MyClass>();
if (...)
  query = query.Where(n => .....);
if (...)
  query = query.OrderBy(n => .....);
var res = query.ToList();



Только в данном случае функционал оказался разнесен по методам, и вместо конкретного делегата, у меня появился асбстрактный. То есть, конечно, тут вообще должно быть выражение, а не делегат.
Завтра на свежую голову еще раз попробую уже с выражением.
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691302
ViPRos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,

просто разные уровни - метаданные смещены с конечным кодом
надо сгенерировать код и вызвать
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691323
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,

Выражения ты можешь строить благодаря тому, что IQueryable можно достраивать и прогонять через спецификации.

Но ты почему-то собираешь делегаты. Это очень странно )))
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691424
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttВыражения ты можешь строить благодаря тому, что IQueryable можно достраивать и прогонять через спецификации.

Но ты почему-то собираешь делегаты. Это очень странно )))Я может немного переусложнил архитектуру. Но раз вопрос поднят, хочу его добить.
Делегат взялся как раз из того, что мне нужно передавать запрос в "достраиватель" в виде коллбека.
Попробую на примере:
В самом низу стека вызова есть метод, который строит начало запроса, потом выполняет коллбэк для достройки запроса, а потом выполняет его, то есть примерно так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
		private async Task<List<MyClass>> GetMyClasses(Func<IQueryable<MyClass>, IQueryable<MyClass>> extendQueryCallback)
		{
			// формируем первоначальный запрос
			IQueryable<MyClass> query = _db.Set<MyClass>()
				.Include(n => ...)
				.Where(m => ...);
			// достраиваем его, если этого хочет вышестоящий вызов
			if (extendQueryCallback != null)
				query = extendQueryCallback(query);
			// материализуем
			return await workingQuery.ToListAsync().ConfigureAwait(false);
		}



А вот вышестоящий вызов как раз формирует этот "достраиватель" из нескольких условий, плюс у него на входе тоже есть аналогичный достраиватель в виде входного параметра, который тоже надо учесть.

Но я, кажется понял, в вызывающем методе нужно сделать просто один большой делегат-достраиватель, а не пытаться накопить его в переменной

В общем, перемудрил на несвежую голову, иногда нужно просто с тобой поболтать, чтобы понять самого себя
Спасибо


ЗЫ:hVosttпрогонять через спецификации что ты имел ввиду?
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691430
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.ProЭтот код приведет к Stack Overflow, а как по-другому собрать, что-то не могу сообразить, хотя, кажется, лежит на поверхности.
Ну еще бы. Решарпер такой код сразу подчеркивает с предупреждением Access to modified closure для funcResult внутри func2, func3, func4. Причина очень проста: на каждом шаге комбинирования тот funcResult, который скармливается функции конвейера, и сам funcResult - одно и то же, т.к. выполнение происходит после присваивания. Отсюда и SOF. Стандартный выход - копировать замыкаемую переменную в отдельную локальную. Вырожденный пример, когда комбинируем всё:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
var sequence = Enumerable.Range(0, 9).Select(n => n.ToString(CultureInfo.InvariantCulture)).AsQueryable();
Func<IQueryable<string>, IQueryable<string>> func1 = seq => seq.Select(s => $"{s}a").AsQueryable();
Func<IQueryable<string>, IQueryable<string>> func2 = seq => seq.Select(s => $"{s}b").AsQueryable();
Func<IQueryable<string>, IQueryable<string>> func3 = seq => seq.Select(s => $"{s}c").AsQueryable();
Func<IQueryable<string>, IQueryable<string>> func4 = seq => seq.Select(s => $"{s}d").AsQueryable();
var all = func1;
var all1 = all;
all = n => func2(all1(n));
var all2 = all;
all = n => func3(all2(n));
var all3 = all;
all = n => func4(all3(n));
var result = all(sequence);
foreach(var s in result)
  Console.WriteLine(s);


Вывод:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
0abcd
1abcd
2abcd
3abcd
4abcd
5abcd
6abcd
7abcd
8abcd
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691431
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не, ну причины-то понятны, я написал этот код как пример.
Надо было просто сделать так:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Func<IQueryable<MyClass>, IQueryable<MyClass>> funcResult = n =>
{
if (...)
  n = func1(n);
if (...)
  n = func2(n);
if (...)
  n = func3(n);
if (...)
  n = func4(n);
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691439
Фотография skyANA
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.ProhVosttпрогонять через спецификации что ты имел ввиду?
Походу шаблон Спецификация и в частности Composite Specification.
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691440
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
skyANAПоходу шаблон Спецификация и в частности Composite Specification.А, ну это оверкодинг для моей задачки. Я просто разнес несколько приватных методов, чтобы избежать повторения кода.
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691600
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.ProskyANAПоходу шаблон Спецификация и в частности Composite Specification.А, ну это оверкодинг для моей задачки. Я просто разнес несколько приватных методов, чтобы избежать повторения кода.

Ну собственно ты и начал делать эти спецификации, только почему-то не захотел из грамотно оформить )
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691628
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttНу собственно ты и начал делать эти спецификации, только почему-то не захотел из грамотно оформить )Эти промежуточные методы помимо операций над запросом выполняют и другую логику, для нормальной реализации паттерна, пришлось бы создавать веер классов, а все на самом деле происходит внутри всего лишь одного класса в приватных методах.
Но на будущее посмотрю в сторону использования паттерна в подобных случаях ))
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691675
Siemargl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691679
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Proпришлось бы создавать веер классов

Веер классов может содержать данные, которые применяют или нет спецификацию.

Допустим, самый распространённый случай:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
public class IsDeletedSpecification<T> : Specification<T>
{
   private bool _isDeleted;

   public IsDeletedSpecification(bool isDeleted = false) { _isDeleted = isDeleted; }

   public override IQueryable<T> Specify(IQueryable<T> query)
   {
      return query.Where(p => p.IsDeleted == _isDeleted);
   }
   
   ....


    var mySpec = Specification.And(spec1, spec2, spec3);
}



Это идеологический пример. На деле есть готовые решения, которые берёшь и используешь :)

Чем лучше? Все спецификации можно тестировать. Их изменение и сопровождение не ломает ничего, они могут лежать в разных сборках. Они могут комбинироваться сколько угодно благодаря общему интерфейсу.

И т.д. и т.п., этот на первый взгляд оверхед, очень даже оправдан по всем фронтам.
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691681
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Собрать конвейер
    #39691682
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
Код: c#
1.
var mySpec = Specification.And(spec1, spec2, spec3);



это не внутри класса IsDeletedSpecification, это просто отдельный пример )
...
Рейтинг: 0 / 0
20 сообщений из 20, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Собрать конвейер
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]