ASP.NET MVCとEF 6を使用しています。
在庫アイテムに関するすべての情報を表示する在庫ページがあります。ここで、レコードもフィルタリングしたいと思います。
以下の図には、3つのオプションがあります。一度に1つずつ、または2つを組み合わせて、または3つすべてを組み合わせて、各オプションでフィルター処理する場合があります。
私は、選択されたすべてのオプションに対してlinqクエリを書くことを考えていました。ただし、フィルターオプションが増加すると、これは不可能になります。これに勝る方法はありますか。
ありがとう!
これは私がコントローラーでやったことです(現在、ドロップダウンには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());
}
懸念事項を分けて、コントローラーのコードが次のようなシンプルで美しい、拡張可能なアプローチを使用することをお勧めします。
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パターン をご覧ください。
.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
は、コードを大幅に簡素化できます。
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;
}
}
WhereIf
はより宣言的な方法を提供します。拡張機能を使用したくない場合は、次のようにフィルタリングできます。
var result = context.Stocks
.Where(batchNumber == null || stock.Number == batchNumber)
.Where(name == null || s => s.Name.StartsWith(name))
.ToList();
WhereIf
とまったく同じ効果が得られ、複数のツリーを構築してマージするのではなく、ランタイムが1つのExpressionTreeを構築するだけで済むため、より高速に動作します。
これを簡単にするために、いくつかの拡張機能を作成しました。 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があります。
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());
}