2

I have the following query and I am getting a null reference exception:

(from cec in _myContext.table1s
 join ceclrp in _myContext.table2s on cec.table1ID equals ceclrp.table1ID
 join lrp in _myContext.table3s on ceclrp.table3ID equals lrp.table3ID
 join cecs in _myContext.table4s on cec.table1ID equals cecs.table1ID into cecsGroup
 from ecService in cecsGroup.DefaultIfEmpty()
 join cecse in _myContext.table5s on ecService.table4ID equals cecse.table4ID into cecseGroup
 from ecServiceEntitlement in cecseGroup.DefaultIfEmpty()
 where cec.ClientKey == clientKey
 select new
 {
     table1 = cec,
     table2 = ceclrp,
     table3 = lrp,
     table4 = ecService,
     table5 = ecServiceEntitlement,
 }).AsNoTracking();

Table 1, 2, and 3 records are required tables. However table 4 record is optional. If there is a table 4 record, then there can be an optional table 5 record.

However, I am getting a null reference exception when there is no table 4 or table 5 record.

I have looked at other questions similar to mine on StackOverflow but I can't figure out what is different from my query and the solutions posted.

Can someone please help me figure out why I am getting the null reference exception and what I need to do differently?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
jkruer01
  • 2,175
  • 4
  • 32
  • 57
  • I should add, there can be multiple table 4 records. Each table 4 record should only have 1 or 0 table5 records. – jkruer01 Apr 03 '15 at 21:22
  • See http://stackoverflow.com/questions/5537995/entity-framework-left-join – abatishchev Apr 06 '15 at 03:24
  • That question is only doing a single left outer join. I need to do a 2nd left outer join on my first left outer join. – jkruer01 Apr 06 '15 at 03:53
  • You can nest such left join. – abatishchev Apr 06 '15 at 03:55
  • Yeah, but when I try I get a null reference exception. Which is why I am asking this question. – jkruer01 Apr 06 '15 at 03:59
  • Ok, was you able to chase down where do you receive it exactly? – abatishchev Apr 06 '15 at 04:00
  • No. I can't figure it out. Visual Studio highlights the entire query block and says NullReference exception. I don't know which part is null. – jkruer01 Apr 06 '15 at 04:01
  • Materialize the data before left joins first. Then split the query into sub-queries with methods. Then you'll be able to find it out. – abatishchev Apr 06 '15 at 04:03
  • If I do just the first join everything works fine. But when I add the second join, it throws an error if the first join was null. – jkruer01 Apr 06 '15 at 04:10
  • Second join or second left join? – abatishchev Apr 06 '15 at 04:13
  • If I do just the first LEFT join everything works fine. But when I add the second LEFT join, it throws an error if the first LEFT join was null. – jkruer01 Apr 06 '15 at 04:26
  • Weird. I'd recommend to deconstruct/simplify the query as much as possible. Maybe create a new project and try there, then make it more and more complex. – abatishchev Apr 06 '15 at 04:28
  • But when you go to that page, the very last comment on the accepted answer says the same thing that I am saying. "if any of the previous join results in null, then I'm getting an null reference exception. how can I solve that issue? – Jain Sep 10 '14 at 5:46" – jkruer01 Apr 06 '15 at 04:46
  • Could you, please, include here in comment current stack when the exception is thrown? – mr100 Apr 06 '15 at 20:12

4 Answers4

2

According to MSDN,

The default value for reference and nullable types is null.

For more than one LEFT JOIN, we can handle the null value by passing defaultValue for the DefaultIfEmpty() extension method. The following sample code works when I pass the default Constructor new Driver(), it throws exception if the defaultValue is removed.

Here I have created three models (Dealer, Model and Customer) with matching records and two models (Driver, DriverAddress) with optional records. The same as OP scenario.

So, passing the default value for the first LEFT JOIN may solve your problem.

public class Dealer
{
    public int DealerId { get; set; }
    public string Name { get; set; }
}

public class Model
{
    public int ModelId { get; set; }
    public string Name { get; set; }
    public Dealer Dealer { get; set; }
}

public class Customer
{
    public int CustomerId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Model Model { get; set; }
}

public class Driver
{
    public int DriverId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Customer Customer { get; set; }
}

public class DriverAddress
{
    public int DriverAddressId { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string City { get; set; }
    public string Zip { get; set; }
    public Driver Driver { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var honda = new Dealer { DealerId = 1, Name = "Honda" };
        var ford = new Dealer { DealerId = 2, Name = "Ford" };
        var toyoto = new Dealer { DealerId = 3, Name = "Toyoto" };
        var volkswagen = new Dealer { DealerId = 4, Name = "Volkswagen" };
        var chevrolet = new Dealer { DealerId = 5, Name = "Chevrolet" };

        var civic = new Model { ModelId = 1, Name = "Civic", Dealer = honda };
        var fiesta = new Model { ModelId = 2, Name = "Fiesta", Dealer = ford };
        var corolla = new Model { ModelId = 3, Name = "Corolla", Dealer = toyoto };
        var passat = new Model { ModelId = 4, Name = "Passat", Dealer = volkswagen };
        var cruze = new Model { ModelId = 5, Name = "Cruze", Dealer = chevrolet };

        var magnus = new Customer { CustomerId = 1, FirstName = "Magnus", LastName = "Hedlund", Model = civic };
        var terry = new Customer { CustomerId = 2, FirstName = "Terry", LastName = "Adams", Model = fiesta };
        var charlotte = new Customer { CustomerId = 3, FirstName = "Charlotte", LastName = "Weiss", Model = corolla };
        var john = new Customer { CustomerId = 4, FirstName = "John", LastName = "Miller", Model = passat };
        var arlene = new Customer { CustomerId = 5, FirstName = "Arlene", LastName = "Huff", Model = cruze };

        var driver1 = new Driver { DriverId = 1, FirstName = "Fadi", LastName = "Fakhouri", Customer = magnus };
        var driver2 = new Driver { DriverId = 2, FirstName = "Hanying", LastName = "Feng", Customer = terry };
        var driver3 = new Driver { DriverId = 3, FirstName = "Cesar", LastName = "Garcia", Customer = charlotte };
        var driver4 = new Driver { DriverId = 4, FirstName = "Lint", LastName = "Tucker", Customer = magnus };
        var driver5 = new Driver { DriverId = 5, FirstName = "Robert", LastName = "Thomas", Customer = arlene };
        var driver6 = new Driver { DriverId = 6, FirstName = "David", LastName = "Adams", Customer = charlotte };

        var driver1Address = new DriverAddress { DriverAddressId = 1, AddressLine1 = "Main St", City = "Minnehaha", Zip = "57105", Driver = driver1 };
        var driver2Address = new DriverAddress { DriverAddressId = 2, AddressLine1 = "State St", City = "Los Angeles", Zip = "90034", Driver = driver2 };
        var driver3Address = new DriverAddress { DriverAddressId = 3, AddressLine1 = "Ralph St", City = "Winnebago", Zip = "61109", Driver = driver4 };

        List<Dealer> lstDealers = new List<Dealer> { honda, ford, toyoto, volkswagen, chevrolet };
        List<Model> lstModels = new List<Model> { civic, fiesta, corolla, passat, cruze };
        List<Customer> lstCustomers = new List<Customer> { magnus, terry, charlotte, john, arlene };
        List<Driver> lstDrivers = new List<Driver> { driver1, driver2, driver3, driver4, driver5, driver6 };
        List<DriverAddress> lstDriverAddress = new List<DriverAddress> { driver1Address, driver2Address, driver3Address };

        var result = from dealer in lstDealers
                     join model in lstModels on dealer.DealerId equals model.Dealer.DealerId
                     join customer in lstCustomers on model.ModelId equals customer.Model.ModelId
                     join driver in lstDrivers on customer.CustomerId equals driver.Customer.CustomerId into customerDriverGroup
                     from customerDriver in customerDriverGroup.DefaultIfEmpty(new Driver()) //defaultValue the empty constructor passed here
                     join address in lstDriverAddress on customerDriver.DriverId equals address.Driver.DriverId into driverAddressGroup
                     from driverAddress in driverAddressGroup.DefaultIfEmpty()
                     select new
                     {
                         Dealer = dealer,
                         Model = model,
                         Customer = customer,
                         Driver = customerDriver,
                         DriverAddress = driverAddress
                     };

        foreach (var v in result)
        {
            Console.WriteLine("{0,-15}{1,-15}{2,-15}{3,-15}{4}", v.Dealer.Name + ":",
                v.Model.Name + ":", v.Customer.FirstName + ":", v.Driver == null ? String.Empty : v.Driver.FirstName
                + ":", v.DriverAddress == null ? string.Empty : v.DriverAddress.City);
        }
        Console.Read();
    }
}
SelvaS
  • 2,105
  • 1
  • 22
  • 31
0

You should check if your select new {... } values are null

Like:

 select new
 {
     table1 = cec,
     table2 = ceclrp,
     table3 = lrp,
     table4 = (ecService == null ? string.Empty : ecService),
     table5 = (ecServiceEntitlement == null ? string.Empty : ecServiceEntitlement),
 }).AsNoTracking();
cadsjo
  • 106
  • 5
0

This might be the problem.

join cecse in _myContext.table5s on ecService.table4ID equals cecse.table4ID into cecseGroup

When ecService == null, ecService .table4ID will cause a Null reference exception

Can you try this instead.

join cecse in _myContext.table5s on (ecService == null?  0 : ecService.table4ID) equals cecse.table4ID into cecseGroup

you can change 0 to any integer value which is not used in _myContext.table4s.table4ID. So your query would look like this.

(from cec in _myContext.table1s
 join ceclrp in _myContext.table2s on cec.table1ID equals ceclrp.table1ID
 join lrp in _myContext.table3s on ceclrp.table3ID equals lrp.table3ID
 join cecs in _myContext.table4s on cec.table1ID equals cecs.table1ID into cecsGroup
 from ecService in cecsGroup.DefaultIfEmpty()
 join cecse in _myContext.table5s on (ecService == null?  0 : ecService.table4ID) equals cecse.table4ID into cecseGroup
 from ecServiceEntitlement in cecseGroup.DefaultIfEmpty()
 where cec.ClientKey == clientKey
 select new
 {
     table1 = cec,
     table2 = ceclrp,
     table3 = lrp,
     table4 = ecService,
     table5 = ecServiceEntitlement,
 }).AsNoTracking();
ughai
  • 9,830
  • 3
  • 29
  • 47
0

I would just expand the cecse variable into a left outer join too. Like how we would have done it in raw SQL too.

(from cec in _myContext.table1s
 join ceclrp in _myContext.table2s on cec.table1ID equals ceclrp.table1ID
 join lrp in _myContext.table3s on ceclrp.table3ID equals lrp.table3ID
 join cecs in _myContext.table4s on cec.table1ID equals cecs.table1ID into cecsGroup
 from ecService in cecsGroup.DefaultIfEmpty()
 from cecse in _myContext.table5s.DefaultIfEmpty()        // <---- here
 from ecServiceEntitlement in cecseGroup.DefaultIfEmpty()
 where cec.ClientKey == clientKey
    && ecService.table4ID equals cecse.table4ID           // <---- here
 select new
 {
     table1 = cec,
     table2 = ceclrp,
     table3 = lrp,
     table4 = ecService,
     table5 = ecServiceEntitlement,
 }).AsNoTracking();
justin.lovell
  • 675
  • 3
  • 11