IQueryable Where - Equivalent to IN in SQL?

  • Thread starter Thread starter powpowqueen
  • Start date Start date
P

powpowqueen

I need to do the following.

Find all Products from a product list, that are in any of the sub
categories in a list.

The product object has many sub categories.

I tried

products.Where(x => x.SubCategories.Any(sc => subCategories.Contains
(sc)));

(subCategories - is a list of possible sub categories)

This doesnt work, it throws a null refrence exception. im not sure
why.

Any Help?
 
I need to do the following.

Find all Products from a product list, that are in any of the sub
categories in a list.

The product object has many sub categories.

I tried

products.Where(x => x.SubCategories.Any(sc => subCategories.Contains
(sc)));

(subCategories - is a list of possible sub categories)

This doesnt work, it throws a null refrence exception. im not sure
why.
That's easy: because one of the references you're calling methods on is null.

If that sounds trivial, it's because you haven't reasoned from it.
NullReferenceExceptions are *never* generated by framework methods unless
you actually pass in a null reference in the outer layer (or you've found a
bug in the framework, but that's much rarer).

So in the example above, either "products" is null, or one of the product
references in "products" is null, or one of the "SubCategories" properties
of one of the products is null, or "subCategories" is null. You'll need to
know which one to eliminate the error by either ensuring they're not null or
putting in a check and retuning something else, or by rewriting the query.

For example, assuming that "products" is never null, that a product
reference is never null and that "subCategories" is never null, the problem
could only be that one of the SubCategories properties on a product is null.
This is best solved by making sure SubCategories is always a valid list of
subcategories, even if its empty.
 
That's easy: because one of the references you're calling methods on is null.

If that sounds trivial, it's because you haven't reasoned from it.
NullReferenceExceptions are *never* generated by framework methods unless
you actually pass in a null reference in the outer layer (or you've founda
bug in the framework, but that's much rarer).

So in the example above, either "products" is null, or one of the product
references in "products" is null, or one of the "SubCategories" properties
of one of the products is null, or "subCategories" is null. You'll need to
know which one to eliminate the error by either ensuring they're not nullor
putting in a check and retuning something else, or by rewriting the query..

For example, assuming that "products" is never null, that a product
reference is never null and that "subCategories" is never null, the problem
could only be that one of the SubCategories properties on a product is null.
This is best solved by making sure SubCategories is always a valid list of
subcategories, even if its empty.

None of these objects are null, i did check that.

I didnt include code as i was hoping for a generic response,

I have read in other places that this was not possible in LINQ


public class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<SubCategory> SubCategories { get; set; }
}


public class SubCategory
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}

IList<SubCategories> subCategories = new Services.SubCategoryService
().Fetch(); //Retrieves all Sub Catagories.

IQueryable<Product> products = _repository.Query(); // this returns a
IQueryable object via NHibernate.

products.Where(x => x.SubCategories.Any(sc => subCategories.Contains
(sc)));

subCategories is not null, Products is not null, and neither is
SubCategories in products.

Yet i get a null exception.

I do find linq quite difficult to debug.
 
None of these objects are null, i did check that.
Humor me and check again. Run the following code instead of your query:

products.ToString();
products.Select(x => x.ToString()).ToArray();
products.Select(x => x.SubCategories.ToString()).ToArray();
subCategories.ToString();
products.Select(x => x.SubCategories.Select(sc =>
sc.ToString()).ToArray()).ToArray();

If *none* of these statements generate a NullReferenceException, *then* the
problem is interesting. As in, "show me the stack trace" interesting.
I didnt include code as i was hoping for a generic response,
Well, I'd say you got what you asked for. I hope you also see the inherent
drawback in doing this: we now have to do rounds of questions and answers
before we can get near the actual problem.
I have read in other places that this was not possible in LINQ
That *what* wasn't possible in LINQ? Executing LINQ queries? Because that's
what you're doing.

A NullReferenceException indicates a bug, not an unsupported feature. Your
query should return a result. Whether it's the result you want is another
matter.
public class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<SubCategory> SubCategories { get; set; }
}


public class SubCategory
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}
Are these your actual classes or idealized versions? In particular, does
SubCategory happen to override Equals or implement IEquatable?
IList<SubCategories> subCategories = new Services.SubCategoryService
().Fetch(); //Retrieves all Sub Catagories.

IQueryable<Product> products = _repository.Query(); // this returns a
IQueryable object via NHibernate.

products.Where(x => x.SubCategories.Any(sc => subCategories.Contains
(sc)));
More possible causes for error can be added to the list: there could be a
bug in NHibernate or the way you use it.
subCategories is not null, Products is not null, and neither is
SubCategories in products.
That's not a full set of checks.
Yet i get a null exception.

I do find linq quite difficult to debug.

LINQ basically generates complex expressions. You debug those like any
other: break them up in explicit steps and check intermediate results. But
yes, lots of calls may be involved that you don't see coming, especially if
you're operating on highly abstracted sequences.

NullReferenceExceptions deserve special mention because they're
intrinsically hard to debug, LINQ or no. By the time you get the exception
it's too late: you only know that a reference is ultimately null, not *why*
it's null. This is why it's critical that every piece of code in the chain
check for nulls as soon as possible (or reasonable) and throw an
ArgumentNullException instead, so you can identify exactly where assumptions
were violated. Obviously, this is not happening somewhere, possibly in a
piece of code that omits this for performance reasons.
 
Jeroen Mostert wrote:
[tracing a NullReferenceException]
If *none* of these statements generate a NullReferenceException, *then*
the problem is interesting. As in, "show me the stack trace" interesting.
Actually, whatever you do, include the stack trace. I've got a hunch it will
show exactly where the problem is in this case. (This is not true in general
for NullReferenceExceptions.)
 
I tried running what you asked, and this is what i got.

products.ToString(); -- "NHibernate.Linq.Query`1[Domain.Product]"
subCategories.ToString(); -- "System.Collections.Generic.List`1
[Domain.SubCategory]"

I tried running all of these -
string[] prod = products.Select(x => x.ToString()).ToArray();
string[] prod1 = products.Select(x =>
x.SubCategories.ToString()).ToArray();
string[][] prod2 = products.Select(x =>
x.SubCategories.Select(sc => sc.ToString()).ToArray()).ToArray();
but each threw an error - errors below respectively

Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index
Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index
The method 'ToArray' is not implemented.

When i said i wanted a generic answer, i didnt mean to be awkward, i
was merely asking, whether what i had written didnt look like
completely the wrong way to takle what i was trying to do.



Here is the stack trace of my original statment
[NullReferenceException: Object reference not set to an instance of an
object.]
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetEntityName
(ICriteria subcriteria, String propertyName) +13
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetType
(ICriteria subcriteria, String propertyName) +18

NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetTypeUsingProjection
(ICriteria subcriteria, String propertyName) +94
NHibernate.Criterion.InExpression.GetTypedValues(ICriteria
criteria, ICriteriaQuery criteriaQuery) +63

NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetQueryParameters
() +159

NHibernate.Criterion.SubqueryExpression.InitializeInnerQueryAndParameters
(ICriteriaQuery criteriaQuery) +120
NHibernate.Criterion.SubqueryExpression.ToSqlString(ICriteria
criteria, ICriteriaQuery criteriaQuery, IDictionary`2 enabledFilters)
+23
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetWhereCondition
(IDictionary`2 enabledFilters) +223
NHibernate.Loader.Criteria.CriteriaJoinWalker..ctor
(IOuterJoinLoadable persister, CriteriaQueryTranslator translator,
ISessionFactoryImplementor factory, CriteriaImpl criteria, String
rootEntityName, IDictionary`2 enabledFilters) +296
NHibernate.Loader.Criteria.CriteriaLoader..ctor(IOuterJoinLoadable
persister, ISessionFactoryImplementor factory, CriteriaImpl
rootCriteria, String rootEntityName, IDictionary`2 enabledFilters)
+131
NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList
results) +173
NHibernate.Impl.CriteriaImpl.List(IList results) +41
NHibernate.Impl.CriteriaImpl.List() +35
NHibernate.Linq.<GetEnumerator>d__0.MoveNext() +71
System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
+7663156
System.Linq.Enumerable.ToList(IEnumerable`1 source) +61
Services.ProductService.Fetch(IList`1 subCategories) in E:\Code
\FluentNHibTest\Services\ProductService.cs:75
Controls.Products.UpdateCriteria(IList`1 subCategories) in E:\Code
\FluentNHibTest\Web\Controls\Products.ascx.cs:39
Web.Products.cCriteria_CriteriaChanged(Object sender, EventArgs`1
e) in E:\Code\FluentNHibTest\Web\Products.aspx.cs:72
Web.Controls.Criteria.ResetSelection() in E:\Code\FluentNHibTest\Web
\Controls\Criteria.ascx.cs:454
Web.Controls.Criteria.SubcategoriesCheckBoxList_DataBound(Object
sender, EventArgs e) in E:\Code\FluentNHibTest\Web\Controls
\Criteria.ascx.cs:305
System.Web.UI.WebControls.BaseDataBoundControl.OnDataBound
(EventArgs e) +97
System.Web.UI.WebControls.ListControl.PerformSelect() +63
System.Web.UI.WebControls.BaseDataBoundControl.DataBind() +73
System.Web.UI.WebControls.BaseDataBoundControl.EnsureDataBound()
+82
System.Web.UI.WebControls.BaseDataBoundControl.OnPreRender
(EventArgs e) +22
System.Web.UI.WebControls.ListControl.OnPreRender(EventArgs e) +18
System.Web.UI.WebControls.CheckBoxList.OnPreRender(EventArgs e) +20
System.Web.UI.Control.PreRenderRecursiveInternal() +80
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
+842
 
I tried running what you asked, and this is what i got.

products.ToString(); -- "NHibernate.Linq.Query`1[Domain.Product]"
subCategories.ToString(); -- "System.Collections.Generic.List`1
[Domain.SubCategory]"


I tried running all of these -
string[] prod = products.Select(x => x.ToString()).ToArray();
string[] prod1 = products.Select(x =>
x.SubCategories.ToString()).ToArray();
string[][] prod2 = products.Select(x =>
x.SubCategories.Select(sc => sc.ToString()).ToArray()).ToArray();
but each threw an error - errors below respectively


Index was out of range. Must be non-negative and less than the size
of
the collection. Parameter name: index
Index was out of range. Must be non-negative and less than the size
of
the collection. Parameter name: index
The method 'ToArray' is not implemented.


When i said i wanted a generic answer, i didnt mean to be awkward, i
was merely asking, whether what i had written didnt look like
completely the wrong way to takle what i was trying to do.


Here is the stack trace of my original statment
[NullReferenceException: Object reference not set to an instance of
an
object.]
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetEntityName
(ICriteria subcriteria, String propertyName) +13
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetType
(ICriteria subcriteria, String propertyName) +18


NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetTypeUsingProjection
(ICriteria subcriteria, String propertyName) +94
NHibernate.Criterion.InExpression.GetTypedValues(ICriteria
criteria, ICriteriaQuery criteriaQuery) +63


NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetQueryParameters
() +159


NHibernate.Criterion.SubqueryExpression.InitializeInnerQueryAndParameters
(ICriteriaQuery criteriaQuery) +120
NHibernate.Criterion.SubqueryExpression.ToSqlString(ICriteria
criteria, ICriteriaQuery criteriaQuery, IDictionary`2 enabledFilters)
+23

NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetWhereCondition
(IDictionary`2 enabledFilters) +223
NHibernate.Loader.Criteria.CriteriaJoinWalker..ctor
(IOuterJoinLoadable persister, CriteriaQueryTranslator translator,
ISessionFactoryImplementor factory, CriteriaImpl criteria, String
rootEntityName, IDictionary`2 enabledFilters) +296
NHibernate.Loader.Criteria.CriteriaLoader..ctor(IOuterJoinLoadable
persister, ISessionFactoryImplementor factory, CriteriaImpl
rootCriteria, String rootEntityName, IDictionary`2 enabledFilters)
+131
NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList
results) +173
NHibernate.Impl.CriteriaImpl.List(IList results) +41
NHibernate.Impl.CriteriaImpl.List() +35
NHibernate.Linq.<GetEnumerator>d__0.MoveNext() +71
System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
+7663156
System.Linq.Enumerable.ToList(IEnumerable`1 source) +61
Services.ProductService.Fetch(IList`1 subCategories) in E:\Code
\FluentNHibTest\Services\ProductService.cs:75
Controls.Products.UpdateCriteria(IList`1 subCategories) in E:\Code
\FluentNHibTest\Web\Controls\Products.ascx.cs:39
Web.Products.cCriteria_CriteriaChanged(Object sender, EventArgs`1
e) in E:\Code\FluentNHibTest\Web\Products.aspx.cs:72
Web.Controls.Criteria.ResetSelection() in E:\Code\FluentNHibTest
\Web
\Controls\Criteria.ascx.cs:454
Web.Controls.Criteria.SubcategoriesCheckBoxList_DataBound(Object
sender, EventArgs e) in E:\Code\FluentNHibTest\Web\Controls
\Criteria.ascx.cs:305
System.Web.UI.WebControls.BaseDataBoundControl.OnDataBound
(EventArgs e) +97
System.Web.UI.WebControls.ListControl.PerformSelect() +63
System.Web.UI.WebControls.BaseDataBoundControl.DataBind() +73
System.Web.UI.WebControls.BaseDataBoundControl.EnsureDataBound()
+82
System.Web.UI.WebControls.BaseDataBoundControl.OnPreRender
(EventArgs e) +22
System.Web.UI.WebControls.ListControl.OnPreRender(EventArgs e) +18
System.Web.UI.WebControls.CheckBoxList.OnPreRender(EventArgs e)
+20
System.Web.UI.Control.PreRenderRecursiveInternal() +80
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Control.PreRenderRecursiveInternal() +171
System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
+842
 
IQueryable<Product> products = _repository.Query(); // this returns a
IQueryable object via NHibernate.

Last I checked (which was about 3 months ago), NHibernate LINQ support
was "experimental", "not fully supported", and was being rewritten
from scratch for the next version. It seems likely that it is your
real problem.

I also did a quick Google search for "NHibernate LINQ 2009" to get the
recent updates, and immediately found this thread:

http://groups.google.com/group/nhusers/browse_thread/thread/b7a3f537f4cbf46d

Specifically:

"Most queries are supported with the exception of the following:

- Group Joins
- Subqueries in select clause

For a more complete list of unsupported queries, you can look at the
skipped
test cases in the source distribution."

So probably best to ask there if it's supported or not (in general, if
you're using a third-party LINQ provider, you should ask the
implementor about any limits and known bugs).
 
I tried running what you asked, and this is what i got.

products.ToString(); -- "NHibernate.Linq.Query`1[Domain.Product]"
subCategories.ToString(); -- "System.Collections.Generic.List`1
[Domain.SubCategory]"

I tried running all of these -
string[] prod = products.Select(x => x.ToString()).ToArray();
string[] prod1 = products.Select(x =>
x.SubCategories.ToString()).ToArray();
string[][] prod2 = products.Select(x =>
x.SubCategories.Select(sc => sc.ToString()).ToArray()).ToArray();
but each threw an error - errors below respectively

Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index
Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index
The method 'ToArray' is not implemented.
The problem is with your query provider or your underlying data. These
queries should have run fine, as we're not referring to an array index
anywhere (so the underlying code is, and doing so wrongly).
When i said i wanted a generic answer, i didnt mean to be awkward, i
was merely asking, whether what i had written didnt look like
completely the wrong way to takle what i was trying to do.
Then I can say that it doesn't look like the completely wrong way. It should
work.
Here is the stack trace of my original statment
[NullReferenceException: Object reference not set to an instance of an
object.]
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetEntityName
(ICriteria subcriteria, String propertyName) +13
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetType
(ICriteria subcriteria, String propertyName) +18
OK, that seems to settle it then -- NHibernate doesn't like what you're
doing, for whatever reason. You'll have to ask the NHibernate folks for more
details.
 
Back
Top