LINQ - can't create with WHERE condition - c#

I have the following method:
public DataTable.DataTablesPage<DataTable.UserModel> DataTableUserListByAttendees(int geoArea, int CurrentUserID)
{
var result = from i in _dbContext.Users
where i.GeographicalAreas.Any(p => p.GeoAreaID == geoArea)
select new DataTable.UserModel()
{
ID = i.ID,
Company = i.Company,
DCMember = (i.UserId != null),
FirstName = i.FirstName,
LastName = i.LastName
};
}
it works fine, but it returns entities that dont have the geoArea proeprty set.
public DataTable.DataTablesPage<DataTable.UserModel> DataTableUserListByAttendees(int? geoArea, int CurrentUserID)
{
var result = from i in _dbContext.Users
where i.GeographicalAreas.Any(p => p.GeoAreaID == geoArea)
select new DataTable.UserModel()
{
ID = i.ID,
Company = i.Company,
DCMember = (i.UserId != null),
FirstName = i.FirstName,
LastName = i.LastName
};
}
How can I change the query so that it returns only entities that have their geoArea property set (aka is not null).

where (geoArea == null) || i.GeographicalAreas.Any(p => p.GeoAreaID == geoArea)
or conditionally add the where to the query

That should be
where geoArea == null || i.GeographicalAreas.Any(p => p.GeoAreaID == (int)geoArea)

Related

Filtering data from parameters c#

I'm facing a issue when trying to search my page when trying to filter from 5 parameters.
If the parameters is null then I would want to ignore that search and only return results if there is a value in the parameters.
At the moment i used separate parameters but i need something that caters for all 5 and if it has 2 out of 4 values then it should return based on the filter.:
if (name != null)
{
report = report.Where(asd => asd.name == name).OrderBy(x => x.Id).ToList();
}
else if (Id != null)
{
report = report.Where(x => x.Id == Id).OrderBy(x => x.Id).ToList();
}
So this works with only 1 in mind, how would i join all 4 together so it works together.
This is my full code:
[HttpGet]
public ActionResult Get(string Id = null, string name = null, string status = null, string startDate = null, string endDate = null)
{
List<ReportModel> report = new List<ReportModel>();
report = (from data in _context.ReportData
//where data.Id== Id
//&& data.name== name
//&& data.Status == status
//&& data.Date >= Convert.ToDateTime(startDate) && data.Date < Convert.ToDateTime(endDate)
select new ReportModel
{
Id = data.Id,
Name = data.Plant,
Status = data.Status,
Date = data.Date
}).ToList();
if (name != null)
{
report = report.Where(asd => asd.name == name).OrderBy(x => x.Id).ToList();
}
else if (Id != null)
{
report = report.Where(x => x.Id == Id).OrderBy(x => x.Id).ToList();
}
else if (status != null)
{
report = report.Where(x => x.Status == status).OrderBy(x => x.Id).ToList();
}
return Ok(report);
}
Kindly help.
If you care about performance, do not call ToList() early, because it will load whole table into the memory. IQueryable can be combined to produce desired result.
[HttpGet]
public ActionResult Get(string Id = null, string name = null, string status = null, string startDate = null, string endDate = null)
{
var query = _context.ReportData.AsQueryable();
if (name != null)
{
query = query.Where(asd => asd.name == name);
}
if (Id != null)
{
query = query.Where(x => x.Id == Id);
}
if (status != null)
{
query = query.Where(x => x.Status == status);
}
query = query.OrderBy(x => x.Id);
var report = query
.Select(data => new ReportModel
{
Id = data.Id,
Name = data.Plant,
Status = data.Status,
Date = data.Date
})
.ToList();
return Ok(report);
}
Intead of using if condition, you can add directly the condition in the Where clause
if (name != null)
{
query = query.Where(asd => asd.name == name);
}
// replace by
query = query.Where(asd => asd.name == name || name == null);
when name will be null, the Where condition will always true.

Nullable object must have a value in join tables query

I have three tables TableTeam, TableUser and TableLink. I want to join them to get a list.
var teams = _db.TableTeam;
var users = _db.TableUser;
var managerIds = teams.Select(x=>x.ManagerId).ToList();
var list = (from user in _db.TableUser
where user.EndDate == null && user.ManagerId != null && managerIds.Contains(user.ManagerId.Value)
let r = _db.TableLink.FirstOrDefault(l => l.ProjectId == projectId && l.UserId == user.UserId)
let t = _db.TableTeam.FirstOrDefault(m => m.ManagerId == user.ManagerId)
select new MyDto
{
ProjectId = projectId,
UserId = user.UserId,
IsAssigned = r.AssignedId > 0 ?true : false,
AssignedId = r.AssignedId,
Percent = r.Percent.HasValue ? (r.Percent.Value).ToString() + "%": ""
}).GroupBy(d => d.UserId).ToList();
For the table TableLink, the corresponding dataSet class in entity framework is:
public LinkDto
{
public int AssignedId {get;set;}
public short ProjectId {get;set;}
public short UserId {get;set;}
public decimal? Percent {get;set;}
// ...
}
However I got the error:
Nullable object must have a value
I think that it may be caused by decimal? Percent. But not sure how to fix it. Also I can use the store d procedure as well, which means if you can solve it by SQL query, that is also great.
It turns out I have to move let codes before where clause then add the condition to where.
Such as
var list = (from user in _db.TableUser
let r = _db.TableLink.FirstOrDefault(l => l.ProjectId == projectId && l.UserId == user.UserId)
let t = _db.TableTeam.FirstOrDefault(m => m.ManagerId == user.ManagerId)
where user.EndDate == null && user.ManagerId != null && managerIds.Contains(user.ManagerId.Value)
&& r != null && t != null
select new MyDto
{
ProjectId = projectId,
UserId = user.UserId,
IsAssigned = r.AssignedId > 0 ?true : false,
AssignedId = r.AssignedId,
Percent = r.Percent.HasValue ? (r.Percent.Value).ToString() + "%": ""
}).GroupBy(d => d.UserId).ToList();

Method'' has no supported translation to SQL

I want to, in the midst of a linq to sql query where clause, check against a public int. I am getting this error: Method 'Int32 isInDept(System.String)' has no supported translation to SQL.
Vaguely related classes (from a public static class called ad):
//get AD property
public static string GetProperty(this Principal principal, String property) {
DirectoryEntry directoryEntry = principal.GetUnderlyingObject() as DirectoryEntry;
if (directoryEntry.Properties.Contains(property))
return directoryEntry.Properties[property].Value.ToString();
else
return String.Empty;
}
public static string GetDepartment(this Principal principal) {
return principal.GetProperty("department");
}
The Classes in question (from a different class):
public int isInDept(string department) {
PrincipalContext domain = new PrincipalContext(ContextType.Domain);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(domain, GetUserId());
if (department == userPrincipal.GetDepartment()) {
return 3;
}
else { return 2; }
}
public intranetGS.viewArticle viewArticle(int id) {
string user = GetUserId();
var result = ( from a in n.articles
join s in n.sections on a.section equals s.section_id
join p in n.privacies on a.privacy equals p.privacy_id
let iid = isInDept(s.name)
where (a.active == true && a.article_id == id && a.privacy < iid) ||
(a.active == true && a.article_id == id && a.privacy == 3 && a.author == user)
select new intranetGS.viewArticle {
articleId = a.article_id,
title = a.title,
author = a.author,
html = a.html,
section = s.name,
privacy = p.name,
dateCreated = a.date_created,
dateModified = a.date_modified,
userCreated = a.user_created,
userModified = a.user_modified
}).First();
var nv = (from v in n.navs
join s in n.sections on v.section equals s.section_id
let iid = isInDept(s.name)
where (v.active == true && s.name == result.section && v.privacy < 3) ||
(v.active == true && s.name == result.section && v.privacy == iid && v.user_created == user)
select v.html);
StringBuilder sb = new StringBuilder();
foreach (var r in nv) {
sb.Append(nv);
}
result.articleNav = sb.ToString();
return result;
}
What am I doing wrong? If I can't do it this way, how is it suggested that it be done?
It is not possible to translate that function to SQL, one workaround for this is to make most of your query with linq to sql, and use Linq to Objects for the rest. It should be something like this:
var query = ( from a in n.articles
join s in n.sections on a.section equals s.section_id
join p in n.privacies on a.privacy equals p.privacy_id
where (a.active == true && a.article_id == id)
select new intranetGS.viewArticle {
articleId = a.article_id,
title = a.title,
author = a.author,
html = a.html,
section = s.name,
privacy = p.name,
privacyId = a.privacy,
dateCreated = a.date_created,
dateModified = a.date_modified,
userCreated = a.user_created,
userModified = a.user_modified
}).ToList();
And then filter the list:
var result = query.Where(a => (a.privacyId < isInDept(a.section)) ||
(a.privacyId == 3 && a.author == user)).First();
Then you can do the same for the second query.

The entity or complex type cannot be constructed in a LINQ to Entities query

On our online billing application, we give a billing summary of what bills the customer received and the payments they made.
In order for this to work, I have to first pull the payments then match them to the bills. So I have do something like:
foreach (BillPaymentSummary payment in billPayments)
{
DateTime dt = payment.DueDate;
// Debug errors on this next line
var summary = (from a in db.BillHistories
where a.CustomerId == customerNumber && a.DueDate == dt && a.Type == "BILL"
select new BillSummary
{
Id = a.Id,
CustomerId = a.CustomerId,
DueDate = a.DueDate,
PreviousBalance = a.PreviousBalance.Value,
TotalBill = a.TotalBill.Value,
Type = a.Type,
IsFinalBill = a.IsFinalBill
}).SingleOrDefault();
if (summary != null)
{
summary.PayDate = payment.PaidDate;
summary.AmountPaid = payment.AmountPaid;
returnSummaries.Add(summary);
}
else
{
summary = (from a in db.BillHistories
where a.CustomerId == customerNumber && a.DueDate == payment.DueDate && a.Type == "ADJ "
select new BillSummary
{
Id = a.Id,
CustomerId = a.CustomerId,
DueDate = a.DueDate,
PreviousBalance = a.PreviousBalance.Value,
TotalBill = a.TotalBill.Value,
Type = a.Type,
IsFinalBill = a.IsFinalBill
}).SingleOrDefault();
if (summary != null)
{
summary.PayDate = payment.PaidDate;
summary.AmountPaid = payment.AmountPaid;
returnSummaries.Add(summary);
}
}
}
I have been playing with this, but no matter what I do, I get the following error message:
The entity or complex type 'UtilityBill.Domain.Concrete.BillSummary' cannot be constructed in a LINQ to Entities query.
Is it because I am running queries within queries? How can I get around this error?
I have tried searching Google for an answer and see many answers, but none of them seem to explain my problem.
You cannot project onto a mapped entity. You would have to call ToList() before doing your mapping.
Or better yet, change to the following (calling FirstOrDefault will execute the query and allow you to populate your object):
var summary = db.BillHistories.FirstOrDefault(a => a.CustomerId == customerNumber && a.DueDate == dt && a.Type == "BILL").Select(x => new BillSummary
{
Id = a.Id,
CustomerId = a.CustomerId,
DueDate = a.DueDate,
PreviousBalance = a.PreviousBalance.Value,
TotalBill = a.TotalBill.Value,
Type = a.Type,
IsFinalBill = a.IsFinalBill
});
To decouple yourself from the Entity Framework you may want to also consider using a different model class to return instead of the Entity Framework model.
What I ended up doing was:
foreach (BillPaymentSummary payment in billPayments)
{
var data = db.BillHistories.Where(b => b.CustomerId == customerNumber && b.DueDate == payment.DueDate && b.Type == "B").FirstOrDefault();
if (data != null) // There is a bill history
{
returnSummaries.Add(new BillSummary
{
Id = data.Id,
CustomerId = data.CustomerId,
DueDate = data.DueDate,
PreviousBalance = data.PreviousBalance,
TotalBill = data.TotalBill,
Type = (data.Type.Trim() == "B" ? "BILL" : (data.Type == "A" ? "ADJ" : "")),
IsFinalBill = data.IsFinalBill,
PayDate = payment.PaidDate,
AmountPaid = payment.AmountPaid
});
}
else // No bill history record, look for an adjustment
{
data = db.BillHistories.FirstOrDefault(b => b.CustomerId == customerNumber && b.DueDate == payment.DueDate && b.Type == "A");
if (data != null)
{
returnSummaries.Add(new BillSummary
{
Id = data.Id,
CustomerId = data.CustomerId,
DueDate = data.DueDate,
PreviousBalance = data.PreviousBalance,
TotalBill = data.TotalBill,
Type = (data.Type.Trim() == "B" ? "BILL" : (data.Type == "A" ? "ADJ" : "")),
IsFinalBill = data.IsFinalBill,
PayDate = payment.PaidDate,
AmountPaid = payment.AmountPaid
});
}
}
db.SaveChanges();
}

Most efficient way of loading data into LINQ object for search result type method

I currently have the following:
public IEnumerable<News> NewsItems
{
get { return from s in News.All() where s.Description.Contains(SearchCriteria) || s.Summary.Contains(SearchCriteria) select s; }
}
The problem is I only need to return the one property that actually has the data as well as the Title property, something similar to.
return from s in News.All() where s.Description.Contains(SearchCriteria) || s.Summary.Contains(SearchCriteria) select new {Title = s.Title, Data = //Description or Summary containing the data
How do I determine which one contains the search query?
UPDATE: I have this but it obviously hits the DB 3 times
var FoundInSummary = News.All().Any(x => x.Summary.Contains(SearchCriteria));
var FoundInDesc = News.All().Any(x => x.Description.Contains(SearchCriteria));
IEnumerable<NewsEventSearchResults> result = null;
if ((FoundInSummary && FoundInDesc) || (FoundInSummary))
{
result = (from s in News.All() where s.Summary.Contains(SearchCriteria) select new NewsEventSearchResults { Title = s.Title, Data = s.Summary, ID = s.ID }).AsEnumerable();
}
else if (FoundInDesc)
{
result = (from s in News.All() where s.Description.Contains(SearchCriteria) select new NewsEventSearchResults { Title = s.Title, Data = s.Description, ID = s.ID }).AsEnumerable();
}
return result;
UPDATE 2: Is this more efficent?
var ss = (from s in News.All() where s.Description.Contains(SearchCriteria) || s.Summary.Contains(SearchCriteria) select s).ToList();
List<NewsEventSearchResults> resultList = new List<NewsEventSearchResults>();
foreach (var item in ss)
{
bool FoundInSummary = item.Summary.Contains(SearchCriteria);
bool FoundInDesc = item.Description.Contains(SearchCriteria);
if ((FoundInSummary && FoundInDesc) || (FoundInSummary))
{
resultList.Add(new NewsEventSearchResults { Title = item.Title, Data = item.Summary, ID = item.ID });
}
else if (FoundInDesc)
{
resultList.Add(new NewsEventSearchResults { Title = item.Title, Data = item.Description, ID = item.ID });
}
}
What if they both contain the criteria? Or are they mutually exclusive? If so
Data = (s.Description != null ? s.Description : s.Summary)
I went with option 3

Categories