Breeze $filter projections

老子叫甜甜 提交于 2019-12-06 16:06:11

You have some success by passing your filter criteria as simple parameters using the Breeze .withParameters({...}) clause. You can still use orderBy, take, skip.

Breeze, DTOs and the Web API

Do you need to use WCF OData? Could you switch to Web API where you have more flexibility?

Suppose we reimagine your example in the Northwind world of DocCode. For convenience, you define a DTO class

public class ProductDto
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
}

This is not needed, strictly speaking. But you created this class so you don't ever have to see the ugly anonymous-type-name that is generated for your projection.

Then you add a query method to the NorthwindController like this one:

[HttpGet]
public IQueryable ProductDtos()
{
    return _repository.Products
        // TODO: move the following into the repository where it belongs
        .Where(x => x.CategoryID == 1) // a surrogate for your 'Owner' filter
        .Select(x => new ProductDto
            {
                ProductID = x.ProductID, 
                ProductName = x.ProductName
            });
}

When your client issues a breeze query such as

var q = breeze.EntityQuery.from('ProductDtos')
      .where('ProductName', 'startsWith', 'C')
      .orderBy('ProductName')
      .take(5);
// execute it

it resolves to the following URL

http://localhost:47595/breeze/Northwind/ProductDtos?$filter=startswith(ProductName,'C') eq true&$orderby=ProductName&$top=5

and returns four {ProductID, ProductName} objects.

If you describe ProductDto in client-side metadata, breeze will treat these objects as entities and cache them. You'll get change notification, change tracking, validation, etc. You can save the changes back to the server where, in the beforeSaveEntities method, you can validate them and convert them back into Product entities so that EF can save them to the database. I'm not going into details in this answer but I want you to know that you can do it.

Restricted filtering

Note that you can only filter by the projected properties, not by properties in the undisclosed root type. For example, the following query fails because Product.SupplierID is not among the selected ProductDto properties:

http://localhost:47595/breeze/Northwind/ProductDtos?$filter=SupplierID eq 18&$orderby=ProductName&$top=5

The response is

Message: "The query specified in the URI is not valid.",
ExceptionMessage: "Type 'Northwind.Models.ProductDto' does not have a property 'SupplierID'.",

Given your security concerns about undisclosed properties, I assume you would want this query to fail.

But if you actually need to filter by some criteria that are not in the projected type, you could change the server method to accept a parameter. For example:

[HttpGet]
public IQueryable ProductDtos(int? supplierID=null)
{
     // TODO: move the following into the repository where it belongs
    var query = _repository.Products
        .Where(x => x.CategoryID == 1); // a surrogate for a security filter

    if (supplierID != null)
    {
        query = query.Where(x => x.SupplierID == supplierID);
    }

    return query.Select(x => new ProductDto
            {
                ProductID = x.ProductID, 
                ProductName = x.ProductName
            });
}

Now the client can write:

var q = breeze.EntityQuery.from('ProductDtos')
      .withParameters( {supplierID: 18} ) // <-- passed as arg to the GET method
      .where('ProductName', 'startsWith', 'C')
      .orderBy('ProductName')
      .take(5);
// execute it

which resolves to

http://localhost:47595/breeze/Northwind/ProductDtos?$filter=startswith(ProductName,'C') eq true&$orderby=ProductName&$top=5&supplierID=18

and the result is two ProductDto objects that pass all filter criteria.

p.s.: I'm not making this up. I tried this code and got these results exactly as described in this answer.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!