简体   繁体   中英

Why isn't this short circuit is not working in linq to sql query?

The query:

List<int> companyIds = null;

(from car in context.GetTable<Car>()
where companyIds == null || companyIds.Contains(car.companyID)
select car)
.ToList();

The result:

at System.Linq.Enumerable.OfType[TResult](IEnumerable source) at System.Data.Linq.SqlClient.QueryConverter.VisitContains(Expression sequence, Expression value) at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp) at System.Data.Linq.SqlClient.QueryConverter.VisitBinary(BinaryExpression b) at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp) at System.Data.Linq.SqlClient.QueryConverter.VisitWhere(Expression sequence, LambdaExpression predicate) at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) at System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node) at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations) at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query) at System.Data.Linq.DataQuery 1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Collections.Generic.List 1..ctor(IEnumerable 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable 1 source) at EVaultSDK.Services.CompanyService.Get...

If I add ToList

 context.GetTable<Car>().ToList()

It works

I think the reason is you are using LINQ to SQL, your query needs to be translated into SQL and you get the exception while this translation happens. The Contains is translated into IN operator in SQL. But since the list is null I think the LINQ to SQL provider throws an exception.

This is the reason why you don't get the exception when you add ToList after GetTable<Car>() , this causes you to fetch and load all Cars in memory, therefore the query runs on memory so don't need to be translated to SQL and the short-circuiting works as expected.

Normally if you call Contains on a null list, you should get NullReferenceException but you are getting ArgumentNullException . So you should check stack trace and if it's the case, don't use a null list in your query.

Edit: The stack trace you posted confirms my assumption. Somewhere along the road OfType method is called on the list and it causes the exception.

You already got a good answer on why you're getting the error - not everything in C# translates nicely to SQL, and you often don't find out until runtime.

Just in case a future visitor hits this question looking for a workaround, I'd suggest using the fact that you can build a query up and it won't retrieve any data until you call ToList() (or Single() , etc...):

List<int> companyIds = null;

var query = context.GetTable<Car>();
if (companyIds != null)
    query = query.Where(car => companyIds.Contains(car.companyID));

var result = query.ToList();

Try this. It will return CompanyIds is null or contain car.CompanyID

return (from car in context.GetTable<Car>() 
        where companyIds == null || (companyIds != null && companyIds.Contains(car.companyID)) 
        select car)
        .ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM