Why am I NOT getting a Null Reference Exception? - c#

I'm using LINQ to Entities to get some data from a database. Below is my query.
var location = from l in dbContext.Locations
join e in dbContext.Equipment on l.ID equals e.LocationID into rs1
from e in rs1.DefaultIfEmpty()
where ids.Contains(l.ID)
select new
{
EquipmentClass = e,
LocationID = l.ID,
LocationName = l.Name,
EquipmentName = e == null ? null : e.Name,
Description = e == null ? null : e.Description,
InServiceStatus = e == null ? false : e.InServiceStatus,
EquipmentType = e.EquipmentType.Name
};
foreach (var item in location)
{
// some logic
}
In the code above, ids is a list of ints that I pass in to filter the results. When I get the results, I see that one of the return records has a null EquipmentClass. I had performed some null checking but realized that I forgot to do null checking on one of the properties. Now I would expect to get a null reference exception on EquipmentType = e.EquipmentType.Name but I don't. To my surprise, it works just fine, and is set to null. My EquipmentClass has a property type of EquipmentType, which is another class. EquipmentType has a Name property which is a String.
Why doesn't this throw a null reference exception?
Just as a test, I removed the null check from InServiceStatus = e == null ? false : e.InServiceStatus and it fails with an invalid operation exception upon running a foreach loop using the query.
Does this mean that I only need to do null checking on non-nullable values?
Update:
foreach (var item in location)
{
var p = item.EquipmentClass.EquipmentType.Name;
}
Added this right after the query. On the assignment of p, I get a null reference exception. I'm not sure how it even gets that far, as it should fail on the first line of the foreach loop. Without the line declaring variable p, I do not get a null reference exception. If anyone can explain what is happening I would be grateful. Just for reference, the values of item.EquipmentClass and item.EquipmentType are both null by the time the foreach loop starts.
Update2:
I found this link where it seems that someone has an almost identical issue using LINQ to SQL. I get the gist of the answer but don't fully understand its potential impact on my two questions above.

If you write a LINQ query against IQueryable, what happens is that the methods that you are calling behind the scenes (such as Select, Where, etc.) don't do anything more than just recording how you have called them, i.e. they record the predicate expressions and carry over a LINQ provider. As soon as you start iterating the query, the provider is asked to execute the query model. So basically, the provider uses the expression model to give you a result of the expected type.
The provider is by no means required to actually compile or even execute the code (model) you delivered as an expression. In fact, the whole point of LINQ to SQL or LINQ to Entities is that the provider does not do this and translate the code expression to SQL instead.
Therefore, your query is actually rendered as an SQL query and the result of that query is translated back. Therefore, the variable e that you see in the query is not necessarily really created but only used for the LINQ provider to compile an SQL query. However, most database servers have null propagations.
Run the same query against LINQ to Objects and you will get your missing NullReferenceException.

Your update helped me understand your real concern. The concept you need to know in regards to LINQ queries is deferred execution in LINQ.
Please go through below links for more details:
What are the benefits of a Deferred Execution in LINQ?
Linq - What is the quickest way to find out deferred execution or not?
Now what happens in your case? You've stored your query in location variable. That particular step is just the initialization part. It doesn't really executes the query on your database through your ORM layer. This is how you can test this.
Put a break point on the line of code where you're initializing location variable with LINQ query. When debugger stops in Visual Studio, then go to SQL Server Management Studio (SSMS) and start a SQL Server Profiler session.
Now press F10 in Visual Studio to step over the code statement. At this point of time you'll see absolutely no query execution in profiler session as shown below:
That is all because LINQ query didn't get executed at all till this point of time.
Now you reach to your below line of code:
foreach (var item in location)
{
var p = item.EquipmentClass.EquipmentType.Name;
}
The moment you enter inside foreach loop, the LINQ query gets fired and you'll see the corresponding log in trace session in SQL Server profiler. So a LINQ query doesn't get fired unless it is being enumerated. This is called deferred execution i.e. the runtime defers the execution until enumeration. If you never enumerate location variable in your code then query execution will never happen at all.
So to answer your query, exception will come only when query gets fired. Not before that!
Update 1: You're saying that - I do not get a null reference exception. Yes! you will not get null reference exception until you reach to the record whose corresponding RHS joined record is missing. Have a look at below code for better understanding:
class Program
{
static void Main(string[] args)
{
var mylist1 = new List<MyClass1>();
mylist1.Add(new MyClass1 { id = 1, Name1 = "1" });
mylist1.Add(new MyClass1 { id = 2, Name1 = "2" });
var mylist2 = new List<MyClass2>();
mylist2.Add(new MyClass2 { id = 1, Name2 = "1" });
var location = from l in mylist1
join e in mylist2 on l.id equals e.id into rs1
from e in rs1.DefaultIfEmpty()
//where ids.Contains(l.ID)
select new
{
EquipmentClass = e,
InServiceStatus = e == null ? 1 : e.id,
EquipmentType = e.id
};
foreach (var item in location)
{
}
}
}
class MyClass1
{
public int id { get; set; }
public string Name1 { get; set; }
}
class MyClass2
{
public int id { get; set; }
public string Name2 { get; set; }
}
So, Now when I start iterating the location variable, it doesn't breaks in first iteration. It breaks in second iteration. It breaks when it fails to obtain the record/object corresponding to MyClass1 object having id 2 present in mylist1. mylist2 doesn't have any object with id 2. Hope this helps!

your e.EquipmentType.Name is null and it is getting assigned to EquipmentType and it is perfectly fine to assign null to a nullable type and you are using DefaultIfEmpty() which will initialize the elements with their default value if it fails to match any condition and in this case your e.ElementType.Name is getting set to null which is fine I guess. use ToList() that will throw the exception.
I hope I am making some sense and you people might have discussed this.

Related

C# - Unable to create a constant value of type

I have this error
Unable to create a constant value of type 'Controllers.Administrator'. Only primitive types or enumeration types are supported in this context.
I have a variable List<Administrator> result that is getting it's values from Database.SqlQuery<Administrator>.
Administrator class looks like this
public class Administrator
{
public string AdminName { get; set; }
public string Numer { get; set; }
}
Than I have something like this
var sheet = from k in Context.Admins.AsNoTracking()
select new Admin()
{
// some other variables
Numer = k.Numer,
AdminName = result.FirstOrDefault(a => a.Numer == k.Numer).AdminName ?? String.Empty
};
AdminName line is giving me the error, why can't it work like this?
I'm trying to get a value from result into sheet that matches by the Numer.
Your problem is that the whole query is being sent to SQL Server, and when it gets to the bit where you try to create an Admin object, SQL Server goes "Huh? What's one of those?"
If you enumerate your query before that point, then the results are sent back from SQL Server to your code, where the Admin object can be created...
var sheet = Context.Admins.AsNoTracking()
.ToList()
.Select(a => new Admin()
{
// some other variables
Numer = k.Numer,
AdminName = result.FirstOrDefault(a => a.Numer == k.Numer).AdminName ?? String.Empty
});
I've converted your code to use method syntax, as I think it's easier to read for this sort of thing.
Either way, the point is to understand what bit of code gets executed where. Anything to do with your models must be executed in your code, not in the database, as that doesn't know about your models. The call to ToList() enumerates the query (ie actually runs it against the database). Everything after that then happens in your code.
As a side point, you should really be using async code for this sort of thing...
var sheet = (await Context.Admins.AsNoTracking()
.ToListAsync())
//... other code as before

Value cannot be null, r nparameter name source

I am confused. I have two statements that are the same and one works and the other receives the error - value cannot be null. r nparameter name source. From what I read I am receiving the error because something is null in my linq expression. However from what I can tell nothing is null.
The first if statement works. When a person selects 'Select' from a list of Burn Project a list of BurnPiles is displayed below the BurnProject list. (this works). Then when a person selects 'Select' from the list of BurnPiles a list of RequestedBurns is display below it. This gives me the null error. At one time it did work now it doesn't.
I am at a loss of what went wrong. I do know the RequestedBurn Table starts at record #2 but that shouldn't have anything to do with it. The BurnPile records that I have been using have associated RequestedBurns.
//when 'Select' is chosen from the list of burn projects the list of burn piles
//associated with that specific burn project is display below it.
if (burnerID != null)
{
ViewBag.BurnerID = burnerID.Value;
viewModel.BurnPiles = viewModel.BurnProjects.Where(
b => b.BurnerID == burnerID.Value).Single().BurnPiles;
}
//when 'Select' is chosen from the list of burn piles the list of requested
//burns associated with that specific burn pile is displayed below it.
if (burnPileID != null)
{
ViewBag.BurnPilesID = burnPileID.Value;
viewModel.RequestedBurns = viewModel.BurnPiles.Where(
x => x.BurnPilesID == burnPileID).Single().RequestedBurns;
}
If you look at documentation for Where or Single, you would see that source is the name of the parameter that represents your collection. So, it looks like you are trying to call a method on null reference, which would be the case if viewModel.BurnProjects = null or viewModel.BurnPiles = null.
viewModel.BurnPiles = viewModel.BurnProjects.Where(
b => b.BurnerID == burnerID.Value).Single().BurnPiles;
could be setting viewModel.BurnPiles to null.
or
viewModel.BurnPiles.Where(
x => x.BurnPilesID == burnPileID).Single()
is returning nothing so when you try and access RequestedBurns then it throws an exception.
SingleOrDefault also has an overload where you can simplify the expression a bit more. You can also combine it with the null conditional operator (if using at least C# 6).
if (burnerID != null)
{
ViewBag.BurnerID = burnerID.Value;
viewModel.BurnPiles = viewModel.BurnProjects.SingleOrDefault(b => b.BurnerID == burnerID.Value)?.BurnPiles;
}

IEnumerable Select statement with ternary operator

I have an odd behavior using an IEnumerable<string> with a ternary operator and a Select statement.
I have two lists with different objects. One list contains Enums the other list contains objects. Those objects do have a String property.
If one list is null or empty I want to get the values of the other list.
Here is some code:
public class ExportItem
{
public string Type;
...
}
public enum ExportType
{
ExportType1,
ExportType2,
...
}
The List<ExportItem> is always filled by a config file. The List<ExportType> is filled if command line arguments are provided. So if List<ExportType> is filled I want to use them, otherwise I want to use those from the config file.
So my code ist like this:
IEnumerable<string> exportTypes = MyListOfExportTypes != null &&
MyListOfExportTypes.Any() ? MyListOfExportTypes.Select(x => x.ToString()) :
MyListOfExportItems.Select(x => x.Type);
The thing is that exportTypes is null but I don't get it...
When I do this with if-else everything works as expected. Also if exportTypes is of type List<string> and I call ToList() after the Select statement everything works fine.
Using var a = MyListOfExportTypes.Select(x => x.ToString()); and var b = MyListOfExportItems.Select(x => x.Type); does work as expected.
Must be something with the ternary operator and/or IEnumerable. But what?
Or what do I miss? Any suggestions?
EDIT:
I now have a screenshot...
Note that the code above foreach works nevertheless...
Not sure if this was answered,
But I think that this is related to the fact that you are using LINQ deferred execution.
When writing LINQ queries,
there is a difference between creating the query and executing it.
Writing the select statement, is creating the query, adding ToList() executes it.
Think of it like writing SQL query in SQL server console (that's the writing stage),
and once you hit F5 (Or the play button) you execute it.
I hope this little code sample will help to clarify it.
public class SomeClass
{
public int X { get; set; }
public int Y { get; set; }
public void Test()
{
//Here I'm creating a List of Some class
var someClassItems = new List<SomeClass> {
new SomeClass { X = 1, Y = 1 },
new SomeClass { X = 2, Y = 2 }
};
//Here I'm creating a Query
//BUT, I'm not executing it, so the query variable, is represented by the IEnumerable object
//and refers to an in memory query
var query = someClassItems.
Select(o => o.X);
//Only once the below code is reached, the query is executed.
//Put a breakpoint in the query, click the mouse cursor inside the select parenthesis and hit F9
//You'll see the breakpoint is hit after this line.
var result = query.
ToList();
}
}

Exception raised when using a Linq query with Entity Framework

I removed a substantial amount of text from this question because I feel it needed to be more succinct. - Serg
Here's what I'm trying to do. User selects a State, then a ComboBox loads with Cities in that State, then when user selects a Сity a ListBox loads all projects from that City.
When running the method that's supposed to load the projects to the ComboBox, I receive this error:
Exception has been thrown by the
target of an invocation. Inner
Exception is: "{"Specified cast is not
valid."}"valid."}"
This is the method that causes the exception:
private void LoadProjectsToListBox(long idCity)
{
ProjectRepository projectRepo = new ProjectRepository();
//This line causes the error.
var projects = projectRepo.FindAllProjects().Where(c => c.IDCity == 1).ToList();
}
In the Linq query, you'll see that I'm comparing against a hard coded number. 1, is a valid ID of a City that has Projects so it should work. The exception is only raised when I give the Linq query a City ID that has Projects. So it seems the error is raised when the query returns results.
Here is the SQL statements I used to generate the Project and City tables:
create table City
(
ID integer primary key autoincrement,
Name string,
IDState integer references State(ID)
);
create table Project
(
ID integer primary key autoincrement,
Name string,
StartDate text,
IDManager integer references Manager(ID),
IDCity integer references City(ID),
IDDepartment integer references Department(ID),
ContactNumber string,
Description string
);
What strikes me as odd is that using the exact same pattern and access code to show a list of Departments (just another table in my database) everything works as expected. For example here's how I load Departments right now just for testing:
private void LoadProjectsToListBox(long idCity)
{
ProjectRepository projectRepo = new ProjectRepository();
DepartmentRepository departmentRepo = new DepartmentRepository();
//Doesn't work - raises exception.
var projects = projectRepo.FindAllProjects().Where(c => c.IDCity == 1).ToList();
//Works exactly as expected.
var deps = departmentRepo.FindAllDepartments().Where(c => c.IDParentDepartment == 7).ToList();
lstProjects.Items.Clear();
foreach (var item in deps)
{
lstProjects.Items.Add(item.Name);
}
}
Edit:
The FindAllDepartments(), FindAllCities(), FindAllProjects() methods are essentially the same.
DocumentsDBEntities db = new DocumentsDBEntities();
public IQueryable<Project> FindAllProjects()
{
return db.Projects;
}
public IQueryable<City> FindAllCities()
{
return db.Cities;
}
public IQueryable<Department> FindAllDepartments()
{
return db.Departments;
}
As mentioned before, Department fetching works; Project fetching causes the exception.
Edit 3:
I fixed it! The problem was with using the datatype of 'Text' in the Project table. If I changed the datatype from that column from 'Text' to 'String' it works flawlessly. Can anyone tell me why?
I had the exact same problem last week! In my case the problem was that some of the tables I had pulled over to the .dbml file had a different database connection (test vs production) and even though the tables were (conceptually) the same, it was impossible to cast a table of type 'db1.table1' to 'db2.table1'. I don't know if that is the root of your problem or not, but I hope this helps in some way. Making sure that all my tables were coming from the exact same database connection was what solved it for me.
According to what I see here, Text datatype doesn't allow comparison with == (or OrderBy, for that matter). Unless I misunderstood the docs.
You most start your query with city (up to down):
var projects = projectRepo.FindAllCities()
.Where(c => c.ID == 1)
.Select(p => p.Projects)
.ToList();

Why does LINQ query throw an exception when I attempt to get a count of a type

public readonly IEnumerable<string> PeriodToSelect = new string[] { "MONTH" };
var dataCollection = from p in somedata
from h in p.somemoredate
where h.Year > (DateTime.Now.Year - 2)
where PeriodToSelect.Contains(h.TimePeriod)
select new
{
p.Currency,
h.Year.Month, h.Value
};
Can someone tell me why an exception is thrown when at the following line of code?
int count = dataCollection.Count();
This is the exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Linq.Enumerable.<SelectManyIterator>d__31`3.MoveNext()
at System.Linq.Enumerable.<SelectManyIterator>d__31`3.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at ...
This looks like a normal null reference exception in linq2objects while it tries to execute your predicates or projections.
The cases were you'd get a null ref exception that I can think of are if some elements of the "somedata" collection are null, if "h.Year" is null (what type is that?), or if "p.somemoredate" is null..
Deferred execution strikes again!
(First off, my first guess is that this is caused by p.somemoredate being null somewhere in your collection.)
Given your example, there's no way for us to really know, since you've simplified away the bits that are being queried. Taking it at its face, I would say that whatever "somedata" or "somemoredate" are the things you need to look at.
To figure this out, (when I get really desperate) I split the query into parts and watch where exceptions get thrown. Notice the .ToArray() calls which will basically "stop" deferred execution from happening temporarily:
var sd = somedata.ToArray();
var x = (from p in sd from h in p.somemoredate.ToArray()).ToArray(); //My guess is that you'll get your exception here.
Broken up like this, it's a lot easier to see where the exception gets thrown, and where to look for problems.
The exception is thrown at the Count() statement because LINQ uses deferred execution and the actual LINQ query will not be executed until to call .Count(), .ToList(), etc.
I ran into the same issue. It is annoying that MS did not provide built-in null detection and handling for the aggregate functions. The other issue is I wanted to ensure I got a 0 or $0 return result for nulls/empty query results, as I was working on dashboards/reporting. All of those tables would have data eventually, but early on you get a lot of null returns. After reading multiple postings on the subject I came up with this:
Retrieve the fields you want to return or later apply aggregate functions to first.
Test for a null return. Return 0 if a null is detected.
If you do get actual data returned then you can safely utilize/apply the Count or Sum aggregate Linq functions.
public ActionResult YourTestMethod()
{
var linqResults = (from e in db.YourTable
select e.FieldYouWantToCount);
if (linqResults != null)
{
return Json(linqResults.ToList().Count(), JsonRequestBehavior.AllowGet);
}
else
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
Sum Example Below
public ActionResult YourTestMethod()
{
var linqResults = (from e in db.YourTable
select e.Total);
if (linqResults != null)
{
return Json(linqResults.ToList().Sum(), JsonRequestBehavior.AllowGet);
}
else
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}

Categories