1

我在尝试过滤数据时遇到问题,因为微风在 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 的 QueryHelper.WrapResults 中找到将动态类型投射回一个可用对象以检索扩展的 TotalCount 属性.

QueryHelper 将执行查询

queryResult = Enumerable.ToList((dynamic)queryResult)

request.Properties.TryGetValue("MS_InlineCount", out tmp) 

由于错误的基础对象而失败。

我的解决方案是在我的 BreezeController 中执行查询,并将行和 TotalCount 包装在一个数组中,就像 Breeze 一样。然后我可以将数组作为 QueryResult 类型传回,然后微风将序列化为 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;
}
4

1 回答 1

5

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

Breeze、DTO 和 Web API

您需要使用 WCF OData 吗?您能否切换到具有更大灵活性的 Web API?

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

公共类 ProductDto
{
    公共 int ProductID { 获取;放; }
    公共字符串产品名称 { 获取;放; }
}

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

然后你添加一个像这样的查询方法NorthwindController

[HttpGet]
公共 IQueryable ProductDtos()
{
    返回 _repository.Products
        // TODO: 将以下内容移动到它所属的存储库中
        .Where(x => x.CategoryID == 1) // 'Owner' 过滤器的代理
        .Select(x => new ProductDto
            {
                ProductID = x.ProductID,
                产品名称 = x.产品名称
            });
}

当您的客户发出轻而易举的查询时,例如

var q = 微风.EntityQuery.from('ProductDtos')
      .where('ProductName', 'startsWith', 'C')
      .orderBy('产品名称')
      .take(5);
// 执行它

它解析为以下 URL

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

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

如果您ProductDto在客户端元数据中进行描述,那么微风会将这些对象视为实体并缓存它们。您将收到更改通知、更改跟踪、验证等。您可以将更改保存回服务器,在该beforeSaveEntities方法中,您可以验证它们并将它们转换回Product实体,以便 EF 可以将它们保存到数据库中。我不会在这个答案中详细介绍,但我想让你知道你可以做到。

限制过滤

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

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

回应是

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

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

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

[HttpGet]
公共 IQueryable ProductDtos(int?supplierID=null)
{
     // TODO: 将以下内容移动到它所属的存储库中
    var query = _repository.Products
        .Where(x => x.CategoryID == 1); // 安全过滤器的代理

    如果(供应商 ID != null)
    {
        查询 = query.Where(x => x.SupplierID == 供应商ID);
    }

    return query.Select(x => new ProductDto
            {
                ProductID = x.ProductID,
                产品名称 = x.产品名称
            });
}

现在客户端可以写:

var q = 微风.EntityQuery.from('ProductDtos')
      .withParameters( {supplierID: 18} ) // <-- 作为参数传递给 GET 方法
      .where('ProductName', 'startsWith', 'C')
      .orderBy('产品名称')
      .take(5);
// 执行它

解决为

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

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

ps:这不是我编的。我尝试了这段代码,并得到了与此答案中描述的完全相同的结果。

于 2013-07-28T07:14:26.550 回答