0

I have a problem in my query of null variable when I try to use related table with include, my database is mysql

// working no error
var query = context.Category.Where(c => c != null && ListOfIds.Contains(c.Id)).ToList();
var count = context.Category.Include(x => x.Products).Count();

// error null
var query = context.Category.Include(x => x.Products).Where(c => c != null && ListOfIds.Contains(c.Id)).ToList();

Here My Model Product :

public class category {

public category() {}

public category(int productscount: this() {
    products = new List < Product > ();
    for (int i = 0; i < productscount: i++) {
        products.Add(new Product());
    }
}

public override int Id {
    get;
    set;
}

public List < Product > Products {
    get;
    set;
}}

Here my stack trace :

à MySql.Data.EntityFramework.SelectStatement.AddColumn(ColumnFragment column, Scope scope) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbApplyExpression expression) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbProjectExpression expression) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbSortExpression expression) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbProjectExpression expression) à MySql.Data.EntityFramework.SelectGenerator.GenerateSQL(DbCommandTree tree) à MySql.Data.MySqlClient.MySqlProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree) à System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory) à System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext) à System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree) à System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean streaming, Span span, IEnumerable1 compiledQueryParameters, AliasGenerator aliasGenerator) à System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable1 forMergeOption) à System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass41_0.b__1() à System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) à System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass41_0.b__0() à System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption) à System.Data.Entity.Core.Objects.ObjectQuery1..GetEnumerator>b__31_0() à System.Data.Entity.Internal.LazyEnumerator1.MoveNext() à System.Collections.Generic.List1..ctor(IEnumerable1 collection) à System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)

Coder-Meca
  • 401
  • 1
  • 3
  • 13
  • 1
    didn't you post the exact same question a few seconds ago: https://stackoverflow.com/questions/71321689/null-reference-with-related-table-in-where-iqueryable?noredirect=1. Just posting that again and again without adding any more information about your data for example won't help you getting an answer. – MakePeaceGreatAgain Mar 02 '22 at 11:20
  • @MakePeaceGreatAgain Just in the previous question said that is duplicate – Coder-Meca Mar 02 '22 at 11:23
  • the point on NullReferences is that there's no way for us to help you unless we don't know your object-structure and the data. Usually you're far better off using your debugger and inspect which values exactly are null. That is what the dupe also suggests. – MakePeaceGreatAgain Mar 02 '22 at 11:25
  • my assumption would be, that not all categories are mapped to a product, hence your null values. why do you include Products if you don't use them in the query? – Mong Zhu Mar 02 '22 at 11:28
  • @MakePeaceGreatAgain I added my model product – Coder-Meca Mar 02 '22 at 11:30
  • @MongZhu because after this query iI want to get all Category with related product , how to prevent category that is not mapped to product in query ? – Coder-Meca Mar 02 '22 at 11:32
  • @MongZhu in my datatbase i don't have CategoryId is null in my data table product all CategoryId are not null – Coder-Meca Mar 02 '22 at 11:37
  • Please use your debugger and check which values are null - there **must** be one. Don't let your **assumptions** about your data guide your **developer-experience**. Instead **verifiy** that your data is valid, e.g by splitting your querires into smaller chunks and check there elements one by one. – MakePeaceGreatAgain Mar 02 '22 at 12:11
  • 1
    Or check the stack trace to verify that it's your own code or a bug in the MySql query provider. – Gert Arnold Mar 03 '22 at 18:53

1 Answers1

0

Simply adding an Include() statement in a query should not cause an error executing that query to materialize results. That isn't to say that if you are later iterating through those top level entities that the included relationship will not be empty.

If you only want to return Categories that are in that list of IDs and have at least one Product associated with them, eager loading those products:

var query = context.Category
    .Include(x => x.Products)
    .Where(c => c.Products.Any() 
        && ListOfIds.Contains(c.Id))
    .ToList();

Firstly, adding a Where clause for c => c != null is pretty much pointless. However, if you want categories that have Products then use c.Products.Any() to put a condition to check for those.

One recommendation I have to avoid Null references when dealing with the possibility that you will have entities with no related data is to ensure you initialize related collection references.

Instead of:

public class Category
{
    // ...
    public virtual ICollection<Product> Products { get; set; }
}

use

public class Category
{
    // ...
    public virtual ICollection<Product> Products { get; set; } = new List<Product>();
}

This ensures that any code that might access the products collection won't raise the null reference exception. Ideally you would support lazy loading as a failsafe in the event that the collection wasn't eager loaded. For new top-level entities (Category) this also means that your products collection is ready to go to add references to products.

Edit: Check that your relationship between category and product is mapped correctly, either via attributes, OnModelCreating, or an EntityTypeConfiguration, or whether you are relying on an EDMX for DB-First. EF can work out relationships by convention, but that can be leaving things to chance as to whether EF worked it out correctly.

For example, using attributes:

public int? CategoryId { get; set; }
[ForeignKey("CategoryId"), InverseProperty("Products")]
public Category Category{ get; set; }

This tells EF what the FK property in this entity is, and that this is related to the Products collection in the Category entity. Alternatively the [InverseProperty] could be put on the Products collection within Category. If EF isn't associating the Products collection properly with the relationship to a product's Category then there may be issues. Generally though this would result in a mapping error rather than something like NullReferenceException.

Another consideration might be an issue specific to the MySQL EF provider you are using. I am not aware of any MySQL issues around Include interfering with a query execution.

the last consideration might be if your code base is overriding EF's query execution for some reason to inspect and attempt to inject SQL or such. A clue might be in the Exception's call stack to see where this NullReferenceException is being raised, whether internal to EF or a method within the DbContext itself.

Edit 2: Ok, from your comments Ok, t sounds like you have one query with filtering where you end up getting the IDs, but then want to feed those IDs into this method to get the data and related records. This will result in a few problems, possibly limitations in the MySQL provider. Normally when you use something like Contains in a query, the provider will generally be trying to produce something like an IN() clause and ty in the JOIN for the related table. I'd be surprised if it worked with such a large set without the Include.

The first thing I'd check as a test is just trying loading all Categories with their products eager loaded...

var categories = context.Category.Include(x => x.Products).ToList();

If this causes an error then it might be a case where your mapping of Products or the relationship between product and category is configured to expect something like a required non-nullable value/reference and declared as a non-nullable member in the entity, but the data supports and has #null in that column on some rows. When loading the first few thousand rows, everything is valid, but it happens that some of the later data has an issue.

The next point is to not use that Contains approach for dealing with such a large set of IDs. From what you describe I'm guessing you have something like:

List<int> listOfIds = SomeMethodThatQueriesAndGetsIds(criteria);
var categories = GetCategoriesWithProducts(listOfIds);

To fix this where one method has a Query that has all of the filtering criteria and you don't want to duplicate that code, break off the query generation into a private method that returns IQueryable<Category> that takes the criteria and can be called by both the original method to Select IDs or whatever data that method needs, where your new method can call it, Include Products and ToList the results passing the same criteria instead of a list of 10,000 - 41,000 Ids.

For instance if the original method had something like:

var results = context.Category
    .Where(x => x.Condition == criteria.Condition
         && x.SomeOtherCondition == criteria.Condition2)
    .Select(x => new SomeDTO
    {
        CategoryId = x.Id,
        // ....
    }).ToList();
return results;

and you were generating your ListOfIds using the results.Select(x => x.CategoryId).ToList(); or such to feed to the new method... Change this in the original method to do something like this:

var results = getCategorysByCriteria(criteria)
    .Select(x => new SomeDTO
    {
        CategoryId = x.Id,
        // ....
    }).ToList();
return results;

then

private IQueryable<Category> getCategoriesByCriteria(Criteria criteria)
{
    return context.Category
    .Where(x => x.Condition == criteria.Condition
         && x.SomeOtherCondition == criteria.Condition2);
}

This way, in your new method, instead of passing the list of IDs, pass the criteria that would have been sent to the first method to get those IDs:

 var data = getCategoriesByCriteria(criteria)
     .Include(x => x.Products)
     .ToList();

The "Criteria" could be an object containing the criteria, or simply replaced with the parameters used for the method you wrote to get the IDs. The point is that the logic to build the Query is centralized in one spot and the methods using it can still customize how they execute and consume the query results.

Loading 10,000 or 41,000 entities and related data is certainly not a good idea. You should take a close look at why you want to load this data and either summarize or paginate these results. Users typically will not need 40k results all in one go, and even things like batch updates should probably use a more direct mechanism to update the data rather than EF entities. (I.e. a stored procedure, or a background process that will deal with smaller batches at a time and handle rollback capabilities manually if necessary.)

Steve Py
  • 26,149
  • 3
  • 25
  • 43
  • NullReferenceException with this query also and my category.Products it is a list – Coder-Meca Mar 02 '22 at 11:51
  • Please add the model declaration for Category to your question, as well as any configuration for the relationships between Category and Model. I'm highly suspicious that there is some kind of mapping anomaly between these classes if the ToList() call is triggering an exception. – Steve Py Mar 02 '22 at 20:37
  • Also the full exception call stack. I added some detail above to consider checking to narrow down possible culprits for this exception. – Steve Py Mar 02 '22 at 21:21
  • I added my model Category to question – Coder-Meca Mar 03 '22 at 08:59
  • That constructor initializing the list of empty products is a bit odd, but shouldn't cause an issue when loading from the DbContext. Why is Id an override when there is no base class mentioned? So what happens if you add an `InverseProperty` to configure the relationship between Category and its Products? Also, please include the full exception call stack for the NullReferenceException you are getting. – Steve Py Mar 03 '22 at 20:53
  • Inverse Property dosn't solve the problem I have added the stack trace of error – Coder-Meca Mar 04 '22 at 09:37
  • The problem with a result of 5000 rows my query work but over 10000 rows dosn't work – Coder-Meca Mar 04 '22 at 09:52
  • When you say a result of 5000 vs 10000, are you trying to pass a List of 5000 vs. 10000 IDs into that ListOfIds to run `.Contains()` over? Loading 10,000, or even 5000 entities at one time with related data is generally not a good idea, Using Lists /w `.Contains()` should be limited to far, far fewer values. The exception call stack doesn't look quite complete. – Steve Py Mar 04 '22 at 10:38
  • yes that is what i'am doing I have a scenario of 41000 rows in IDs and when the number of elements in Ids pass almost 10000 I have the exception. The logic I do that because I'm working in a method that makes me obliged to work with a variable dtresult iquerable that have all filters so in every condition I must use the old dtresult and do where condition for not lose old filtering and also dtresult contains related data so I don't need to do Include just I do dtResult.where how can I change the logic of my query and without Include so I will use dtResult variable and my List ListOfIds – Coder-Meca Mar 04 '22 at 14:45
  • 1
    I have added another Edit (2) to the answer for you to consider and the pitfalls you might be hitting with that approach. – Steve Py Mar 05 '22 at 03:10
  • To get list of Id i'm using SQL query context.category.SqlQuery("SELECT id ...) I Can get the list of category or their ids I do that because i'm using Full text string index in MySQL to filter Columns with exact words this IS m'y criteria it IS a query string executed with SQL query c # and After that i must do union with the dtresult that contains the precedent filtering so my objectif is doing dtresult.where id in m'y list ids or i do union to m'y dtresult and the New iquerable filtering with criteria Columns match words Full string text index in mysql – Coder-Meca Mar 05 '22 at 19:01
  • With my method I Can get the searched category mapped to thé model with _supplierDbContext.Database.SqlQuery but without products despite that I did a join in the query and a select product.* So when I do the result of this .include I have the error and if it works when I did union between this result and dtresult that contain the precedent filtering iquerable I have the error of null. In m'y method there IS Many Many if condition than a filtering and the general result IS dtresult so I did Always dtresult.where(filtering) in every if condition until the end I return final dtresult – Coder-Meca Mar 05 '22 at 19:12
  • var data = getCategoriesByCriteria(criteria) .Include(x => x.Products) .ToList(); this dosn't load products for me and I have added proprities inverse and foreign key in model – Coder-Meca Mar 07 '22 at 10:05
  • what if you try `_supplierDbContext.Category.Include(x=>x.Products).ToList();` Yes, this will load all categories with all products, but does this run and eager load the products? If this errors (barring attempting to load far too much data if these tables are huge) then there may be an issue with your Product mapping not handling null data present on some rows. A `getCategoriesByCriteria` method like I outlined above that returns `IQueryable` while the DbContext is still in scope (injected or module level) should certainly work with `.Include()` to eager load. – Steve Py Mar 08 '22 at 00:42
  • I did it but products not loaded the only difference that I do that var data = getCategoriesByCriteria(queryStringSearchedCategoryCriteria).Include(x => x.products).ToList(); string queryStringSearchedCategoryCriteria= String.Format("SELECT distinct cat.* ..... ) Here the result I have searched category but products dosn't been loaded --- In getCategories I have : context.Database.SqlQuery(query).ToList().AsQueryable(); the result 41365 rows without products – Coder-Meca Mar 08 '22 at 11:44
  • 1
    Using an SQLQuery to retrieve Categories will *not* eager load Products even if you use `Include`. `Include` works in conjunction with EF's query building to know to build an SQL statement that joins the included rows and deals with any Cartesians in the results. When you use SqlQuery() you are giving EF the SQL to use and just telling it what to project. If you want to leverage eager loading and navigation properties you need to use Linq and EF to compose your `IQueryable`. – Steve Py Mar 08 '22 at 11:50
  • The problem I want to use Full text string index search in filtering so there is no way to load related entities with SQLQuery ?? or without Include ? I can't return a result from sql string with join and fill products List dynamically ? also I have an iquerable result with products loaded and when I want to do result.union(data (without products)) dosn't work So my logic is to take loaded products from result and put them in data ( generated with SQL QUERY) for that I try to use ListOfIds – Coder-Meca Mar 08 '22 at 11:55
  • What are these 41k records used for? Output to the UI? processing in code? Consider fetching the ID using criteria and the full text search except ensuring they are ordered and paginating the results so that <1000 IDs are returned at a time, ideally a page of 50-100 at a time, then use a regular Linq query with the resulting IDs eager loading the projects. If you still get errors loading specific pages (I.e. page 101 being rows 10100 - 10199) for example) then you most likely have mapping issues with Product or another related entity. Page 101 might fail, but 102 or 103 works. – Steve Py Mar 08 '22 at 12:02
  • 1
    EF Core can also utilize full text indexes with .FreeText(), though it may depend on your provider. EF6 may need some caressing to order results. Something like this might help: https://discourse.world/h/2019/06/06/Entity-Framework-6-with-Full-Text-Search-via-LINQ. – Steve Py Mar 08 '22 at 12:07
  • This is with MS SQL Server, me I'm working with MySQL Database – Coder-Meca Mar 08 '22 at 14:24