Подтвердить что ты не робот

Фильтр/поиск с использованием нескольких полей - ASP.NET MVC

Я использую ASP.NET MVC с EF 6.

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

На рисунке ниже у меня есть 3 варианта. Я могу фильтровать по каждому параметру, по одному за раз или по сочетанию двух или со всеми тремя.

Я подумывал написать запрос linq для каждого выбранного варианта. Но это будет невозможно, если опция фильтра увеличивается. Есть ли лучший способ этого.

Спасибо!

введите описание изображения здесь

Это то, что я сделал в моем контроллере. (в настоящее время выпадающий список имеет два параметра, за исключением: "- select one -" )

public ActionResult StockLevel(string option, string batch, string name)
{
    if (option != "0" && batch == "" && name == "")
    {
        if(option == "BelowMin")
        {
            List<Stock> stk = (from s in db.Stocks
                               where s.Qty < s.Item.AlertQty
                               select s).ToList();
            return View(stk);
        }
        else
        {
            List<Stock> stk = (from s in db.Stocks
                               where s.Qty == s.InitialQty
                               select s).ToList();
            return View(stk);
        }
    }
    if (option == "0" && batch != "" && name == "")
    {
        List<Stock> stk = (from s in db.Stocks
                           where s.BatchNo == batch
                           select s).ToList();
        return View(stk);
    }
    if (option == "0" && batch == "" && name != "")
    {
        List<Stock> stk = (from s in db.Stocks
                           where s.Item.Name.StartsWith(""+name+"")
                           select s).ToList();
        return View(stk);
    }
    return View(db.Stocks.ToList());
}
4b9b3361

Ответ 1

Я рекомендую вам разделить проблемы и использовать подход, чтобы код в вашем контроллере был таким, простым, красивым и расширяемым:

public ActionResult Index(ProductSearchModel searchModel)
{
    var business = new ProductBusinessLogic();
    var model = business.GetProducts(searchModel);
    return View(model);
}

Преимущества:

  • Вы можете положить все, что вам нужно, в ProductSearchModel в соответствии с вашими требованиями.
  • Вы можете написать любую логику в GetProducts на основе требований. Здесь нет ограничений.
  • Если вы добавите новое поле или параметр для поиска, ваши действия и контроллер останутся нетронутыми.
  • Если логика вашего поиска изменится, ваши действия и контроллер останутся нетронутыми.
  • Вы можете повторно использовать логику поиска везде, где вам нужно искать продукты, контроллеры или даже другую бизнес-логику.
  • Имея такой ProductSearchModel, вы можете использовать его в качестве модели частичного вида ProductSearch, и вы можете применить к нему DataAnnotations, чтобы улучшить валидацию модели и помочь пользовательскому интерфейсу визуализировать ее, используя Display или другие атрибуты.
  • В этот класс бизнес-логики вы можете добавить другую бизнес-логику, связанную с вашим продуктом.
  • Следуя этому пути, вы можете получить более организованное приложение.

Пример реализации:

Предположим, у вас есть класс Product:

public class Product
{
    public int Id { get; set; }
    public int Price { get; set; }
    public string Name { get; set; }
}

Вы можете создать класс ProductSearchModel и добавить в него несколько полей для поиска:

public class ProductSearchModel
{
    public int? Id { get; set; }
    public int? PriceFrom { get; set; }
    public int? PriceTo { get; set; }
    public string Name { get; set; }
}

Затем вы можете поместить свою логику поиска в класс ProductBusinessLogic следующим образом:

public class ProductBusinessLogic
{
    private YourDbContext Context;
    public ProductBusinessLogic()
    {
        Context = new YourDbContext();
    }

    public IQueryable<Product> GetProducts(ProductSearchModel searchModel)
    {
        var result = Context.Products.AsQueryable();
        if (searchModel != null)
        {
            if (searchModel.Id.HasValue)
                result = result.Where(x => x.Id == searchModel.Id);
            if (!string.IsNullOrEmpty(searchModel.Name))
                result = result.Where(x => x.Name.Contains(searchModel.Name));
            if (searchModel.PriceFrom.HasValue)
                result = result.Where(x => x.Price >= searchModel.PriceFrom);
            if (searchModel.PriceTo.HasValue)
                result = result.Where(x => x.Price <= searchModel.PriceTo);
        }
        return result;     
    }
}

Тогда в вашем ProductController вы можете использовать этот способ:

public ActionResult Index(ProductSearchModel searchModel)
{
    var business = new ProductBusinessLogic();
    var model = business.GetProducts(searchModel);
    return View(model);
}

Важное примечание:

В реальной реализации, пожалуйста, рассмотрите возможность применения подходящего шаблона Dispose для вашего бизнес-класса, чтобы при необходимости располагать контекст БД. Для получения дополнительной информации посмотрите Реализация метода Dispose или Шаблон Dispose.

Ответ 2

Условная фильтрация

.ToList(), .First(), .Count() и несколько других методов выполняют окончательный запрос LINQ. Но прежде чем он будет выполнен, вы можете применять фильтры так:

var stocks = context.Stocks.AsQueryable();
if (batchNumber != null) stocks = stocks.Where(s => s.Number = batchNumber);
if (name != null)        stocks = stocks.Where(s => s.Name.StartsWith(name));
var result = stocks.ToList(); // execute query

Где расширение LINQ

Простой WhereIf может значительно упростить код:

var result = db.Stocks
    .WhereIf(batchNumber != null, s => s.Number == batchNumber)
    .WhereIf(name != null,        s => s.Name.StartsWith(name))       
    .ToList();

Где реализация. Это простой метод расширения для IQueryable:

public static class CollectionExtensions
{
    public static IQueryable<TSource> WhereIf<TSource>(
        this IQueryable<TSource> source,
        bool condition,
        Func<TSource, bool> predicate)
    {
        if (condition)
            return source.Where(predicate).AsQueryable();
        else
            return source;
    }
}

Non-WhereIf путь LINQ (рекомендуется)

WhereIf предоставляет более декларативный способ, если вы не хотите использовать расширения, вы можете просто фильтровать вот так:

var result = context.Stocks
    .Where(batchNumber == null || stock.Number == batchNumber)
    .Where(name == null || s => s.Name.StartsWith(name))
    .ToList();

Он дает тот же эффект, что и WhereIf, и он будет работать быстрее, так как runtime необходимо будет создать только один ExpressionTree вместо создания нескольких деревьев и слияния их.

Ответ 3

Я написал несколько расширений, чтобы сделать это проще. https://www.nuget.org/packages/LinqConditionalExtensions/

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

var results = db.Stocks
                .If(option != "0", stocks => stocks
                    .IfChain(option == "BelowMin", optionStocks => optionStocks
                        .Where(stock => stock.Qty < stock.Item.AlertQty))
                    .Else(optionStocks => optionStocks
                        .Where(stock => stock.Qty == stock.InitialQty)))
                .WhereIf(!string.IsNullOrWhiteSpace(batch), stock => stock.BatchNo == batch)
                .WhereIf(!string.IsNullOrWhiteSpace(name), stock => stock.Item.Name.StartsWith("" + name + ""))
                .ToList();

return results;

По сути, начальный метод If() будет применять переданную цепочку if, если условие истинно. IfChain() - это ваш вложенный оператор if-else. IfChain() позволяет вам IfElse() несколько IfElse() и заканчиваться Else().

WhereIf() будет только условно применять ваше условие where, если условие истинно.

Если вы заинтересованы в библиотеке, https://github.com/xKloc/LinqConditionalExtensions содержит файл readme.

Ответ 4

public ActionResult Index(string searchid)
{ 
var personTables = db.PersonTables.Where(o => o.Name.StartsWith(searchid) )||  o.CombanyTable.ComName.StartsWith(searchid) ).Include(k => k.CombanyTable);
return View(personTables.ToList());
}