Linq Expression управляет приоритетом между AndAlso и OrElse - PullRequest
0 голосов
/ 05 декабря 2018

У меня есть динамическая система фильтрации в моем уровне данных для создания выражения для получения данных.

У меня есть объект запроса, который содержит мою модель фильтрации, то есть:

public class BookingPagerQuery : PagerQuery
{
    public bool? IsAssignedToPlanningSlot { get; set; }
    public IEnumerable<Guid> PlanningSlotIds { get; set; }
    public bool MainInterventionOnly { get; set; }
    public bool MainOperatorOnly { get; set; }
}

и внутри методов получения я выполняю процесс фильтрации:

           // For all, not deleted and only current establissment
            Expression<Func<BookingOperator, bool>> where = a => !a.Deleted && a.EstablishmentId == establishmentId;

            // filtering by role
            if (userIdentity.Roles.HasAnyFlag(Roles.Admin))
            {
                // Admin : no filter
            }
            else if (userIdentity.Roles.HasAnyFlag(BookingRoles.ProcessSentBookings))
            {
                // booking process granted = no draft from others but his draft
                where = where.Combine(c => !new List<BookingState> { BookingState.Draft }.Contains(c.BookingState));                    
                where = where.CombineOrElse(c => c.CreatedBy == userIdentity.Id);
            }
        else if (userIdentity.Roles.HasAnyFlag(Roles.Secretary))
        {
            // 1. All self created
            // 2. All from his office or service                     
            // 3. with or without operator

            Secretary secretary = Context.Secretaries.First(x => x.Id == userIdentity.Id);
            // 1. Tous ceux créé par elles même
            where = where.Combine(a => a.CreatedBy == userIdentity.Id);

            // 2. pour son office ou partnerEst.      
            if (secretary.OfficeId != null)
            {
                // Les chirurgiens de son office
                IEnumerable<Guid> privateSurgeons = Context.PrivateSurgeons.Where(x => x.OfficeId == secretary.OfficeId).Select(x => x.Id);
                where = where.CombineOrElse(x => x.OperatorId.HasValue && privateSurgeons.Contains(x.OperatorId.Value));
            }

            if (secretary.ServiceId != null)
            {
                // Les chirurgiens de son service
                IEnumerable<Guid> serviceMembers = Context.ServiceMembers.Where(x => x.ServiceId == secretary.ServiceId).Select(x => x.Id);
                where = where.CombineOrElse(x => x.OperatorId.HasValue && serviceMembers.Contains(x.OperatorId.Value));
            }
        }
            else if (userIdentity.Roles.HasAnyFlag(Roles.Surgeon))
        {
            // All self created                    
            where = where.Combine(a => a.CreatedBy == userIdentity.Id);
            // All given by the secretary
            where = where.CombineOrElse(a => a.OperatorId == userIdentity.Id);
        }
            if (query.MainInterventionOnly)
            {
                where = where.Combine(a => a.IsMainIntervention == true);
            }

            if (query.MainOperatorOnly)
            {
                where = where.Combine(x => !x.OperatorType.HasValue || x.OperatorType == OperatorType.Operator1);
            }

Здесь код Combine и CombineOrElse:

    public static Expression<Func<T, bool>> Combine<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        ParameterExpression param = Expression.Parameter(typeof(T), "param");
        Expression newFirst = new ReplaceVisitor(first.Parameters.First(), param).Visit(first.Body);
        Expression newSecond = new ReplaceVisitor(second.Parameters.First(), param).Visit(second.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(newFirst, newSecond), param);
    }
    public static Expression<Func<T, bool>> CombineOrElse<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        ParameterExpression param = Expression.Parameter(typeof(T), "param");
        Expression newFirst = new ReplaceVisitor(first.Parameters.First(), param).Visit(first.Body);
        Expression newSecond = new ReplaceVisitor(second.Parameters.First(), param).Visit(second.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(newFirst, newSecond), param);
    }

Я могу переписать код, чтобы удалить все CombineElse и включите это в Combine, но система будет расти и фильтр будет более гранулированным, поэтому мне нужно создать Expression в виде CombineIncludeOrElse:

Comine: A && B

CombineOrElse: A ||B

CombineIncludeOrElse: A && (B || C)

Я переименовываю слишком Combine и CombineOrElse с помощью AndAlso и OrElse

Итак, как в Expression я могу получить B безА и вводить OrElse на C?

...