OData IQueryable查询自动添加多余括号问题及解决咨询
我正在查询Copernicus的OData产品列表服务,实体请求Uri为https://scihub.copernicus.eu/dhus/odata/v1/Products,Products是一个IQueryable<Product>。
我用Linq查询指定日期之后的数据,代码如下:
var uri = new Uri("https://scihub.copernicus.eu/dhus/odata/v1/"); // DHuSData类由OData $metadata自动生成 var odata = new DHuSData(uri); odata.Credentials = new NetworkCredential("MyUserName", "MyPassword"); var query = odata.Products.Where(f => f.ContentDate.End > datetime).ToList();
但查询无法正常工作,因为生成的请求Uri在?$filter前多了一对括号:
https://scihub.copernicus.eu/dhus/odata/v1/Products()?$filter=ContentDate/End gt datetime'2023-01-01T17%3A49%3A29.9719648-07%3A00'
请问Linq查询为何会添加这些括号,以及如何移除它们?
补充信息
我通过自定义DataServiceContext复现了该问题:
public class TestOData { private void CreateOdata() { var odata = new MyOdata(new Uri("https://myodata.com/dhus/odata/v1/")); var datetime = DateTime.Now.Subtract(TimeSpan.FromDays(15)); IQueryable<MyProduct> query = odata.MyProducts.Where(f => f.ContentDate.End > datetime); DataServiceQuery serviceQuery = (DataServiceQuery)query; Console.WriteLine(serviceQuery.RequestUri.AbsoluteUri); } } public class MyProduct { public TimeRange ContentDate { get; set; } } public class MyOdata : System.Data.Services.Client.DataServiceContext { public MyOdata(System.Uri serviceRoot) : base(serviceRoot) { } private System.Data.Services.Client.DataServiceQuery<MyProduct> _MyProducts; public System.Data.Services.Client.DataServiceQuery<MyProduct> MyProducts { get { if ((this._MyProducts == null)) { this._MyProducts = base.CreateQuery<MyProduct>("MyProducts"); } return this._MyProducts; } } }
我也尝试过使用AddQueryOption方法,但问题依旧,看来这可能和Linq无关:
var formattedDate = datetime.ToString("yyyy-MM-ddTHH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); formattedDate = WebUtility.UrlEncode(formattedDate); DataServiceQuery<MyProduct> selectedOrders = odata.MyProducts .AddQueryOption("$filter", string.Format("ContentDate/End gt '{0}'", formattedDate));
括号出现的原因
这些括号是WCF Data Services Client(也就是你使用的DataServiceContext相关类)生成的——当客户端库判断目标实体集是函数导入(Function Import)而非普通实体集时,会自动添加()。
这个问题通常出现在两种场景:一是自动生成的客户端代码误将实体集识别为函数导入;二是Copernicus的DHuS服务OData实现存在细微的非标准特性,导致客户端库判断出错。
解决方法
方法1:修改自动生成的客户端代码
如果DHuSData是通过OData元数据自动生成的,找到Products属性的实现代码,把创建DataServiceQuery时的实体集名称里的括号去掉:
// 自动生成的原始代码可能类似这样: public DataServiceQuery<Product> Products { get { if (_Products == null) { _Products = base.CreateQuery<Product>("Products()"); // 这里带了括号 } return _Products; } } // 修改为: public DataServiceQuery<Product> Products { get { if (_Products == null) { _Products = base.CreateQuery<Product>("Products"); // 移除括号 } return _Products; } }
方法2:手动构造请求Uri绕过自动生成逻辑
如果修改自动生成代码不方便,可以直接构造正确的请求Uri,再通过DataServiceContext执行查询:
// 格式化日期为OData要求的格式 var formattedDatetime = datetime.ToString("yyyy-MM-ddTHH:mm:sszzz"); // 构造filter语句 var filterClause = $"ContentDate/End gt datetime'{formattedDatetime}'"; // 拼接完整请求Uri var requestUri = new Uri($"{uri.AbsoluteUri}Products?$filter={WebUtility.UrlEncode(filterClause)}"); // 执行查询 var result = odata.Execute<Product>(requestUri).ToList();
方法3:升级OData客户端库
旧版本的System.Data.Services.Client库存在实体集识别错误的问题,建议替换为最新版的Microsoft.OData.Client NuGet包——新版本对实体集和函数导入的判断逻辑更准确,大概率能直接避免括号问题。
内容的提问来源于stack exchange,提问作者tval

