odata.net icon indicating copy to clipboard operation
odata.net copied to clipboard

System.NotSupportedException: Could not convert constant System.Collections.Generic.List`1[System.Guid] expression to string

Open ShvetsovAU opened this issue 3 years ago • 3 comments

Hello.

I use Microsoft.OData.Client library to get data from service.

var dataServerUrl = "http://localhost:5005/api"; var oDataContext = new DataServiceContext(new Uri(dataServerUrl));

var dataSet4 = oDataContext.CreateQuery<JournalRecord>("JournalRecords");

var res43 = dataSet4.ToList(); var res441 = dataSet4.Where(i => res43.Any(db => db.ObjectId == i.ObjectId)).ToList();

or

var res43IdList = res43.Select(db => db.ObjectId).ToList();//.Take(100).ToList(); var res442 = dataSet4.Where(i => res43IdList.Contains(i.ObjectId)).ToList();

or

var res442 = dataSet4.Where(i => res43.Select(db => db.ObjectId).Any(i.ObjectId)).ToList();

I get error: "System.NotSupportedException: Could not convert constant System.Collections.Generic.List1[System.Guid] expression to string. at Microsoft.OData.Client.ExpressionWriter.VisitConstant(ConstantExpression c) at Microsoft.OData.Client.ALinqExpressionVisitor.Visit(Expression exp) at Microsoft.OData.Client.DataServiceALinqExpressionVisitor.Visit(Expression exp) at Microsoft.OData.Client.ExpressionWriter.Visit(Expression exp) at Microsoft.OData.Client.ExpressionWriter.VisitMethodCall(MethodCallExpression m) at Microsoft.OData.Client.ALinqExpressionVisitor.Visit(Expression exp) at Microsoft.OData.Client.DataServiceALinqExpressionVisitor.Visit(Expression exp) at Microsoft.OData.Client.ExpressionWriter.Visit(Expression exp) at Microsoft.OData.Client.ExpressionWriter.Translate(Expression e) at Microsoft.OData.Client.ExpressionWriter.ExpressionToString(DataServiceContext context, Expression e, Boolean inPath, Version& uriVersion) at Microsoft.OData.Client.UriWriter.VisitQueryOptions(ResourceExpression re) at Microsoft.OData.Client.UriWriter.VisitQueryableResourceExpression(QueryableResourceExpression rse) at Microsoft.OData.Client.DataServiceALinqExpressionVisitor.Visit(Expression exp) at Microsoft.OData.Client.UriWriter.Translate(DataServiceContext context, Boolean addTrailingParens, Expression e, Uri& uri, Version& version) at Microsoft.OData.Client.DataServiceQueryProvider.Translate(Expression e) at Microsoft.OData.Client.DataServiceQuery1.Translate() at Microsoft.OData.Client.DataServiceQuery1.Execute() at Microsoft.OData.Client.DataServiceQuery1.GetEnumerator() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at OdataToEntity.Test.DynamicDataContext.ODataClientTest.Controllers.TestController.GetByOData()"

What's wrong?

ShvetsovAU avatar Mar 01 '21 05:03 ShvetsovAU

@ShvetsovAU Thank you for reporting this issue. I was able to reproduce it. Find my explanation below.

This is currently not supported:

var res43ObjectIdList = dataSet4.Select(d1 => d1.ObjectId).ToList();

System.NotSupportedException: 'Individual properties can only be selected from a single resource or as part of a type. Specify a key predicate to restrict the entity set to a single instance or project the property into a named or anonymous type.'

Workaround would be to do this:

// ?$select=ObjectId
var res43 = dataSet4.Select(d1 => new { d1.ObjectId }).ToList();

// Then generate an array of ObjectId
var res43IdList = res43.Select(d1 => d1.ObjectId).ToArray();

To achieve your objective, you could use the array in the code snippet above as follows:

// ?$filter=ObjectId in (...)
var res442 = dataSet4.Where(i => res43IdList.Contains(i.ObjectId)).ToList();

Support for IN with IEnumerable<T> or arrays was introduced in this PR https://github.com/OData/odata.net/pull/1868. The Contains method is translated to the in expression. However, IList/List & ICollection/Collection are not currently supported. You can go through the conversation on the referenced PR for more details. We'd be appreciate a pull request contribution to add that support if you're in a position to. Kindly let me know if the workaround works for you

gathogojr avatar Mar 03 '21 14:03 gathogojr

@ShvetsovAU Thank you for reporting this issue. I was able to reproduce it. Find my explanation below.

This is currently not supported:

var res43ObjectIdList = dataSet4.Select(d1 => d1.ObjectId).ToList();

System.NotSupportedException: 'Individual properties can only be selected from a single resource or as part of a type. Specify a key predicate to restrict the entity set to a single instance or project the property into a named or anonymous type.'

Workaround would be to do this:

// ?$select=ObjectId
var res43 = dataSet4.Select(d1 => new { d1.ObjectId }).ToList();

// Then generate an array of ObjectId
var res43IdList = res43.Select(d1 => d1.ObjectId).ToArray();

To achieve your objective, you could use the array in the code snippet above as follows:

// ?$filter=ObjectId in (...)
var res442 = dataSet4.Where(i => res43IdList.Contains(i.ObjectId)).ToList();

Support for IN with IEnumerable<T> or arrays was introduced in this PR #1868. The Contains method is translated to the in expression. However, IList/List & ICollection/Collection are not currently supported. You can go through the conversation on the referenced PR for more details. We'd be appreciate a pull request contribution to add that support if you're in a position to. Kindly let me know if the workaround works for you

Hello.

Thank you for your answer.

Kindly let me know if the workaround works for you

Enough next:

var res43 = dataSet4.ToList();
var res43IdList = res43.Select(db => db.ObjectId).ToArray();//.ToList();; //TODO: IList/List & ICollection/Collection are not currently supported
var res442 = dataSet4.Where(i => res43IdList.Contains(i.ObjectId)).ToList();

ShvetsovAU avatar Mar 10 '21 07:03 ShvetsovAU

@ShvetsovAU Thank you for reporting this issue. I was able to reproduce it. Find my explanation below.

Hello. Can you another check next issue https://github.com/OData/odata.net/issues/1247 ?

ShvetsovAU avatar Mar 11 '21 12:03 ShvetsovAU