我在尝试过滤数据时遇到问题,因为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;
}
答案 0 :(得分:5)
通过使用Breeze .withParameters({...})
子句将过滤条件作为简单参数传递,您取得了一些成功。您仍然可以使用orderBy
,take
,skip
。
您需要使用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。:我不是这样做的。我尝试了这段代码并完全按照这个答案中的描述得到了这些结果。