@thekip
Php/C#/Js Developer

Linq to EF и фильтр на сайте — как построить запрос для необязательных параметров?

Пишу небольшое приложение и пытаюсь выполнить банальнейшую операцию, сделать выборку из БД с учетом фильтра, который задает пользователь.

Используется EF, запросы делаю с помощью LINQ.

От пользователя приходят данные для поиска: страна, список курортов и регионов. Каждый из этих параметров не обязателен.

Проблема в постройке LINQ запроса для необязательных параметров.

Класс со входящими параметрами:
public class FilterConditions
    {
        public int? country { get; set;}
        public int[] resorts { get; set; }
        public int[] regions { get; set; }
        public int[] hotels { get; set; }
    }


Экшен контроллера с самим запросом

public IHttpActionResult GetHotels([FromUri] FilterConditions conditions)
{
      var hotels = (from hotel in db.Hotels
             join spo in db.SpoData on hotel.Key equals spo.HotelKey
             where (conditions.country == null || conditions.country == spo.CountryKey) &&
                        (conditions.regions == null ||  conditions.regions.Contains((int)spo.RegionKey)) &&
                        conditions.resorts == null || conditions.resorts.Contains((int)spo.ResortKey))
             select new HotelDto { Name = hotel.Name, Id = hotel.Key, CountryKey = hotel.CountryKey });
           
       return Ok(hotels);
}


Проблема в том, что построитель LINQ запросов, заворачивает в базу данных все, что написано в запросе, независимо от того, надо оно или нет. Т.е. проверки conditions.country == null и conditions.regions == null оно тоже заворачивает в базе.

И если с первым случаем (conditions.country == null ) оно еще справляется, т.к. это обычный int, то в случае с conditions.resorts уже падает, так как это массив.

Понимаю, что задача тривиальная, но не могу придумать, как ее решить лаконично и красиво.
  • Вопрос задан
  • 2899 просмотров
Решения вопроса 1
trerums
@trerums
Делал недавно нечто подобное. Я думаю вам нужно составлять запрос на основе входных данных пользователя, а не просто вставлять что бы ни пришло в запрос. Вот например как делал я:

Модель, которая принимает пользовательский ввод:
public class PostFilterModel
    {
        public PostFilterModel()
        {
            Currency = Enumerable.Empty<Currency>();
            Condition = Enumerable.Empty<Condition>();
            Transmission = Enumerable.Empty<Transmission>();
            Rudder = Enumerable.Empty<Rudder>();
            Body = Enumerable.Empty<Body>();
            Engine = Enumerable.Empty<Engine>();
            Gear = Enumerable.Empty<Gear>();
        }

        public int City { get; set; }
        public int Region { get; set; }
        public int Brand { get; set; }
        public int Model { get; set; }
        public int MinHorsePower { get; set; }
        public int MaxHorsePower { get; set; }
        public int MinEngineCapacity { get; set; }
        public int MaxEngineCapacity { get; set; }
        public int MinMileage { get; set; }
        public int MaxMileage { get; set; }
        public int MinPrice { get; set; }
        public int MaxPrice { get; set; }
        public int MinYear { get; set; }
        public int MaxYear { get; set; }

        public IEnumerable<Currency> Currency { get; set; }
        public IEnumerable<Condition> Condition { get; set; }
        public IEnumerable<Transmission> Transmission { get; set; }
        public IEnumerable<Rudder> Rudder { get; set; }
        public IEnumerable<Body> Body { get; set; }
        public IEnumerable<Engine> Engine { get; set; }
        public IEnumerable<Gear> Gear { get; set; }
    }


А вот построение запроса к БД:
public ICollection<Post> GetByFilter(PostFilterModel filter)
        {
            IQueryable<Post> Posts = uow.PostRepository.GetAll();

            //
            // Обработка входных параметров, которые содержат только одно значение
            //
            if (filter.City > 0)
                Posts = Posts.Where(p => p.City.CityId == filter.City);

            if (filter.Region > 0)
                Posts = Posts.Where(p => p.City.Region.RegionId == filter.Region);

            if (filter.Brand > 0)
                Posts = Posts.Where(p => p.Car.Brand.BrandId == filter.Brand);

            if (filter.Model > 0)
                Posts = Posts.Where(p => p.Car.Model.ModelId == filter.Model);

            if (filter.MinPrice > 0)
                Posts = Posts.Where(p => p.Price >= filter.MinPrice);

            if (filter.MaxPrice > 0)
                Posts = Posts.Where(p => p.Price <= filter.MaxPrice);

            if (filter.MinYear > 0)
                Posts = Posts.Where(p => p.Car.Year >= filter.MinYear);

            if (filter.MaxYear > 0)
                Posts = Posts.Where(p => p.Car.Year <= filter.MaxYear);

            //
            // Обработка входных параметров, которые могут содержать множественные значения
            //
            if (filter.Condition.Count() > 0)
                Posts = Posts.Where(p => filter.Condition.Contains(p.Car.Condition));

            if (filter.Transmission.Count() > 0)
                Posts = Posts.Where(p => filter.Transmission.Contains(p.Car.Transmission));

            if (filter.Rudder.Count() > 0)
                Posts = Posts.Where(p => filter.Rudder.Contains(p.Car.Rudder));

            if (filter.Body.Count() > 0)
                Posts = Posts.Where(p => filter.Body.Contains(p.Car.Body));

            if (filter.Engine.Count() > 0)
                Posts = Posts.Where(p => filter.Engine.Contains(p.Car.Engine));

            if (filter.Gear.Count() > 0)
                Posts = Posts.Where(p => filter.Gear.Contains(p.Car.Gear));

            // Выполнение запроса
            return Posts.ToList();
        }


Надеюсь, вам это как-то поможет.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы