IF statement is not working as expected - c#

My IF statement is not working as expected and it's bugging me big time. I'm not sure to what I'm doing wrong. Could someone please shed some light on this?
var _getData = db.EventTable.Where(x => x.EventID == id && x.Town == town).ToList();
if (_getData != null)
{
foreach (var e in _getData)
{
// some logic here to update the event etc
}
}
else
{
// some logic to create an event
}
My code never hits the else block when the _getData is null, I don't know why this is. I need it to hit the else block in order to create an event in the database.
What am I doing wrong?
Thank you

ToList() can't return null but empty list:
if (_getData.Any())
{
foreach (var e in _getData)
{
// some logic here to update the event etc
}
}
else
{
// some logic to create an event
}

Even if there are no rows that match the criteria, a list with no entries will be returned.
Solution:
if(_getData.Any())
{
...
To prevent similar errors in the future I strongly suggest naming variables like this:
var townEventsList = db.EventTable.Where(x => x.EventID == id && x.Town == town).ToList();
if (townEventsList.Any())
{
foreach (var townEvent in townEventsList)
{

Since you are calling ToList() it will have created a List which is being stored in your _getData variable.
Try checking for presence of items in this list by:
if (_getData.Any())
{
}

Because _getData will never be NULL. Entity Framework does not return NULL if no data is found. It will always return a list even with ZERO items.
You should check the length of the _eventData to find out if you got anything back or not.

Related

Linq Call - There is already an open DataReader associated with this Command which must be closed first

I have some code which when run an exception of type 'EntityCommandExecutionException' is raised.
The line which Visual Studio points to:
else if (item.FirstOrDefault().InspectionEquipmentTypes.Any())
The inner details of the exception say that:
There is already an open DataReader associated with this Command which must be closed first.
My question is the line which raised the error is not trying to use a database/datareader (to my knowledge) so I am unsure why this exception is being generated.
Edit:
public static IEnumerable<IGrouping<string,Entities.Inspection>> GetUnscheduledBatchInspections(Entities.EntityModel context)
{
var results = context.Inspections.Where(w =>
w.InspectionBatchNo != null
&& w.IsCancelled == false
&& !w.CalendarItems.Any()
&& w.Duration.HasValue).GroupBy(g => g.InspectionBatchNo);
return results;
}
Calling method:
private void MapBatchInspectionsToViewModel(ref SchedulerViewModel viewModel)
{
var batchInspections = SchedulerManager.GetUnscheduledBatchInspections(this.Context);
foreach (var item in batchInspections)
{
var bigi = new BatchInspectionGridItem();
if (item.Any())
{
bigi.BatchInspectionNo = item.First().InspectionBatchNo;
if (item.FirstOrDefault().EquipmentTypeID != null)
{
bigi.EquipmentTypeName = item.FirstOrDefault().EquipmentType.Description;
}
else if (item.FirstOrDefault().InspectionEquipmentTypes.Any())
{
bigi.EquipmentTypeName = string.Join(" / ", item.FirstOrDefault().InspectionEquipmentTypes.Select(s => s.EquipmentType.Description));
}
bigi.CustomerName = item.First().CustomerSite.Customer.CustomerName;
bigi.CustomerID = item.First().CustomerSite.Customer.CustomerID;
bigi.NumberOfInspections = item.Count();
bigi.TotalDuration = item.Sum(s => s.Duration);
}
viewModel.BatchInspectionGridViewModel.Add(bigi);
}
}
Here's what happens: while you loop through batchInspections the database reader is reading this collection from the database. Within the loop you do new database reads by the numerous First(OrDefault) calls, the Sum and the Count. That causes the exception 'There is already an open DataReader...'.
As said by George Lica, you can probably solve this by setting MultipleActiveResultSets=True in your connection string.
Or you can finish reading batchInspections before the loop starts itereating by...
foreach (var item in batchInspections.ToList())
But it is far more efficient to first collect the data you're going to need and then loop through them:
foreach (var item in batchInspections
.Select(b => new
{
First = b.FirstOrDefault(),
Count = b.Count(),
Sum = b.Sum(s => s.Duration)
} )
.ToList())
{
var bigi = new BatchInspectionGridItem();
if (item.Any())
{
bigi.BatchInspectionNo = item.First.InspectionBatchNo;
if (item.First.EquipmentTypeID != null)
{
bigi.EquipmentTypeName = item.First.EquipmentType.Description;
}
else if (item.First.InspectionEquipmentTypes.Any())
{
bigi.EquipmentTypeName = string.Join(" / ", item.First.InspectionEquipmentTypes.Select(s => s.EquipmentType.Description));
}
bigi.CustomerName = item.First.CustomerSite.Customer.CustomerName;
bigi.CustomerID = item.First.CustomerSite.Customer.CustomerID;
bigi.NumberOfInspections = item.Count;
bigi.TotalDuration = item.Sum;
}
viewModel.BatchInspectionGridViewModel.Add(bigi);
}
I hope that SchedulerManager.GetUnscheduledBatchInspections returns an IQueryable, so that the subsequent Select into the anonymous type will be translated into SQL.
It must be said though that activating MARS is nearly always a good idea with Entity Framework, because lazy loading has a way of causing this exception.
This happens when you make queries in a nested way.
item.FirstOrDefault().InspectionEquipmentTypes.ToList().Any()
may work. I am not sure though. Try simplifying the nested queries. For example don't make queries like:
items.Where(/*some condition*/).Any();
instead make
items.Any(/*some condition*/);
If you really want to have nested queries ( i don't recommend that, i would rather do separate queries and link entities using some hashing data structures) and you are using sql server, you actually have an alternative: activate MARS. To activate it, just add in your connection string MultipleActiveResultSets=True. For more details follow this link : https://msdn.microsoft.com/en-us/library/h32h3abf(v=vs.110).aspx

Search a table with a lot of columns and then group the result with EntityFramework

I have an Asp.Net MVC 5 website and I'm using Entity Framework code first to access its database. I have a Restaurants table and I want to let users search these with a lot of parameters. Here's what I have so far:
public void FilterModel(ref IQueryable<Restaurant> model)
{
if (!string.IsNullOrWhiteSpace(RestaurantName))
{
model = model.Where(r => r.Name.ToUpper().Contains(RestaurantName));
}
if (Recommended)
{
model = model.Where(r => r.SearchSponsor);
}
//...
}
Basically I look for each property and add another Where to the chain if it's not empty.
After that, I want to group the result based on some criteria. I'm doing this right now:
private static IQueryable<Restaurant> GroupResults(IQueryable<Restaurant> model)
{
var groups = model.GroupBy(r => r.Active);
var list = new List<IGrouping<bool, Restaurant>>();
foreach (var group in groups)
{
list.Add(group);
}
if (list.Count < 1)
{
SortModel(ref model);
return model;
}
IQueryable<Restaurant> joined, actives, inactives;
if (list[0].FirstOrDefault().Active)
{
actives = list[0].AsQueryable();
inactives = list.Count == 2 ? list[1].AsQueryable() : null;
}
else
{
actives = list.Count == 2 ? list[1].AsQueryable() : null;
inactives = list[0].AsQueryable();
}
if (actives != null)
{
//....
}
if (inactives != null)
{
SortModel(ref inactives);
}
if (actives == null || inactives == null)
{
return actives ?? inactives;
}
joined = actives.Union(inactives).AsQueryable();
return joined;
}
This works but it's got a lot of complications which I rather not talk about for the sake of keeping this question small.
I was wondering if this is the right and efficient way to do it. It seems kind of "dirty"! Lots of ifs and Wheres. Stored procedures, inverted indices, etc. This is my first "big" project and I want to learn from your experience to do this the "right" way.
Looking at the GroupResults Method I get a little confused about what you are doing. It seems the intention is to receive an arbitrary list of restuarants and return an ordered list of restaurants ordered by Active and some other criteria.
If thats true you may just do something like this and your job's done:
model.OrderBy(x => x.Active).ThenBy(x => Name);
If SortModel is somehow more sophisticated you may either add a comparer to the statement or stick with your current solution but change it to this:
if (model == null || !model.Any())
{
return model;
}
var active = model.Where(x=>x.Active);
var inactives = model.Where(x=>!x.Active);
// if (inactives == null) //not needed as where always return at least an empty list. Mabye check for inactive.Any()
SortModel(ref inactives); //You may also remove the ref as it's an reference anyway
joined = actives.Union(inactives).AsQueryable();
return joined;
Regarding the way you are handling your searching, I think it is simple, easy to read and understand, and it works. New team members will be able to look at that code and know immediately what it is doing and how it works. I think that is a pretty good indication that your approach is sound.

Checking to see if string exists in db using linq

Here's my attempt:
public void ReadLot(LotInformation lot)
{
try
{
using (var db = new DDataContext())
{
var lotNumDb = db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(r.lot_number));
if (lotNumDb.lot_number != null && lotNumDb.lot_number.Length == 0)
{
Console.WriteLine("does not exist. yay");
var lotInfo = db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(lotNumber));
}
else if (lotNumDb.lot_number.ToString().Equals(lot.lot_number))
{
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
}
Here what I want to do:
When the user uploads a file, I check if the deserialized string from memory is a duplicate in the database or not. If it is, pop up a dialog box saying it's a duplicate/already exists and have nothing happen afterward. If it is not a duplicate, proceed with application. Also, if the column in the table in the database is null, store the lot number there and proceed.
I noticed a few things. If the database is empty and I run the above, I get a null exception because I'm trying to find a lot number in db that is not there. How do I change the code above so that if I check in db and the column is null, then just add the number and not throw an exception when comparing. I think that might be the only problem right now.
I'm not sure what this is supposed to be doing, but you don't need it:
var lotNumDb =
db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(r.lot_number));
Instead, just check for the existance of the lot_number passed to the method, and use Any to determine whether there were any matches. If it returns true, then the lot number is already in the database.
// Check for duplicates
var isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (isDuplicate)
{
// Inform user that the lot_number already exists
return;
}
Console.WriteLine("does not exist. yay");
// Store the lot_number in the database
bool lotNumDbExists = db.LotInformation(r => r.lot_number.Equals(r.lot_number)).Any;
or .exists
This should return either a true or false of if it exists.

Object reference not set to an instance of an object - C# Error

I know this gets asked a lot and I have read through similar posts but I can't figure my error out. I am getting the Object reference not set to an instance of an object error with a .aspx.cs file.
Here is a snippet of my code...the error is thrown by the String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id.ToString()) line.
Note: context is declared further up in my code and the enduserDropdown is a control I created using ASP.
foreach (var accountInCrm in context.AccountSet.ToList())
{
if (accountInCrm.StateCode == 0)
{
if (String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id.ToString()))
{
enduserDropdown.Items.Add(new ListItem(accountInCrm.Name, accountInCrm.Id.ToString()));
}
I know that accountInCrm is not empty and If I just remove the String.IsNullOrEmpty If statement all is well, and values are added to my dropdown.
Any ideas what would be causing this?
EDIT:------------------
foreach (var accountInCrm in context.AccountSet.ToList())
{
if (accountInCrm.StateCode == 0)
{
if (accountInCrm.ParentAccountId != null)
{
enduserDropdown.Items.Add(new ListItem("test 2", accountInCrm.Id.ToString()));
}
else
{
enduserDropdown.Items.Add(new ListItem("test 3", accountInCrm.Id.ToString()));
}
}
}
EDIT 2:-------------retrieving data from CRM
var context = new XrmServiceContext();
foreach (var accountInCrm in context.AccountSet.ToList())
The loop goes through each account record in CRM. I know for sure that not all of the accounts in CRM have blank Parent Account Id's, some do but the majority don't.
Your ParentAccountId is probably null:
if (accountInCrm.ParentAccountId != null &&
String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id.ToString()))
{
...
}
Your ParentAccountID could be null and thus you need to check for it.
if (accountInCrm.ParentAccountId != null && String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id))
{
...
}
Also keep in mind that you should not try to convert ID to a string.
If ID is already a string then there is no point in doing a ToString on it and, if it is null, calling ToString on a null reference will crash again your code.
If ID is not a string then you should probably not call String.IsNullOrEmpty on a non string object.
You're doing your Linq To CRM all wrong by doing all of your filtering on the client side. You also want to only select the values that you actually need:
foreach(var account in AccountSet.Where (a => a.StateCode.Value == 0 && a.ParentAccountId != null).
Select(a => new Account { Name = a.Name, AccountId = a.AccountId})
{
enduserDropdown.Items.Add(new ListItem(accountInCrm.Name, accountInCrm.Id.ToString()));
}
As long as enduserDropdown is not null that shouldn't be an issue.

Element not getting added in List

I have a class called Estimate and it has the following field and property:
private IList<RouteInformation> _routeMatrix;
public virtual IList<RouteInformation> RouteMatrix
{
get
{
if (_routeMatrix != null && _routeMatrix.Count > 0)
{
var routeMatrix = _routeMatrix.ToList();
routeMatrix =
routeMatrix.OrderBy(tm => tm.Level.LevelType).ThenBy(tm => tm.Level.LevelValue).ToList();
return routeMatrix;
}
else return _routeMatrix;
}
set { _routeMatrix = value; }
}
So, in the getter method, I am just sorting the _routeMatrix by Level Type and then by Level Value and returning the sorted list.
In one of my programs, I have the following code:
public void SaveApprovers(string[] approvers)
{
int i = 1;
foreach (var approver in approvers)
{
var role = Repository.Get<Role>(long.Parse(approver));
var level = new Models.Level
{
LevelType = LevelType.Approver,
LevelValue = (LevelValue)i,
Role = role
};
Repository.Save(level);
var routeInformation = new Models.RouteInformation
{
Level = level,
RouteObjectType = RouteObjectType.Estimate,
RouteObjectId = _estimate.Id
};
Repository.Save(routeInformation);
_estimate.RouteMatrix.Add(routeInformation); // <--- The problem is here
Repository.Save(_estimate);
i++;
}
}
The problem is that, if there are multiple approvers (i.e: the length of the approvers array is greater than 1, only the first routeInformation is added in the RouteMatrix. I don't know what happen to the rest of them, but the Add method doesn't give any error.
Earlier, RouteMatrix was a public field. This problem started occuring after I made it private and encapsulated it in a public property.
Your get member returns a different list, you add to that temporary list.
get
{
if (_routeMatrix != null && _routeMatrix.Count > 0)
{
var routeMatrix = _routeMatrix.ToList(); // ToList creates a _copy_ of the list
...
return routeMatrix;
}
else return _routeMatrix;
}
.....
_estimate.RouteMatrix.Add(routeInformation); // add to the result of ToList()
I think the moral here is not to make getters too complicated. The sorting is wasted effort anyway when you just want to Add().
Also, bad things will happen when _routeMatrix == null. That may not happen but then the if (_routeMatrix != null && ...) part is misleading noise.
When you are applying ToList() then completely new list is created, which is not related to original _routeMatrix list. Well, they share same elements, but when you add or remove elements from one of lists, it does not affect second list.
From MSDN:
You can append this method to your query in order to obtain a cached
copy of the query results.
So, you have cached copy of your _routeMatrix which you are successfully modifying.
To solve this issue you can return IEnumerable instead of IList (to disable collection modifications outside of estimation class), and create AddRouteInformation method to estimation class which will add route information to _routeMatrix. Use that method to add new items:
_estimate.AddRouteInformation(routeInformation);
Repository.Save(_estimate);
The problem is that you're not actually modifying _routeMatrix, you're modifying a copy of it. Don't issue the ToList on _routeMatrix, just sort it. Change the get to this:
get
{
if (_routeMatrix != null && _routeMatrix.Count > 0)
{
_routeMatrix =
_routeMatrix.OrderBy(tm => tm.Level.LevelType).ThenBy(tm => tm.Level.LevelValue).ToList();
return _routeMatrix;
}
else return _routeMatrix;
}

Categories