【问题标题】:Flter expanded table with breeze用微风过滤扩展表
【发布时间】:2014-12-31 19:03:11
【问题描述】:

我在我的项目中使用 angular、breeze、WebApi、EF、SqlServer2008。 我有表 Articles 和表 ArticleComments,所以一篇文章可以有很多 articleComments 记录。

public class Article{

    public int ArticleId { get; set; }
    public string ArticleName { get; set; }
    public string Description { get; set; }
    public ICollection<ArticleComment> Comments { get; set; }
    public ICollection<ArticleImage> ImagesList { get; set; }
    ...
}

public class ArticleComment
{
    public int ArticleCommentId { get; set; }
    public string CommentText { get; set; }
    public int UserId { get; set; }
    public int Rate { get; set; }
    public DateTime CommentDate { get; set; }
    public int ArticleId { get; set; }
    public Article Article { get; set; }
    public int Status { get; set; }
}

在客户端中,我需要获取带有 cmets、图像和其他链接实体的完整文章实体,但 cmets 需要仅用于选定的文章记录并且字段“status”== 1。

我尝试使用这样的查询

          var pred = breeze.Predicate.create('articleId', 'eq', id)
                  .and('comments', 'any', 'status', '==', 1);
          return EntityQuery.from("Articles")
            .where(pred)
            .expand('comments, imagesList...')
            .toType(entityNames.article)
            .using(manager).execute()
            .to$q(querySucceded, self._queryFailed);

这将返回 article 的所有 cmets,但不会按状态字段过滤展开的表 ArticleComments。

【问题讨论】:

    标签: javascript entity-framework breeze


    【解决方案1】:

    杰里米是对的。这是长期存在的 EF 疼痛。 OData 不支持过滤expand 的方法并没有使情况变得更好。

    您可以通过投影在服务器端做您想做的事。我在 DocCode 示例中使用了这个想法。

    我向NorthwindRepository 添加了一个新的CustomersAnd1998Orders 方法,该方法在NorthwindController 上向客户端公开

    这是一个完全可查询的端点。它返回客户以及他们在 1998 年订购的订单。奇怪的是它返回类型为 CustomerDto 而不是 Customer 的 JSON。您必须根据客户端查询进行调整……正如我将在下面解释的那样。

    我不知道为什么我必须这样做。如果我投影到Customer,我会从 EF 获得运行时异常。

    这是`NorthwindRepositoryCustomersAnd1998Orders:

    private class CustomerDto : Customer { } // EF requires a shadow class to make the LINQ query work
    public IQueryable<Customer> CustomersAnd1998Orders {
      get
      {
        return ForCurrentUser(Context.Customers)
        .Select(c => new CustomerDto {
          CustomerID = c.CustomerID,
          CompanyName =  c.CompanyName,
          ContactName =  c.ContactName,
          ContactTitle = c.ContactTitle,
          Address = c.Address,
          City = c.City,
          Region = c.Region,
          PostalCode = c.PostalCode,
          Country = c.Country,
          Phone =  c.Phone,
          Fax = c.Fax,
          RowVersion = c.RowVersion,
    
          Orders = c.Orders
                    .Where(o =>  o.OrderDate != null && o.OrderDate.Value.Year == 1998)
                    .ToList()
        });
      }
    }
    

    有点乏味,但如果你需要它,你需要它。

    “订单”上的过滤器选择 1998 年订购的订单(这是旧的 Northwind 数据)。 OrderDatenullable&lt;DateTime&gt;,所以我们必须检查 null,然后从 Value 中提取年份。

    请注意,它使用.Select 子句投射到CustomerDto 中,这是Customer 的私有“无操作”子类,所以我在技术上返回IQueryable&lt;CustomerDto&gt;。但它有效。

    NorthwindController.NorthwindRepositoryCustomersAnd1998Orders 没有出卖任何东西:

    [HttpGet]
    public IQueryable<Customer> CustomersAnd1998Orders() {
      return _repository.CustomersAnd1998Orders;
    }
    

    像查询“客户”一样在 Breeze 客户端上查询,但不指定 .expand 子句;服务器会处理的。

    有一个转折点。看看能不能在this test from DocCode检测到:

    var query = EntityQuery.from('CustomersAnd1998Orders')
        .where('CompanyName', 'startsWith', 'C')
        .orderBy("CompanyName")
        .toType('Customer'); // Essential ... unless fix with a JsonResultsAdapter
    
    verifyQuery(newEm, query,
        "Customers from 'CustomersAnd1998Orders' projection",
        showCompanyNamesAndOrderCounts,
        assertCustomersInCache,
        assertOrdersInCache,
        assertAllOrdersIn1998);
    

    是的。您必须将结果转换为Customer,因为线路上的类型是“CustomerDto”

    $type: "DocCode.DataAccess.NorthwindRepository+CustomerDto, DocCode.DataAccess.EF"
    

    但至少您可以在数据层上过滤 (.where()) 和排序 (orderBy()) 和页面 (take()skip())。

    在我看来,要为这个结果付出很小的代价?

    【讨论】:

      【解决方案2】:

      实体框架在使用“.Include(...)”时不支持过滤,这是在服务器上微风的“.expand(...)”翻译的内容。 herehere 发布了类似的问题。

      您当前的查询是:

      给我articleId==id的文章,只要它至少有一个 状态==1的评论。包括它的所有 cmets 和图像。

      相信你想表达的是:

      给我articleId==id的文章。包括 status==1 的 cmets 以及所有的图像。

      除非您创建类似于 Jay 描述的 here 的专用控制器操作,否则我不知道如何在一个查询中表达这一点。

      您也可以这样做:

      var pred = breeze.Predicate.create('articleId', 'eq', id);
      return EntityQuery.from("Articles")
          .where(pred)
          .expand('comments, imagesList')
          .toType(entityNames.article)
          .using(manager)
          .execute()
          .to$q(
              function(result) {
                  // detach all comments whose status!=1
                  var commentType = manager.metadataStore.getEntityType(entityNames.articleComment),
                      comments = manager.getEntities(commentType)
                           .filter(function(comment) { return comment.status !== 1; });
                  comments.forEach(manager.detachEntity);
                  querySucceded(result);
              },
              self._queryFailed);
      

      这种方法的好处是它不需要服务器端修改。缺点是它会加载比您需要的更多的 cmets,然后强制您在客户端上分离它们。

      第三种方法是发出两个查询:一个加载文章及其 imageList,第二个查询加载其 articleId==id 和 status==1 的 cmets。为此,您需要在控制器中执行一个返回IQueryable&lt;ArticleComment&gt; 的操作。这种方法的缺点是您需要发出两个 http 请求。您可以并行执行这些操作,并且随着 spdy 的出现,从长远来看,这可能不是什么大问题。

      【讨论】:

      • 感谢您的详细回答和建议。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 2013-03-26
      相关资源
      最近更新 更多