web-dev-qa-db-ja.com

複数のフィールドを使用したフィルター/検索-ASP.NET MVC

ASP.NET MVCEF 6を使用しています。

在庫アイテムに関するすべての情報を表示する在庫ページがあります。ここで、レコードもフィルタリングしたいと思います。

以下の図には、3つのオプションがあります。一度に1つずつ、または2つを組み合わせて、または3つすべてを組み合わせて、各オプションでフィルター処理する場合があります。

私は、選択されたすべてのオプションに対してlinqクエリを書くことを考えていました。ただし、フィルターオプションが増加すると、これは不可能になります。これに勝る方法はありますか。

ありがとう!

enter image description here

これは私がコントローラーでやったことです(現在、ドロップダウンには2つのオプションがありますが、以下を除きます: "-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());
}
33
Avi-B

懸念事項を分けて、コントローラーのコードが次のようなシンプルで美しい、拡張可能なアプローチを使用することをお勧めします。

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

利点:

  • 要件に基づいて、必要なものをProductSearchModelに入れることができます。
  • 要件に基づいて、GetProductsに任意のロジックを記述できます。制限はありません。
  • 検索する新しいフィールドまたはオプションを追加した場合、アクションとコントローラーはそのまま残ります。
  • 検索のロジックが変更されても、アクションとコントローラーは変更されません。
  • 製品、コントローラー、または他のビジネスロジックで検索する必要がある場所であれば、検索ロジックを再利用できます。
  • このようなProductSearchModelを使用すると、ProductSearch部分ビューのモデルとして使用でき、DataAnnotationsを適用してモデル検証を強化し、Displayを使用してレンダリングするUIを支援できますまたはその他の属性。
  • 製品に関連する他のビジネスロジックをそのビジネスロジッククラスに追加できます。
  • この方法に従えば、より整理されたアプリケーションを作成できます。

サンプル実装:

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パターンを実装して、必要に応じてdbコンテキストを破棄することを検討してください。詳細については、 Disposeメソッドの実装 または Disposeパターン をご覧ください。

74
Reza Aghaei

条件付きフィルタリング

.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

WhereIf LINQ拡張機能

単純なWhereIfは、コードを大幅に簡素化できます。

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

WhereIfの実装。 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とまったく同じ効果が得られ、複数のツリーを構築してマージするのではなく、ランタイムが1つのExpressionTreeを構築するだけで済むため、より高速に動作します。

14
Andrei

これを簡単にするために、いくつかの拡張機能を作成しました。 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()をチェーンし、Else()で終了できます。

WhereIf()は、条件が真の場合にwhere句を条件付きで適用します。

ライブラリに興味がある場合は、 https://github.com/xKloc/LinqConditionalExtensions にreadmeがあります。

1
Todd Skelton
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());
}
0
ana