Фиксирование условий в виде компонентов

Редактировал(а) Alexandr Fokin 2026/04/02 19:25

 Возможная реализация идеи фиксации правил в виде отдельных компонентов.
Позволяет держать все правила в одном месте, гарантирует что изменение повлияет на все нужные места.
Использование IQueryable т.к. он позволяет строить запросы (но важно учитывать конечный план запроса).
Также можно реализовать компоненты под другой формат взаимодействия (OneOf, TryGetIfNotNull) (например те, которые часто используются).
 IEnumerableIQueryable
Пример компонента для фиксации правила/условия.
InMemory conditionpublic interface IInMemoryCondition<TData, TParameters>
{
  /// <summary>
  /// Проверить условие.
  /// </summary>
  /// <param name="source"></param>
  /// <returns></returns>
  bool Check(TData source, TParameters parameters);

  /// <summary>
  /// Фильтрация для InMemory.
  /// </summary>
  /// <param name="source"></param>
  /// <returns></returns>
  IEnumerable<TData> ApplayEnumerable(IEnumerable<TData> source, TParameters parameters);
}
public class DelegateInMemoryCondition<TData, TParameters>
    : IInMemoryCondition<TData, TParameters>
{
  private readonly Func<TData, TParameters, bool> _checkFunc;
  private readonly Func<IEnumerable<TData>, TParameters, IEnumerable<TData>> _applayEnumerableFunc;        

  public DelegateInMemoryCondition(
        Func<TData, TParameters, bool> checkFunc,
        Func<IEnumerable<TData>, TParameters, IEnumerable<TData>>? applayEnumerableFunc = null)
    {
        _checkFunc = checkFunc;
        _applayEnumerableFunc = applayEnumerableFunc
            ?? ((source, parameter) => source.Where(e => Check(e, parameter)));            
    }

  public bool Check(TData source, TParameters parameters)
    {
      return _checkFunc(source, parameters);
    }

  public IEnumerable<TData> ApplayEnumerable(IEnumerable<TData> source, TParameters parameters)
    {
      return _applayEnumerableFunc(source, parameters);
    }        
}
IQueryable conditionpublic interface IQueryableCondition<TData, TParameters>
{
  /// <summary>
  /// Фильтрация для БД.
  /// Может использовать специфичные для БД функции.
  /// </summary>
  /// <param name="source"></param>
  /// <returns></returns>
  IQueryable<TData> ApplayQueryable(IQueryable<TData> source, TParameters parameters);
}
public class DelegateIQueryableCondition<TData, TParameters>
    : IQueryableCondition<TData, TParameters>
{
  private readonly Func<IQueryable<TData>, TParameters, IQueryable<TData>> _applayQueryableFunc;

  public DelegateIQueryableCondition(Func<IQueryable<TData>, TParameters, IQueryable<TData>> applayQueryableFunc)
    {
        _applayQueryableFunc = applayQueryableFunc;
    }

  public IQueryable<TData> ApplayQueryable(IQueryable<TData> source, TParameters parameters)
    {
      return _applayQueryableFunc(source, parameters);
    }
}
Контейнер для агрегата.// *) Можно использовать в виде композиции, статичного класса, или создания/внедрения отельных компонентов.
public interface TEntityConditions
{
public (
    IInMemoryCondition<TEntity, long> Memory,  
    IQueryableCondition<TEntity, long> Query
  ) ById { get; }

public (
    IInMemoryCondition<TEntity, string> Memory,  
    IQueryableCondition<TEntity, string> Query
  ) ByName { get; }

 public (
    IInMemoryCondition<TEntity, object?> Memory,  
    IQueryableCondition<TEntity, object?> Query
  ) IsRule1 { get; }
}
 
Вспомогательный Extension
public static class IConditionExtension
{
  public static IQueryable<TEntity> ApplayFilterCondition<TEntity, TParameter>(
      this IQueryable<TEntity> source,
        IQueryableCondition<TEntity, TParameter> condition,
        TParameter parameter)
    {
      return condition.ApplayQueryable(
            source,
            parameter);
    }

  public static IEnumerable<TEntity> ApplayFilterCondition<TEntity, TParameter>(
      this IEnumerable<TEntity> source,
        IInMemoryCondition<TEntity, TParameter> condition,
        TParameter parameter)
    {
      return condition.ApplayEnumerable(
            source,
            parameter);
    }
}