簡體   English   中英

在MVC應用程序的聯接查詢中使用Linq to SQL的動態where子句

[英]Dynamic where clause using Linq to SQL in a join query in a MVC application

我正在尋找一種方法,該方法使用基於產品所屬類別已分配給產品的屬性過濾器來查詢目錄中的產品。 因此,我涉及以下實體:

產品 -Id -CategoryId

類別 [Id,名稱,UrlName]

屬性 [Id,CategoryId,名稱,UrlName]

PropertyValues [Id,PropertyId,Text,UrlText]

ProductPropertyValues [ProductId,PropertyValueId]

當我將產品添加到目錄中時,將基於類別添加多個ProductPropertyValues,並且我希望能夠通過為一個或多個屬性選擇值來過濾類別中的所有產品。 業務邏輯,SQL索引和約束條件確保所有UrlName和文本對於值屬性和類別都是唯一的。

解決方案將是基於MVC3 EF代碼優先的應用程序,並且路由設置如下:

/products/{categoryUrlName}/{*filters}

過濾器路由部分的長度可變,因此可以應用多個過濾器。 每個過濾器都包含屬性的UrlName和值的UrlText,並用下划線分隔。

網址可能看起來像這樣/ products / websites / framework_mvc3 / language_csharp

通過閱讀URL,我將收集所有過濾器,並將其保存在列表中。 現在是時候實際獲得基於多種屬性的產品了,我一直在嘗試找到正確的策略。

也許還有另一種實現過濾器的方法。 所有較大的網上商店都使用基於類別的過濾器,我仍在尋找實現這種功能的持久性部分的最佳方法。 如果選擇了多個過濾器,建議的解決方案將導致結果集為“或”。 我可以想象將文本屬性添加到其中所有屬性值都存儲為連接字符串的產品表中也可以正常工作。 我不知道這會降低性能。 租用時不會有復雜的連接,並且屬性及其值將始終以文本形式接收。

也許過濾機制可以很好地在客戶端完成。

我想出了一個即使我也能理解的解決方案...通過使用“包含”方法,您可以隨意鏈接多個WHERE。 如果WHERE是空字符串,則將其忽略(或評估為全選)。 這是我在LINQ中聯接2個表,應用多個where子句並填充要返回到視圖的模型類的示例。

public ActionResult Index()
{
    string AssetGroupCode = "";
    string StatusCode = "";
    string SearchString = "";

    var mdl = from a in _db.Assets
              join t in _db.Tags on a.ASSETID equals t.ASSETID
              where a.ASSETGROUPCODE.Contains(AssetGroupCode)
              && a.STATUSCODE.Contains(StatusCode)
              && (
              a.PO.Contains(SearchString)
              || a.MODEL.Contains(SearchString)
              || a.USERNAME.Contains(SearchString)
              || a.LOCATION.Contains(SearchString)
              || t.TAGNUMBER.Contains(SearchString)
              || t.SERIALNUMBER.Contains(SearchString)
              )
              select new AssetListView
              {
                  AssetId = a.ASSETID,
                  TagId = t.TAGID,
                  PO = a.PO,
                  Model = a.MODEL,
                  UserName = a.USERNAME,
                  Location = a.LOCATION,
                  Tag = t.TAGNUMBER,
                  SerialNum = t.SERIALNUMBER
              };


    return View(mdl);
}

棘手的部分是將整個列表作為過濾器發送到數據庫。 您建立越來越多的where子句可以起作用的方法:

productsInCategory = ProductRepository
  .Where(p => p.Category.Name == category); 

foreach (PropertyFilter pf in filterList) 
{
     PropertyFilter localVariableCopy = pf;
     productsInCategory = from product in productsInCategory
       where product.ProductProperties
         .Any(pp => pp.PropertyValueId == localVariableCopy.ValueId)
       select product; 
}

另一種方法是使用List.Contains方法發送整個列表

List<int> valueIds = filterList.Select(pf => pf.ValueId).ToList();

productsInCategory = ProductRepository
  .Where(p => p.Category.Name == category)
  .Where(p => p.ProductProperties
    .Any(pp => valueIds.Contains(pp.PropertyValueId)
  );
IEnumerable<int> filters = filterList.Select(pf => pf.ValueId);

var products = from pp in ProductPropertyRepository
               where filters.Contains(pp.PropertyValueId) 
               && pp.Product.Category.Name == category
               select pp.Product;

請記住,由於使用了Contains ,過濾器將作為sproc參數傳入,這意味着您必須注意不要超過sproc參數限制。

我知道這是一個舊答案,但是如果有人看到了,我就建立了這個項目:

https://github.com/PoweredSoft/DynamicLinq

哪些也應該可以在nuget上下載:

https://www.nuget.org/packages/PoweredSoft.DynamicLinq

您可以使用它來循環遍歷來自查詢字符串的過濾器,並在

query = query.Query(q =>
{
    q.Compare("AuthorId", ConditionOperators.Equal, 1);
    q.And(sq =>
    {
        sq.Compare("Content", ConditionOperators.Equal, "World");
        sq.Or("Title", ConditionOperators.Contains, 3);
    });
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM