How do I set conditions from multiple tables in my LINQ query? - linq

In the query below, I want to return people that meet multiple conditions. Some of the conditions apply to fields in the table containing the people to be returned. The other condition is applied to a different table (EmailAddresses) linked to the main table (People) via PersonId.
var t = People.Where(x =>
x.Type == 102 &&
x.FirstName == "Bob" &&
x.LastName == "Williams" &&
x.EmailAddresses.Where (ea=> ea.EmailAddress
== "bob.williams#acme.org")
)
.Select(x => x.PersonId)
How do I do this?

Do understand this right, at least one of them should have that adress? If yes, use the Any method:
var t = People.Where(x =>
x.Type == 102 &&
x.FirstName == "Bob" &&
x.LastName == "Williams" &&
x.EmailAddresses.Any(ea=> ea.EmailAddress
== "bob.williams#acme.org")
)
.Select(x => x.PersonId)
Any returns true if at least one of the elements of the IQueryable<T> fulfills the predicate.

I think you could do it with a join, something like this...
var t = from p in People
join e in EmailAddress on p.PersonId equals e.PersonId into pe
from a in pe.DefaultIfEmpty
where p.Type == 102 &&
p.FirstName == "Bob" &&
p.LastName == "Williams" &&
a.EmailAddress == "bob.williams#acme.org"
select p.PersonId

Related

Single link query where clause to take only `n` of each type from a single model/table

Having a table Entities with a column type (between other columns) how can I select only n entities from type A and m entities from type b in a single linq query where clause? Is it possible?
I am looking for something like this:
var x = from s in db.Entities
where s.type == `A` && (????) < n
|| s.type == `B` && (????) < m
select s
Current solution with combined queries:
var x = entities.Where(e => e.type == `A`).Take(n).Union(
entities.Where(e => e.type == `B`).Take(m));
You could use GroupBy:
var x = db.Entities.GroupBy(x => x.type)
.Where(g => g.Key == "A" || g.Key == "B")
.SelectMany(g => g.Key == "A" ? g.Take(n) : g.Take(m));
But I don't know why you don't like your Union-based solution - it should result in just one query sent to the database anyway.

Linq query skip querying null coditions

I am very new to linq. I have my clients table. I want to select clients depending on the two conditions
Client type
Client city
So I can write the query like
from c in clients
where c.Type == cType
&& c.City == cCity
Can I use this same query to get the result only providing client type(ignoring the City condition. somithing like *).
What I want to do is if cCity or cType is null ignore the condition.
Is this possible to do?
Isn't that what you're looking for?
from c in clients
where (c.Type == null || c.Type == cType)
&& (c.City == null || c.City == cCity)
You can compose a LINQ statement before actually executing it:
if (cType != null)
clients = clients.Where(c => c.Type == cType);
if (cCity != null)
clients = clients.Where(c => c.City== cCity);
// At this point the query is never executed yet.
Example of how the query can be executed for the first time :
var results = clients.ToList();
from c in clients
where (cType == null ? 1 == 1 : c.Type == cType)
&& (cCity == null ? 1 == 1 : c.City == cCity)

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();

Linq strangeness

I was receiving repeating rows from this linq query:
public static Func<DataContext, string, IQueryable<Building>>
GearFilteredBuildings =
CompiledQuery.Compile((DataContext db, string filter) =>
from b in db.Building
join r in db.Router on b equals r.Building
orderby !b.Active
where filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter
|| filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower()
select b);
After some fiddling, I got the distinct Buildings with this:
public static Func<DataContext, string, IQueryable<Building>>
GearFilteredBuildings =
CompiledQuery.Compile((DataContext db, string filter) =>
(from b in db.Building
join r in db.Router on b equals r.Building
orderby !b.Active
where filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter
|| filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower()
group b by b.Id into g
select g) as IQueryable<Building>);
Is this an acceptable solution? How else might this be done?
Not sure (couldn't test it IDE) but select...join...lalala can replaced with Linq-Chain syntax:
db.Routers
.Where(r => filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter || filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower())
.GroupBy(r => r.Building)
.Select(g => g.Key)
.OrderBy(b => !b.Active)
Also: as I can see no joins are really requred in your query, as you have navigation properties (r.Building)in your model.
Or another approach could be used, select all needed buildings, and use .Distinct() afterwards:
db.Routers
.Where (r => filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter || filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower())
.Select(r => r.Building)
.Distinct()
.OrderBy(b => !b.Active)

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();

Resources