Exception when using LINQ SUM - linq

I'm trying to get the SUM of "bookings" and I get error "The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
var bookings = entities.Bookings.Where(x => x.ID == id &&
x.StartDate <= bookingEnd &&
x.EndDate >= bookingStart)
.Sum(x => x.BookingQuantity);
How shall I fix this? I need to get 0 if it ever gets to be null else its bookings.

Try the null coalescing operator:
var bookings = entities.Bookings.Where(x => x.ID == id &&
x.StartDate <= bookingEnd &&
x.EndDate >= bookingStart &&
x.BookingQuantity != null)
.Sum(x => (int?)x.BookingQuantity) ?? 0;
or declare bookings as a nullable int
int? bookings = ...
The compilers type inference is picking up the result of Sum as a plain int, which should never be null.

This page suggests a fix to this problem;
Sum(x => (int?)x.BookingQuantity) ?? 0;

Add checking for null.
var bookings = entities.Bookings.Where(x => x.ID == id &&
x.StartDate <= bookingEnd &&
x.EndDate >= bookingStart &&
x.BookingQuantity != null)
.Sum(x => x.BookingQuantity);

Related

LINQ EF AND VS2017

I wrote a query and worked on LINQPAD
from x in FacilityData
from y in FavInformation
where y.UserID == 1 && x.ID == y.FacilityID
select new
{
xID = x.ID,
xDistrictName = (from y in _Ilcelers
where y.ID == x.DistrictID
select y.IlceAd).FirstOrDefault(),
xName = x.Name,
Value = (from o in Tags
from p in Table_tags
where o.Prefix != null && o.Prefix == p._NAME && o.Facility == y.FacilityID
orderby p.İd descending
select new
{
FType = o.TagType,
Name = o.TagsName,
Value = p._VALUE,
Time = p._TIMESTAMP
}).Take(Tags.Count(h => h.Facility == y.FacilityID))
}
result
the result is perfect
but does not work in visual studio,
Value = (from o in DB.Tags
from p in DB.table_tags
where o.Prefix != null && o.Prefix == p.C_NAME && o.Facility == 11
orderby p.id descending
select new
{
FType=o.TagType,
Name = o.TagsName,
Value = p.C_VALUE,
Time = p.C_TIMESTAMP
}).Take(Tags.Count(h => h.Facility == y.FacilityID))
and it gives an error.
I guess the part with .Take() doesn't work because it's linq to EF.
error:
Limit must be a DbConstantExpression or a Db Parameter Reference Expression. Parametre name: count]
error image
thank you have a good day
Not sure but I will just throw it in. If you are talking about linq to ef/sql, it is possible they dont know a thing about C#. If take() would be the problem try to get the select result local first by doing .tolist(). Afterwards use your take funtion.
.ToList().Take(Tags.Count(h => h.Facility == y.FacilityID))

Linq statement Where within a Where

I am building a query tool for use by non technical staff to retrieve records from the database.
I have a form with various drop downs which can be selected by the user depending on what they are looking for.
I have come across a problem where my query is returning records that do not match the users selection.
I believe this is only happening when I am querying the joined tables.
I have the following:
results = results.Where(c => c.CustomerEnrollment
.Where(x => x.CustomerCategoryID == CustomerCategoryID)
.Any());
results = results.Where(c => c.CustomerEnrollment
.Where(x => x.StartDate <= DateRangeStart && x.EndDate >= DateRangeStart)
.Any());
This will return results for the correct category but not within the specified date range.
I have also tried:
results = results.Where(c => c.CustomerEnrollment
.Any(x => x.CustomerCategoryID == CustomerCategoryID));
Try changing your date range check as;
Change:
x => x.StartDate <= DateRangeStart && x.EndDate >= DateRangeStart
To:
//StartDate should be greater than or equal to
//EndDate should be less than or equal to
//Also you are using same variable DateRangeStart to check start and end
x => x.StartDate >= DateRangeStart && x.EndDate <= DateRangeEnd
Your query returns categories that have any CustomerEnrollment having their Id, and also have any CustomerEnrollment in the the required data range, but these CustomerEnrollments are not necessarily the same.
To make sure that you get categories with CustomerEnrollments that fulfill both conditions you have to do:
results = results.Where(c => c.CustomerEnrollment
.Where(x => x.CustomerCategoryID == CustomerCategoryID
&& x.StartDate <= DateRangeStart
&& x.EndDate >= DateRangeStart)
.Any());
With PredicateBuilder you can parametrize the conditions:
using LinqKit;
...
var pred = Predicate.True<CustomerEnrollment>();
if (CustomerCategoryID > 0)
pred = pred.And(c => c.CustomerCategoryID == CustomerCategoryID);
if (DateRangeStart.HasValue)
pred = pred.And(c => c.StartDate <= DateRangeStart
&& c.EndDate >= DateRangeStart);
results = results.AsExpandable()
.Where(c => c.CustomerEnrollment.AsQueryable()
.Any(pred));
The combination of .AsExpandable() and .AsQueryable() appears to be the only way to avoid exceptions.

Entity Framework/ Linq - groupby and having clause

Given the query below
public TrainingListViewModel(List<int> employeeIdList)
{
this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday() &&
x.CancelDate.HasValue == false &&
x.OtherLeaveId == Constants.TrainingId)
.OrderBy(x => x.OtherLeaveDate)
.Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
}).ToList();
}
I want to put in the employeeIdList into the query.
I want to retrieve all of the x.OtherLeaveDate values where the same x.OtherLeaveDate exists for each join where x.EmployeeId = (int employeeId in employeeIdList)
For example if there are EmployeeIds 1, 2, 3 in employeeIdList and in the CacheObjects.AllEmployeeOtherLeaves collection there is a date 1/1/2001 for all 3 employees, then retreive that date.
If I read you well it should be something like
var grp = this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId)) // courtesy #IronMan84
.GroupBy(x => x.OtherLeaveDate);
if (grp.Count() == 1)
{
var result = g.First().Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
})
}
First the data is grouped by OtherLeaveDate. If the grouping results in exactly one group, the first (and only) IGrouping instance is taken (which is a list of Leave objects) and its content is projected to EmployeeOtherLeaveItems.
To the where statement add "&& employeeIdList.Contains(x.EmployeeId)"
I need to thank #IronMan84 and #GertArnold for helping me along, and I will have to admonish myself for not being clearer in the question. This is the answer I came up with. No doubt it can be improved but given no one has responded to say why I will now tick this answer.
var numberOfEmployees = employeeIdList.Count;
var grp = CacheObjects.AllEmployeeOtherLeaves.Where(
x =>
x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId))
.GroupBy(x => x.OtherLeaveDate)
.Select(x => new { NumberOf = x.Count(), Item = x });
var list =
grp.Where(item => item.NumberOf == numberOfEmployees).Select(item => item.Item.Key).ToList();

How to programmatically set label text - Linq To Entities

I have to set the label text of a load of differnet labels on a web page based upon user preferences.
I have the following code that is place upon the label load event (probably wrong!)
string memberid = Session["MemberID"].ToString();
string locationid = Session["LocationID"].ToString();
string userName = Membership.GetUser().UserName;
string uuf1 = "UnitUserField1";
MyEntities lblUUF1Text = new MyEntities();
lblUUF1.Text = lblUUF1Text.tblUserPreferences
.Where(p => p.MemberID == memberid && p.LocationID == locationid && p.Username == userName && p.ColumnName == uuf1)
.Select(p => p.Alias)
.ToString();
However when I run this, the label text returned is:
System.Data.Objects.ObjectQuery`1[System.String]
Can someone point me in the error of my ways. I'm feeling very, very thick at the moment.
You're writing a query and then asking that query to be converted to a string. Do you only want the first result of that query? If so, it's easy:
lblUUF1.Text = lblUUF1Text.tblUserPreferences
.Where(p => p.MemberID == memberid &&
p.LocationID == locationid &&
p.Username == userName && p.ColumnName == uuf1)
.Select(p => p.Alias)
.First();
(I'm assuming that the type of p.Alias is already string, but that you included the call to ToString as an attempt to coerce the query into a string to make it compile.)
Note that if there are no results, that will blow up with an exception. Otherwise it'll take the first result. Other options are:
Sure there's exactly one result? Use Single()
Think there's either zero or one? Use SingleOrDefault(), store in a local variable and set the label text if the result isn't null.
Think there's anything from zero to many? Use FirstOrDefault() in the same way.
You need a .First() in there
lblUUF1.Text = lblUUF1Text.tblUserPreferences
.Where(p => p.MemberID == memberid && p.LocationID == locationid && p.Username == userName && p.ColumnName == uuf1)
.Select(p => p.Alias).First().ToString();
ToString() will return the object type as you see, what you will need to do is this:
lblUUF1.Text = lblUUF1Text.tblUserPreferences
.Where(p => p.MemberID == memberid && p.LocationID == locationid && p.Username == userName && p.ColumnName == uuf1)
.Select(p => p.Alias).SingleOrDefault();

Linq error generic parameter or the query must use a nullable type

I got this error when i use sum function in LINQ:
The cast to value type 'Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
GroupProduct.Where(a => a.Product.ProductID==1).Sum(Content => Content.Amount==null?0:Content.Amount),
This is what I usually use. This will cover the possibility of Amount being null and also cover the possibility of an empty set.
GroupProduct.Where(a => a.Product.ProductID == 1)
.Select(c => c.Amount ?? 0) // select only the amount field
.DefaultIfEmpty() // if selection result is empty, return the default value
.Sum(c => c)
DefaultIfEmpty() returns the default value associated with Amount's type, which is int, in which case the default value is 0.
Did you try the following:
GroupProduct.Where(a => a.Product.ProductID==1).Sum(Content => (decimal?)Content.Amount)
The code from my application looks like:
var data = query.AsEnumerable().Select(row => TaskInfo.FetchTaskInfo(row,
ctx.ObjectContext.Hours.Where(hour => hour.TaskId == row.TaskId).Sum(hour => (decimal?)hour.Duration),
ctx.ObjectContext.Notes.Count(note => note.SourceType == (int)SourceType.Task && note.SourceId == row.TaskId)));
You could exclude at source?
var sum = GroupProduct.Where(a => a.Product.ProductID==1 && a.Amount != null)
.Sum(a => (decimal)a.Amount);
Try this:
var sum = GroupProduct.Where(a => a.Product.ProductID==1).Sum(Content => (int?) Content.Amount);
sum = sum ?? 0;
This looks like it should work (and usually does) but fails when the Where() method returns null:
decimal sum1 = GroupProduct
.Where(a => a.Product.ProductID == 1)
.Sum(c => c.Amount ?? 0);
The error: "The cast to value type 'Decimal' failed because the materialized value is null" is due to the Sum() method returning null (not zero) when summing over an empty set.
Either of these work for me:
decimal? sum2 = GroupProduct
.Where(a => a.Product.ProductID == 1)
.Sum(c => c.Amount);
decimal sum3 = GroupProduct
.Where(a => a.Product.ProductID == 1)
.Sum(c => c.Amount) ?? 0;

Resources