简体   繁体   中英

Why does a joined LINQ-to-SQL query generate a NullReferenceException?

I have a Document table and a Tenant table in my database, with a Document_Tenant table to relate the two. I'm trying to get a selection of all Documents , with related Tenants , if any. That should be a left join between Document and Document_Tenant and an inner join between Document_Tenant and Tenant , so I followed Entity framework left join to get this code:

var combinedQuery = from doc in DocumentProvider.GetDocuments()
                    join dt in CustomTableItemProvider.GetItems<Document_TenantItem>()
                         on doc.ItemID equals dt.Document_ID into ddt
                    from x in ddt.DefaultIfEmpty()
                    join t in TenantProvider.GetTenants()
                         on x.Tenant_ID equals t.ItemID    // joined tenants
                    select new { doc, t.Tenant_ID };

When that query executes, though, I get a NullReferenceException. I'm not sure why.

Exception type: NullReferenceException
Exception message: Object reference not set to an instance of an object.

at lambda_method(Closure, <>f__AnonymousType37 2, <>f__AnonymousType36 2 )
at System.Linq.Enumerable.d__23 3.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator
3.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator
3.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator
2.MoveNext()

at System.Linq.Enumerable.WhereEnumerableIterator 1.MoveNext()
at System.Linq.Enumerable.Count[TSource](IEnumerable
1.MoveNext()
at System.Linq.Enumerable.Count[TSource](IEnumerable
1.MoveNext()
at System.Linq.Enumerable.Count[TSource](IEnumerable
1 source)

I made a couple of mistakes in my original post. One was that this isn't actually Entity Framework, but an API similar to it. That meant I couldn't use navigation properties as suggested by Steve Py.

Also, and this is the critical one, I oversimplified my code. Where I posted select new { doc, t } , my code throwing the exception was select new { doc, t.Tenant_ID } . Since this is a left join, the t value is sometimes null - which leads to a NullReference exception.

I changed my select to read:

select new {
    Doc = doc,
    Tenant_ID = t == null ? null : t.Tenant_ID
};

Side note: I'm not sure why we can't use a null coalescing operator in the select, but we can't.

With all of that abstraction ("Providers") it's pretty much impossible to know what's going wrong, whether you have a mapping issue, or your abstractions are gunking up the works. Start with the simplest thing and work out from there:

First, If you have a relationship between documents and tenants via a document tenant table, map that relationship out in EF and leverage the navigation properties.

If the document_tenant is a table consisting of just a DocumentId and a TenantId, you don't even need to map an entity for DocumentTenant, simply HasMany().WithMany() can auto-resolve a joining table. Otherwise a bi-directional mapping 1-to-many-to-1 from Document to DocumentTenant to Tenant. With the relationship mapped, you don't need to explicitly join entities.

Next, remove those Providers out of the equation:

From the query I'm guessing you want to select every combination of document & tenant? If that is the case then having the DocumentTenant mapped, and available as a DbSet in the DbContext might be practical. Otherwise you can select them with a bit more effort via the Document

using (var context = new AppContext()) // insert name of your DBContext
{
    var documentTenants = context.DocumentTenants
       .Select(x => new {x.Document, x.Tenant })
       .ToList();
    // ...

   // or

   var documentTenantsViaDocument = context.Documents
      .SelectMany(x => x.DocumentTenants.Select(t => new { Document = x, Tenant = t.Tenant }))
      .ToList();
}

If that doesn't work then you would likely have a mapping issue against your schema. If that does work, then you possibly have complications being introduced by these Provider abstractions. What data type do they return? IQueryable<T> ? How are they returning it? just returning the DbContext's DbSet, or a filtered query result? Any use of ToList() or the like and AsQueryable() hidden anywhere in there? How is the DbContext being resolved? via an IoC container? What is it's lifetime scope? (PerRequest, Singleton, PerReference, etc.) The providers inside the Linq expression add a lot of unknowns.

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