LINQ join issue, returns all null value - linq

I am new in EF and LINQ and I am facing a strange issue. When I check null value in my select new block, all values from child table is coming null. Below is the LINQ query.
My Linq Code
var linqResult = from pd in entities.tblpackagedetails
join ps in entities.tblpackageselecteds
on pd.PackageDetailsID equals ps.PackageDetailsID
into tabJoin
from tj in tabJoin.Where(ps => ps.UserID == userID
&& ps.IsActive == true).DefaultIfEmpty()
select new
{
IsComplete = (tj == null) ? false : tj.IsComplete,
IsActive = (tj == null) ? false : tj.IsActive,
UserID = (tj == null) ? 0 : tj.UserID,
IsMandatory = pd.IsMandatory,
PackageSelectedID = (tj == null) ? 0 : tj.PackageSelectedID,
IsSelected = (tj == null ? false : tj.IsSelected),
pd.Amount,
pd.Code,
pd.Description,
pd.Points,
pd.PackageDetailsID
};
foreach (var result in linqResult)
{
packagesSelected.Add(new PackageDetailDataModel()
{
Amount = result.Amount,
Code = result.Code,
Description = result.Description,
IsComplete = result.IsComplete,
IsMandatory = result.IsMandatory,
PackageDetailsID = result.PackageDetailsID,
PackageSelectedID = result.PackageSelectedID,
Points = result.Points,
IsActive = result.IsActive,
UserID = result.UserID,
IsSelected = result.IsSelected
});
}
SQL generated by Visualizer
SELECT
`Extent1`.`PackageDetailsID`,
`Extent2`.`IsComplete`,
`Extent2`.`IsActive`,
`Extent2`.`UserID`,
`Extent1`.`IsMandatory`,
`Extent2`.`PackageSelectedID`,
`Extent2`.`IsSelected`,
`Extent1`.`Amount`,
`Extent1`.`Code`,
`Extent1`.`Description`,
`Extent1`.`Points`
FROM `tblpackagedetails` AS `Extent1`
LEFT OUTER JOIN `tblpackageselected` AS `Extent2`
ON (`Extent1`.`PackageDetailsID` = `Extent2`.`PackageDetailsID`)
AND ((`Extent2`.`UserID` = #linq_0) AND (1 = `Extent2`.`IsActive`))
When I ran above sql in MySQL workbench I got below output (repalcing #linq_0 with userID).
My Parent Table Structure
Child Table Structure
Output I want
But the values for IsComplete, IsActive, UserID, PackageSelectedID and IsSelected null as a result condition checking in select new block assign false or 0.
If I remove null checking, I get value for first 3 rows and in fourth iteration I get below exception.
The cast to value type 'Boolean' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type
Please help... :(
Working code block
packagesSelected = new List<PackageDetailDataModel>();
var linqResult = from pd in entities.tblpackagedetails
join ps in entities.tblpackageselecteds
on pd.PackageDetailsID equals ps.PackageDetailsID
into tabJoin
from tj in tabJoin.Where(ps => ps.UserID == userID
&& ps.IsActive == true).DefaultIfEmpty()
select new
{
IsComplete = (bool?)tj.IsComplete,
IsActive = (bool?)tj.IsActive,
UserID = (int?)tj.UserID,
IsMandatory = pd.IsMandatory,
PackageSelectedID = (int?)tj.PackageSelectedID,
IsSelected = (bool?)tj.IsSelected,
pd.Amount,
pd.Code,
pd.Description,
pd.Points,
pd.PackageDetailsID
};
foreach (var result in linqResult)
{
packagesSelected.Add(new PackageDetailDataModel()
{
Amount = result.Amount,
Code = result.Code,
Description = result.Description,
IsComplete = (result.IsComplete ?? false),
IsMandatory = result.IsMandatory,
PackageDetailsID = result.PackageDetailsID,
PackageSelectedID = (result.PackageSelectedID ?? 0),
Points = result.Points,
IsActive = (result.IsActive ?? false),
UserID = (result.UserID ?? 0),
IsSelected = (result.IsSelected ?? false)
});
}
Thanks to 2Kay :)

When tj is null, EF consieders all properties of tj as null. It's ok, but when EF trying to materialize them into value-types it fails. So the solution is to use nullable types..
Try this query:
var linqResult = from pd in entities.tblpackagedetails
join ps in entities.tblpackageselecteds
on pd.PackageDetailsID equals ps.PackageDetailsID
into tabJoin
from tj in tabJoin.Where(ps => ps.UserID == userID
&& ps.IsActive == true).DefaultIfEmpty()
select new
{
IsComplete = (bool?) tj.IsComplete,
IsActive = (bool?) tj.IsActive,
UserID = (int?) tj.UserID,
IsMandatory = pd.IsMandatory,
PackageSelectedID = (int?) tj.PackageSelectedID,
IsSelected = (bool?) tj.IsSelected,
pd.Amount,
pd.Code,
pd.Description,
pd.Points,
pd.PackageDetailsID
};

Related

How to convert LINQ query to work with EF Core

I'm converting from EF6 to EF Core 3.1 and this LINQ query is failing with a runtime exception stating 'The LINQ expression ... could not be translated.
The group by is what is causing the issue, but I'm not sure how to rewrite it to work with EF Core and keep the result in a nested list.
Notification notification = new Notification()
{
ProductReminders = new List<List<ProductNotification>>(),
ProductStats = new List<StatResult>()
};
var profileCode = 123;
notification.ProductReminders =
(from ng in ProductNotification
where ng.UserProfileCode == profileCode
orderby ng.EndDate ?? DateTime.MaxValue
group ng by ng.GroupGUID into groupG
select (from pn in ProductNotification
join p in Product on pn.ProductID equals p.ProductID
where pn.UserProfileCode == profileCode
&& pn.GroupGUID == groupG.Key
orderby pn.EndDate ?? DateTime.MaxValue
select new ProductNotification()
{
ProductDetail = new ProductDetail()
{
ProductId = pn.ProductID ?? 0,
Upc = p.UPC,
Brand = p.Description,
Manufacturer = p.Name,
ProfileCode = p.ProfileCode,
},
EndDate = pn.EndDate,
NotificationId = pn.NotificationID,
Status = pn.Status,
GroupGuid = pn.GroupGUID
})
.ToList())
.ToList();
Since grouping operator has limitations, I would suggest to read all needed data and provide grouping on the client side. Query in your case will be much effective:
// select only needed data from database
var minimalRequiredData =
from pn in ProductNotification
join p in Product on pn.ProductID equals p.ProductID
where pn.UserProfileCode == profileCode
select new ProductNotification
{
ProductDetail = new ProductDetail
{
ProductId = pn.ProductID ?? 0,
Upc = p.UPC,
Brand = p.Description,
Manufacturer = p.Name,
ProfileCode = p.ProfileCode,
},
EndDate = pn.EndDate,
NotificationId = pn.NotificationID,
Status = pn.Status,
GroupGuid = pn.GroupGUID
};
// materialize result
var materialized = minimalRequiredData.ToList();
// form required result shape using IEnumerable<T>
var resultQuery =
from m in materialized
orderby ng.EndDate ?? DateTime.MaxValue
group m by new m.GroupGUID into g
select g.Orderby(x => x.EndDate ?? ng.EndDate).ToList();
notification.ProductReminders = resultQuery.ToList();

how to pass string variable to linq select new {} section

Ii just want to make search functionality with linq with multiple ColumnNames that stored to session variable. I'm using one method:
public void FillGrid(string CommandName,string ColumnName, string SearchText)
That has three string variable that stores session value.
Now I just want to pass ColumnName with this query:
var query1 = (from p in db.Posts
join c in db.Categories on p.Category_id equals c.Id
join u in db.Users on p.User_id equals u.Id
where (p.ToUser_id == user_id || p.ToUser_id == null) && p.User_id != user_id
orderby p.Sent_Datetime descending
select new
{
Id = p.Id,
Title = p.Title,
Publisher = u.First_name + " " + u.Last_name,
ToUser = p.ToUser_id,
PublishDate = p.Sent_Datetime,
IsFile = p.IsFileAttached,
CategoryName = c.Category_name,
status_name = (from s in db.Status where (s.Id == p.status_id) select s.status_name).FirstOrDefault(),
Group_name = (from g in db.Groups where (g.Id == p.group_id) select g.Group_name).FirstOrDefault(),
FileSize = p.TotalFileSize,
ColumnName = Sesssion["ColumnName"].ToString()
}).Where(q => q.ColumnName.Contains(SearchText));
However, ColumnName does not give any text or it may be not part of this query i have to manually give column name because.
for multiple column i have, so i can not use this statement like:
.Where(q => q.Tile.Contains(SearchText));
this query works fine with single column. but there is multiple column i have so i have to set q.ColumnName from outer side.
I would do an extension method for that kind of things, building an expression for your predicate.
public static class Helper
{
public static IQueryable<T> FilterForColumn<T>(this IQueryable<T> queryable, string colName, string searchText)
{
if (colName != null && searchText != null)
{
var parameter = Expression.Parameter(typeof(T), "m");
var propertyExpression = Expression.Property(parameter, colName);
var searchExpression = Expression.Constant(searchText);
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var body = Expression.Call(propertyExpression, containsMethod, searchExpression);
var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
return queryable.Where(predicate);
}
else
{
return queryable;
}
}
}
usage in your case
var query1 = (from p in db.Posts
join c in db.Categories on p.Category_id equals c.Id
join u in db.Users on p.User_id equals u.Id
where (p.ToUser_id == user_id || p.ToUser_id == null) && p.User_id != user_id
orderby p.Sent_Datetime descending
select new
{
Id = p.Id,
Title = p.Title,
Publisher = u.First_name + " " + u.Last_name,
ToUser = p.ToUser_id,
PublishDate = p.Sent_Datetime,
IsFile = p.IsFileAttached,
CategoryName = c.Category_name,
status_name = (from s in db.Status where (s.Id == p.status_id) select s.status_name).FirstOrDefault(),
Group_name = (from g in db.Groups where (g.Id == p.group_id) select g.Group_name).FirstOrDefault(),
FileSize = p.TotalFileSize,
}).FilterForColumn(Sesssion["ColumnName"].ToString(), SearchText);

How to write a LINQ statement with a GroupBy condition

I have this LINQ statement as follows;
var RequestList = (from emp in _employeeIds
from x in db.AnnualLeaveBookeds
where x.EmployeeId == emp
orderby x.AnnualLeaveDate
select new RequestInfo
{
AnnualLeaveBookedId = x.AnnualLeaveBookedId,
AnnualLeaveDate = x.AnnualLeaveDate,
MorningOnlyFlag = x.MorningOnlyFlag,
AfternoonOnlyFlag = x.AfternoonOnlyFlag,
Forename = x.Employee.Forename,
Surname = x.Employee.Surname,
EmployeeId = x.Employee.EmployeeId,
RequestDate = x.RequestDate,
CancelRequestDate = x.CancelRequestDate,
ApprovedFlag = (x.ApprovalDate.HasValue && x.ApproverId != Employee.LoggedInUser.EmployeeId),
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty,
FontColour = "Black"
})
.ToList();
For every RequestInfo I return a FontColour property of Black.
However if I have 2 or more RequestInfo objects with the same AnnualLeaveDate, I want the FontColour to be set to red.
How do I rewrite this query to do that?
Try something like this:
var RequestList = (
from emp in _employeeIds
from x0 in db.AnnualLeaveBookeds
where x0.EmployeeId == emp
orderby x0.AnnualLeaveDate
group x0 by x0.AnnualLeaveDate into xs
from x in xs
select new RequestInfo
{
AnnualLeaveBookedId = x.AnnualLeaveBookedId,
AnnualLeaveDate = x.AnnualLeaveDate,
MorningOnlyFlag = x.MorningOnlyFlag,
AfternoonOnlyFlag = x.AfternoonOnlyFlag,
Forename = x.Employee.Forename,
Surname = x.Employee.Surname,
EmployeeId = x.Employee.EmployeeId,
RequestDate = x.RequestDate,
CancelRequestDate = x.CancelRequestDate,
ApprovedFlag = (x.ApprovalDate.HasValue
&& x.ApproverId != Employee.LoggedInUser.EmployeeId),
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty,
FontColour = xs.Count() > 1 ? "Red" : "Black"
}).ToList();

Linq: Nested queries are better than joins, but what if you use 2 nested queries?

In her book Entity Framework Julie Lerman recommends using nested queries in preference to joins (scroll back a couple of pages).
In her example see populates 1 field this way, but what id you want to populate 2?
I have an example here where I would prefer to populate the Forename and Surname with the same nested query rather than 2 separate ones. I just need to know the correct syntax to do this.
public static List<RequestInfo> GetRequests(int _employeeId)
{
using (SHPContainerEntities db = new SHPContainerEntities())
{
return db.AnnualLeaveBookeds
.Where(x => x.NextApproverId == _employeeId ||
(x.ApproverId == _employeeId && x.ApprovalDate.HasValue == false))
.Select(y => new RequestInfo
{
AnnualLeaveDate = y.AnnualLeaveDate,
Forename = (
from e in db.Employees
where e.EmployeeId == y.EmployeeId
select e.Forename).FirstOrDefault(),
Surname = (
from e in db.Employees
where e.EmployeeId == y.EmployeeId
select e.Surname).FirstOrDefault(),
RequestDate = y.RequestDate,
CancelRequestDate = y.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
})
.OrderBy(x => x.AnnualLeaveDate)
.ToList();
}
}
There's nothing wrong with your query, but you can write it in a way that is much simpler, without the nested queries:
public static List<RequestInfo> GetRequests(int employeeId)
{
using (SHPContainerEntities db = new SHPContainerEntities())
{
return (
from x in db.AnnualLeaveBookeds
where x.NextApproverId == employeeId ||
(x.ApproverId == employeeId && x.ApprovalDate == null)
orderby x.AnnualLeaveDate
select new RequestInfo
{
AnnualLeaveDate = x.AnnualLeaveDate,
Forename = x.Employee.Forename,
Surname = x.Employee.Surname,
RequestDate = x.RequestDate,
CancelRequestDate = x.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
}).ToList();
}
}
See how I just removed your from e in db.Employees where ... select e.Forename) and simply replaced it with x.Employee.Forename. When your database contains the correct foreign key relationships, the EF designer will successfully generate a model that contain an Employee property on the AnnualLeaveBooked entity. Writing the query like this makes it much more readable.
I hope this helps.
try this
using (SHPContainerEntities db = new SHPContainerEntities())
{
return db.AnnualLeaveBookeds
.Where(x => x.NextApproverId == _employeeId ||
(x.ApproverId == _employeeId && x.ApprovalDate.HasValue == false))
.Select(y =>
{
var emp = db.Emplyees.Where(e => e.EmployeeId == y.EmployeeId);
return new RequestInfo
{
AnnualLeaveDate = y.AnnualLeaveDate,
Forename = emp.Forename,
Surname = emp.Surname,
RequestDate = y.RequestDate,
CancelRequestDate = y.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
};
).OrderBy(x => x.AnnualLeaveDate).ToList();
}

How to make this LINQ To entity method work when it has Nullable LEFT JOIN

Here is the code snippet, actually the whole method. This method works f,ine when NULLAblE Foreign Key Refernces has value. When the value is not there, then this method does not work. My idea is to get all the records even if the references column is NULL. Here is the code :
public List<PostedJob> GetPostedJobs(int startingIndex, int maximumRows)
{
using (var records = new CommonEvent())
{
var resultSet =
from r in records.ProjectPosts
join rr in records.Categories on r.Category_FK equals rr.ID
join al in records.ApplyForLimits on r.ApplyForLimit_FK
equals al.Id
//from uImage in
// records.UploadedFiles
// .Where(uu=>uu.Id == r.UploadedFileInfo_FK
// || r.UploadedFileInfo_FK == null).DefaultIfEmpty()
join a in records.UploadedFiles on r.UploadedFileInfo_FK
equals a.Id into something
from uImage in something.DefaultIfEmpty()
orderby r.PostId
select new Models.PostedJob
{
ApplyForLimitName = al.Name,
ProjectTitle = r.ProjectTitle,
ProjectDescription = r.ProjectDescription,
ProjectSummaryDescription = r.ProjectSummaryDescription,
SkillsRequirements = r.SkillsRequirements,
CategoryName = rr.CategoryName,
UploadedFileID = (int) r.UploadedFileInfo_FK,
UploadedFileInformation = uImage == null ?
new Models.UploadedFile
{
fileContents = new byte [] { (byte) 0},
FileExtension = string.Empty,
FileName = string.Empty,
FileSize = 0,
UploadedDate = DateTime.Now
}
:
new Models.UploadedFile
{
fileContents = uImage.FileContents,
FileExtension = uImage.FileExtension,
FileName = uImage.FileName,
FileSize = uImage.FileSize,
UploadedDate = DateTime.Now
}
};
return resultSet.Skip(startingIndex).Take(maximumRows).ToList();
}
Thank you for any suggestions or ideas on how to proceed . I am using .NET 4.0
Can you not use the Associations generated for you?
var a = records
.ProjectPosts
.Select(
projectPost =>
new Models.PostedJob()
{
ProjectTitle = projectPost.ProjectTitle,
CategoryName = projectPost.Category.CategoryName,
});
Something along those lines?
EDIT: And just add Null checks when the FK may fail
example:
CategoryName = projectPost.Category == null ? String.Empty : projectPost.Category.CategoryName,

Resources