用微风吹过桌子

时间:2014-12-31 19:03:12

标签: javascript entity-framework breeze

我在我的项目中使用angular,breeze,WebApi,EF,SqlServer2008。 我有表文章和表格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; }
}

在客户端我需要获得带有评论,图片和其他链接实体的完整文章实体,但评论只需要选择的文章记录和字段&#34; status&#34; == 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);

这将返回文章的所有注释,但不会按状态字段过滤扩展的表ArticleComments。

2 个答案:

答案 0 :(得分:2)

杰里米是对的。这是一个长期存在的EF疼痛。由于OData不支持过滤expand的方法,因此没有做得更好。

您可以使用投影在服务器端执行您想要的操作。我在DocCode示例中使用了这个想法。

我向NorthwindRepository添加了一个新的CustomersAnd1998Orders方法,该方法在NorthwindController

上向客户端公开

这是一个完全可查询的端点。它返回的是客户以及1998年订购的订单。奇怪的是,它返回的JSON类型为CustomerDto而不是Customer。您必须针对那些使用客户端查询进行调整...我将在下面解释。

  

我不知道为什么要这样做。如果我投射到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()
    });
  }
}

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

&#39; Orders&#39;上的过滤器选择1998年订购的订单(这是旧的Northwind数据)。 OrderDatenullable<DateTime>,因此我们必须检查null,然后将年份从Value中拉出来。

请注意,它使用.Select子句投射到CustomerDto,私有,&#34;无所事事&#34; Customer的子类,所以我在技术上返回IQueryable<CustomerDto>。但它确实有效。

NorthwindController.NorthwindRepositoryCustomersAnd1998Orders没有任何背叛:

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

在Breeze客户端上查询,就像客户&#39;但没有指定.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,因为线上的类型是&#34; CustomerDto&#34;

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

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

对我来说这个结果要付出很小的代价?

答案 1 :(得分:1)

实体框架在使用&#34; .Include(...)&#34;时不支持过滤。这就是微风&#34; .expand(...)&#34;转换为服务器。类似问题已发布herehere

您当前的查询是:

  

只要至少有一篇文章,请给我带articleId == id的文章   评论状态== 1。包括所有评论和图片。

我相信你想要表达的是:

  

给我带articleId == id的文章。包括状态== 1的评论   以及它所有的图像。

我不知道在一个查询中表达这种情况的方法,除非您创建一个类似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);

这种方法的优点是它不需要服务器端修改。缺点是它会加载超出您需要的评论,并强迫您在之后将其分离到客户端。

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