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

Версия 1.5 от Alexandr Fokin на 2026/04/02 19:19

 Возможная реализация идеи фиксации правил в виде отдельных компонентов.
Позволяет держать все правила в одном месте, гарантирует что изменение повлияет на все нужные места.
Использование 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);
    }
}