简体   繁体   English

在MVC应用程序的联接查询中使用Linq to SQL的动态where子句

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

I am looking for a way to query for products in a catalog using filters on properties which have been assigned to the product based on the category to which the product belongs. 我正在寻找一种方法,该方法使用基于产品所属类别已分配给产品的属性过滤器来查询目录中的产品。 So I have the following entities involved: 因此,我涉及以下实体:

Products -Id -CategoryId 产品 -Id -CategoryId

Categories [Id, Name, UrlName] 类别 [Id,名称,UrlName]

Properties [Id, CategoryId, Name, UrlName] 属性 [Id,CategoryId,名称,UrlName]

PropertyValues [Id, PropertyId, Text, UrlText] PropertyValues [Id,PropertyId,Text,UrlText]

ProductPropertyValues [ProductId, PropertyValueId] ProductPropertyValues [ProductId,PropertyValueId]

When I add a product to the catalog, multiple ProductPropertyValues will be added based on the category and I would like to be able to filter all products from a category by selecting values for one or more properties. 当我将产品添加到目录中时,将基于类别添加多个ProductPropertyValues,并且我希望能够通过为一个或多个属性选择值来过滤类别中的所有产品。 The business logic and SQL indexes and constraints make sure that all UrlNames and texts are unique for values properties and categories. 业务逻辑,SQL索引和约束条件确保所有UrlName和文本对于值属性和类别都是唯一的。

The solution will be a MVC3 EF code first based application and the routing is setup as followed: 解决方案将是基于MVC3 EF代码优先的应用程序,并且路由设置如下:

/products/{categoryUrlName}/{*filters}

The filter routing part has a variable length so multiple filters can be applied. 过滤器路由部分的长度可变,因此可以应用多个过滤器。 Each filter contains the UrlName of the property and the UrlText of the value separated by an underscore. 每个过滤器都包含属性的UrlName和值的UrlText,并用下划线分隔。

An url could look like this /products/websites/framework_mvc3/language_csharp 网址可能看起来像这样/ products / websites / framework_mvc3 / language_csharp

I will gather all filters, which I will hold in a list, by reading the URL. 通过阅读URL,我将收集所有过滤器,并将其保存在列表中。 Now it is time to actually get the products based on multiple properties and I have been trying to find the right strategy. 现在是时候实际获得基于多种属性的产品了,我一直在尝试找到正确的策略。

Maybe there is another way to implement the filters. 也许还有另一种实现过滤器的方法。 All larger web shops use category depending filters and I am still looking for the best way to implement the persistence part for this type of functionality. 所有较大的网上商店都使用基于类别的过滤器,我仍在寻找实现这种功能的持久性部分的最佳方法。 The suggested solutions result in an "or" resultset if multiple filters are selected. 如果选择了多个过滤器,建议的解决方案将导致结果集为“或”。 I can imagine that adding a text property to the product table in which all property values are stores as a joined string can work as well. 我可以想象将文本属性添加到其中所有属性值都存储为连接字符串的产品表中也可以正常工作。 I have no idea what this would cost performance wise. 我不知道这会降低性能。 At leased there will be no complex join and the properties and their values will be received as text anyway. 租用时不会有复杂的连接,并且属性及其值将始终以文本形式接收。

Maybe the filtering mechanism can be done client side ass well. 也许过滤机制可以很好地在客户端完成。

I came up with a solution that even I can understand... by using the 'Contains' method you can chain as many WHERE's as you like. 我想出了一个即使我也能理解的解决方案...通过使用“包含”方法,您可以随意链接多个WHERE。 If the WHERE is an empty string, it's ignored (or evaluated as a select all). 如果WHERE是空字符串,则将其忽略(或评估为全选)。 Here is my example of joining 2 tables in LINQ, applying multiple where clauses and populating a model class to be returned to the view. 这是我在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);
}

The tricky part about this is sending the whole list into the database as a filter. 棘手的部分是将整个列表作为过滤器发送到数据库。 Your approach of building up more and more where clauses can work: 您建立越来越多的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; 
}

Another way to go is to send the whole list in using the List.Contains method 另一种方法是使用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;

Bear in mind that as Contains is used, the filters will be passed in as sproc parameters, this means that you have to be careful not to exceed the sproc parameter limit. 请记住,由于使用了Contains ,过滤器将作为sproc参数传入,这意味着您必须注意不要超过sproc参数限制。

I know this an old answer but if someone see's this I've built this project: 我知道这是一个旧答案,但是如果有人看到了,我就建立了这个项目:

https://github.com/PoweredSoft/DynamicLinq https://github.com/PoweredSoft/DynamicLinq

Which should be downloadable on nuget as well: 哪些也应该可以在nuget上下载:

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

You could use this to loop through your filter coming from query string and do something in the lines of 您可以使用它来循环遍历来自查询字符串的过滤器,并在

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