Breeze $过滤预测

时间:2013-07-26 12:12:40

标签: wcf-data-services odata breeze

我在尝试过滤数据时遇到问题,因为breeze在URL末尾添加了$ filter子句,而WCF \ odata服务抛出过滤器不能在select子句之后。

 public IQueryable<order> Orders()
{
    string owner= Membership.GetUser(Thread.CurrentPrincipal.Identity.Name).owner;

    IQueryable<Consigne> q = this.db.Consignes
        //                 .AddQueryOption("Dest", dest)
         .Where(x => x.Owner == owner)

         .Select(f => new order{ Name= f.Name, Address1 = f.Address1, Address2 = f.Address2, Address3 = f.Address3 });
    return q;
}

我已经使用服务器端Where子句限制结果集,并使用Select投影限制字段。如果我删除这些并让微风完全控制Where \ Select然后我吹我的安全模型,允许js代码控制。

我意识到这不是一个真正的微风问题而是更多的odata问题,但其他人如何处理这个? 你是放弃iQueryable而只是创建一个webapi并传回json? 如果是这样,那么我正在重新发明轮子,因为我还需要处理skip \ take和orderby。

欣赏建议:) 亲切的问候, 迈克


解决

我发现WCF无法在不丢失TotalCount的情况下作为iQueryable传递。 WCF返回一个QueryOperationResponse,我可以将其传递给breeze,但是一旦通过微风转换为对象,我就无法在Breeze的QueryHelper.WrapResults中找到将动态类型转换回可用对象以检索扩展的TotalCount属性。

QueryHelper将执行查询

queryResult = Enumerable.ToList((dynamic)queryResult)

request.Properties.TryGetValue("MS_InlineCount", out tmp) 
由于底层对象错误,

失败。

我的解决方案是在我的BreezeController中执行查询,并将行和TotalCount包装在一个数组中,就像Breeze一样。然后我可以将数组作为类型QueryResult传回,breeze将序列化为JSON到客户端。

  public QueryResult Consignees(string filter, int skip, int take)
  {
    WcfService.Context context = new WcfService.Context(new System.Uri(System.Configuration.ConfigurationManager.AppSettings["URI"]));

    //Main Table
    System.Data.Services.Client.DataServiceQuery<WcfService.Consigne> qMain = context.Consignes.IncludeTotalCount();

    //Projected Table
    System.Data.Services.Client.DataServiceQuery<Consigne> qProj = (System.Data.Services.Client.DataServiceQuery<Consigne>)qMain
          .Where(x => x.Refname.StartsWith(filter))
          .Skip(skip)
          .Take(take)
          .Select(f => new Consigne { Refname = f.Refname, Consignee = f.Consignee, Address1 = f.Address1, Address2 = f.Address2, Address3 = f.Address3 });

    System.Data.Services.Client.QueryOperationResponse<Consigne> qResult= qProj.Execute() as System.Data.Services.Client.QueryOperationResponse<Consigne>;

    QueryResult queryResult = new QueryResult() { Results = qResult.ToList(), InlineCount = qResult.TotalCount };
    return queryResult;
}

1 个答案:

答案 0 :(得分:5)

通过使用Breeze .withParameters({...})子句将过滤条件作为简单参数传递,您取得了一些成功。您仍然可以使用orderBytakeskip

Breeze,DTO和Web API

您需要使用WCF OData吗?你可以切换到更灵活的Web API吗?

假设我们在DocCode的Northwind世界中重新构想您的示例。为方便起见,您可以定义DTO类

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

严格来说,这不是必需的。但是你创建了这个类,所以你不必看到为你的投影生成的丑陋的匿名类型名称。

然后,您可以像{1}一样向NorthwindController添加查询方法:

[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
            });
}

当您的客户发出微风查询时,例如

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

它解析为以下网址

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

并返回四个{ProductID,ProductName}对象。

如果您在客户端元数据中描述ProductDto,则breeze会将这些对象视为实体并对其进行缓存。您将获得更改通知,更改跟踪,验证等。您可以将更改保存回服务器,在beforeSaveEntities方法中,您可以验证它们并将它们转换回Product实体,以便EF可以将它们保存到数据库中。我不会在这个答案中详述,但我希望你知道你可以做到。

限制过滤

请注意,只能按投影属性进行过滤,而不能按未公开的根类型中的属性进行过滤。例如,以下查询失败,因为Product.SupplierID不属于所选的ProductDto属性:

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

回复是

消息:“URI中指定的查询无效。”,
ExceptionMessage:“Type'Northwind.Models.ProductDto'没有属性'SupplierID'。”,

鉴于您对未公开属性的安全性担忧,我认为您希望此查询失败

但是,如果您确实需要按照某些不在投影类型中的条件进行过滤,则可以更改服务器方法以接受参数。例如:

[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
            });
}

现在客户可以写:

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

解析为

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

,结果是两个传递所有过滤条件的ProductDto个对象。

p.s。:我不是这样做的。我尝试了这段代码并完全按照这个答案中的描述得到了这些结果。