This may look complicated, but I am really just trying to select all records and their children from A where certain conditions exist in their children. Looking for the syntax I can add to the A variable that allows me to filter a bunch of conditions (like a specification pattern?)
If you have a nested view like this:
var a = from Arow in Atable
where ???
select new AViewModel { // (image Products)
id = Arow.id,
name = Arow.Name,
Brows = (from Brow in Arow.Brows
select new BViewModel { // (sold in different regions)
id = Brow.id,
name = Brow.Name,
Crows = (from Crow in Brow.Crows
select new CViewModel { // (and in many stores)
id = Crow.id,
name = Crow.Name
}
}
};
And text representing queries from a web page like this (Specification Pattern?)
We used JQuery selectors for the operators (like ^= means "starts with")
filter[] = { "Brow.Name = Joe", "Crow.Name = Kim" }
How could you filter the expression A with those criteria? In other words, can you have a where expression like a.Where() that can filter nested properties?
My poor attempt:
var b = from row in a
where a.Brow.Where(b => b.name == "Joe") &&
a.Brow.Crow.Where(c => c.name == "Kim")
select row;
What I really need is something like this:
Select *
from A join B on A.key = B.key join C on B.key = C.key -- propagated keys
where exists (select null from B where A.key = B.key and B.Name = "Joe") and
exists (select null from C where A.key = C.key and C.Name = "Kim")
Especially if I can do this:
var result = a.Where(first).Where(second);
Your "poor attempt" isn't so far off. Just substitute Any() for Where():
var b = from row in a
where a.Brow.Any(b => b.name == "Joe") &&
a.Brow.Crow.Any(c => c.name == "Kim")
select row;
Edited to add:
You might want to do a case-insensitive comparison, though, e.g., .Any(b => b.name.Equals("Joe", StringComparison.CurrentCultureIgnoreCase)).
Or for fun, you can do the lambda equivilant:
var b = a.where(x => x.Brow.Any(b => b.name == "Joe")
&& x.Brow.Crow.Any( c => c.name == "Kim")
.Select(y => y.row);
Related
Im trying to make an inner join of 2 queryable lists and I get this error, I noticed when debugging that the result of the query is empty as so:
Debugger Image
The other lead that I have is basically that one of the lists is a list of Guid's and the other is a list of Objects, which, have a property of resourceId that I want to compare to the Id's on the other list, not exactly sure how that could affect. Anyways, this is how the relevant part of my code looks like.
var qarDevices = repository.Devices.GetQueryable(); // <Iqueryable<DeviceEntity>>
var alerts = repository.Alerts.GetQueryable().Where(a => !a.Inactive); // <Iqueryable<AlertEntity>>
List<String> resourceIdList = alerts.Select(a => a.ResourceId.ToString()).ToList();
var avWifiDevices = await dataSyncService.getListOfResourceIds(resourceIdList); // <Iqueryable<Guid>>
var nonLowSLA = alerts.Where(a => a.Name != "Low SLA"); // Alerts but filtered on the criteria stated
var nonPDAlerts = nonLowSLA.Where(a => a.AuditResource.ToUpper() != "PACKAGE" && a.AuditResource.ToUpper() != "DEVICE"); // Same thing but different criteria
var packageAlerts = from a in nonLowSLA
join p in repository.Packages.GetQueryable() on a.ResourceId equals p.Id
join d in qarDevices on p.DeviceId equals d.Id
where !d.Inactive && a.AuditResource.ToUpper() == "PACKAGE"
select a; // This one works fine, and I get results from this query
var qarDeviceAlerts = from a in nonLowSLA
join d in qarDevices on a.ResourceId equals d.Id
where !d.Inactive && a.AuditResource.ToUpper() == "DEVICE"
select a; // This query also works and returns data
var avWifiDeviceAlerts = from a in nonLowSLA
join d in avWifiDevices on a.ResourceId equals d
where a.AuditResource.ToUpper() == "DEVICE"
select a; // This is the query that does not return data and causes an exception later on for the same reason, So I strongly believe something is wrong here.
Also, the error is this one:
System.InvalidOperationException: Processing of the LINQ expression 'DbSet<AlertEntity>
.Where(a => !(a.Inactive))
.Where(a => a.Name != "Low SLA")
.Join(
outer: EnumerableQuery<Guid> { bce3d904-ac1d-47b9-a645-9e951c507ce3, 3955489e-a0ed-4248-b2f9-e66b980b959d, },
inner: a => a.ResourceId,
outerKeySelector: d => d,
innerKeySelector: (a, d) => new {
a = a,
d = d
})' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
Here is what I am trying to accomplish:
I have a list of companies that offer different services. I am trying to group the services together of a company in a string format so when I can export to excel it shows up in one column.
Right now if a company has 4 services, they will show up 4 different times in query. Which is logical, just want to group them together.
Here is what I have tried and get "A lambda expression with a statement body cannot be converted to an expression tree"
Services = (from cc in CharityCategories join c in Cao_Categories on cc.CategoryID equals c.CategoryID
join chy in CharityYears on cc.CharityYearID equals chy.CharityYearID
where chy.CampYearID == 5 && chy.StatusID == 1
group c by c.Category into cg
select new { Categories = cg.Key.Trim()}).Aggregate(new StringBuilder(), (a, b) =>
{
if (a.Length > 0)
a.Append(",");
a.Append(b.ToString().Split('=')[1].Replace(" }", ""));
return a;
}).ToString() ,
LinqPad shows the error on the line, right after StringBuilder(), "Aggregate(new StringBuilder(), (a, b)"
I can get them to group in a link and when clicked, that link lists them in a format like { myservice = 1} - This is why I am using .Append
use below code then change your logic accordingly, hopefully this will work
Correction in cg.Key.ToString().Trim()
Services = (from cc in CharityCategories
join c in Cao_Categories on cc.CategoryID equals c.CategoryID
join chy in CharityYears on cc.CharityYearID equals chy.CharityYearID
where chy.CampYearID == 5 && chy.StatusID == 1
group c by c.Category into cg
select new { Categories = cg.Key.ToString().Trim() }).Aggregate(new StringBuilder(), (a, b) =>
{
if (a.Length > 0)
a.Append(",");
a.Append(b.ToString().Split('=')[1].Replace(" }", ""));
return a;
}).ToString();
I don't know your requirement, we can correct this if you can provide exact expected result
Well as the error says you cannot convert a lambda expression with a statement body to an expression tree. Entity framework uses expression trees to convert to sql statements. However, you can materialize the results first with (AsEnumerable) and then use your code with linq to objects.
Services = (from cc in CharityCategories
join c in Cao_Categories on cc.CategoryID equals c.CategoryID
join chy in CharityYears on cc.CharityYearID equals chy.CharityYearID
where chy.CampYearID == 5 && chy.StatusID == 1
group c by c.Category into cg
select new { Categories = cg.Key.ToString().Trim() })
.AsEnumerable()
.Aggregate(new StringBuilder(), (a, b) =>
{
if (a.Length > 0)
a.Append(",");
a.Append(b.ToString().Split('=')[1].Replace(" }", ""));
return a;
}).ToString();
Important: The aggregation will take place after all the rows were retrieved from the DB.
Assuming your result set can be represented in the form of the following class
public class Category
{
public string Name{get;set;}
public string Product{get;set;}
}
You could write the following LINQ query to get the result in the way you wished to:
var testList = new List <Category> {
new Category {Name = "A",Product = "decks"},
new Category {Name = "B",Product = "cards"},
new Category {Name = "C",Product = "paper"},
new Category {Name = "A",Product = "scissor"},
new Category {Name = "B",Product = "crates"},
new Category {Name = "C",Product = "rocks"}
};
var finalList = testList
.GroupBy(x => x.Name)
.Select(x => new {
Category =x.Key,
Items = String.Join(",", x.Select(y => y.Product))
});
Thus the result set would be in the form of:
Category A -> decks, scissor
Category B-> cards,crates
Category C-> paper, rocks
Do mark as answer if this helps.
var query2 = (from tc in Entities.TutorCourses
join c
in Entities.Course
on tc.CourseId equals c.Id
where tc.CourseId == id
select tc.Username).ToList();
var query1 = Entities.User
.Select(u => u.Role
.Select(p => p.Name)
.Contains("Tutor"));
I am trying to return all the Users from a database that are in query1 except all those Users that are in query2 as above.
How can I achieve this? I tried using returning query1 by adding .Except(query2); in the end but I am not sure which is the best method to implement a 'NOT IN' function in LINQ
That 2nd query just looks wrong. Try this:
var query2 = (from tc in Entities.TutorCourses
join c
in Entities.Course
on tc.CourseId equals c.Id
where tc.CourseId == id
select tc.Username).ToList();
var query1 = Entities.User
.Where(u =>
query1.Any(un => un != u.Username) &&
u.Role.Name.Contains("Tutor"))
.Select(u => u.Role);
If this all query are the same type try to you this method
Intersect
You can use the Any to find matches between two lists, then add a ! to specify that you want the ones that don't match. Here's an example that should work for you:
var excludedList = query1.Where(x => !query2.Any(y => y.Id == x.Id));
How about query1.Where(c=> !query2.Contains(c));
I'm using LINQ on an IQueryable returned from NHibernate and I need to select the row with the maximum value(s) in a couple of fields.
I've simplified the bit that I'm sticking on. I need to select the one row from my table with the maximum value in one field.
var table = new Table { new Row(id: 1, status: 10), new Row(id: 2, status: 20) }
from u in table
group u by 1 into g
where u.Status == g.Max(u => u.Status)
select u
This is incorrect but I can't work out the right form.
BTW, what I'm actually trying to achieve is approximately this:
var clientAddress = this.repository.GetAll()
.GroupBy(a => a)
.SelectMany(
g =>
g.Where(
a =>
a.Reference == clientReference &&
a.Status == ClientStatus.Live &&
a.AddressReference == g.Max(x => x.AddressReference) &&
a.StartDate == g.Max(x => x.StartDate)))
.SingleOrDefault();
I started with the above lambda but I've been using LINQPad to try and work out the syntax for selecting the Max().
UPDATE
Removing the GroupBy was key.
var all = this.repository.GetAll();
var address = all
.Where(
a =>
a.Reference == clientReference &&
a.Status == ClientStatus.Live &&
a.StartDate == all.Max(x => x.StartDate) &&
a.AddressReference == all.Max(x => x.AddressReference))
.SingleOrDefault();
I don't see why you are grouping here.
Try this:
var maxValue = table.Max(x => x.Status)
var result = table.First(x => x.Status == maxValue);
An alternate approach that would iterate table only once would be this:
var result = table.OrderByDescending(x => x.Status).First();
This is helpful if table is an IEnumerable<T> that is not present in memory or that is calculated on the fly.
You can also do:
(from u in table
orderby u.Status descending
select u).Take(1);
You can group by status and select a row from the largest group:
table.GroupBy(r => r.Status).OrderByDescending(g => g.Key).First().First();
The first First() gets the first group (the set of rows with the largest status); the second First() gets the first row in that group.
If the status is always unqiue, you can replace the second First() with Single().
Addressing the first question, if you need to take several rows grouped by certain criteria with the other column with max value you can do something like this:
var query =
from u1 in table
join u2 in (
from u in table
group u by u.GroupId into g
select new { GroupId = g.Key, MaxStatus = g.Max(x => x.Status) }
) on new { u1.GroupId, u1.Status } equals new { u2.GroupId, Status = u2.MaxStatus}
select u1;
What about using Aggregate?
It's better than
Select max
Select by max value
since it only scans the array once.
var maxRow = table.Aggregate(
(a, b) => a.Status > b.Status ? a : b // whatever you need to compare
);
More one example:
Follow:
qryAux = (from q in qryAux where
q.OrdSeq == (from pp in Sessao.Query<NameTable>() where pp.FieldPk
== q.FieldPk select pp.OrdSeq).Max() select q);
Equals:
select t.* from nametable t where t.OrdSeq =
(select max(t2.OrdSeq) from nametable t2 where t2.FieldPk= t.FieldPk)
Simply in one line:
var result = table.First(x => x.Status == table.Max(y => y.Status));
Notice that there are two action.
the inner action is for finding the max value,
the outer action is for get the desired object.
I have the following query:
from p in dataContext.Repository<IPerson>()
join spp1 in dataContext.Repository<ISportsPerPerson>() on p.Id equals spp1.PersonId
join s1 in dataContext.Repository<ISports>() on spp1.SportsId equals s1.Id
join spp2 in dataContext.Repository<ISportsPerPerson>() on p.Id equals spp2.PersonId
join s2 in dataContext.Repository<ISports>() on spp2.SportsId equals s2.Id
where s1.Name == "Soccer" && s2.Name == "Tennis"
select new { p.Id };
It selects all the person who play Soccer and Tennis.
On runtime the user can select other tags to add to the query, for instance: "Hockey". now my question is, how could I dynamically add "Hockey" to the query? If "Hockey" is added to the query, it would look like this:
from p in dataContext.Repository<IPerson>()
join spp1 in dataContext.Repository<ISportsPerPerson>() on p.Id equals spp1.PersonId
join s1 in dataContext.Repository<ISports>() on spp1.SportsId equals s1.Id
join spp2 in dataContext.Repository<ISportsPerPerson>() on p.Id equals spp2.PersonId
join s2 in dataContext.Repository<ISports>() on spp2.SportsId equals s2.Id
join spp3 in dataContext.Repository<ISportsPerPerson>() on p.Id equals spp3.PersonId
join s3 in dataContext.Repository<ISports>() on spp3.SportsId equals s3.Id
where s1.Name == "Soccer" && s2.Name == "Tennis" && s3.Name == "Hockey"
select new { p.Id };
It would be preferable if the query is build up dynamically like:
private void queryTagBuilder(List<string> tags)
{
IDataContext dataContext = new LinqToSqlContext(new L2S.DataContext());
foreach(string tag in tags)
{
//Build the query?
}
}
Anyone has an idea on how to set this up correctly?
Thanks in advance!
A LINQ query is not parsed until it is actually executed. So you can do stuff like this:
var q = from r in ctx.records
/* Do other stuff */
select r;
if (!string.IsNullOrEmpty(search)) {
q = from r in q
where r.title == search
select r;
}
if (orderByName) {
q = q.OrderBy(r => r.name);
}
/* etc */
this will create one SQL statement being executed.
For your specific question: The joins make it somewhat complicated, but I think you can join with other "dynamic" queries.
So you would end up with something like this:
var baseQ = from p in dataContext.Repository<IPerson>()
select p;
foreach(var tag in tags) {
baseQ = from p in baseQ
join spp1 in dataContext.Repository<ISportsPerPerson>() on p.Id equals spp1.PersonId
join s1 in dataContext.Repository<ISports>() on spp1.SportsId equals s1.Id
where s1.name == tag
select p;
}
/* If you have defined your relations correct, simplify to something like this.
Does not actually work because of SportsPerPerson probably has multiple sports: */
foreach(var tag in tags) {
baseQ = baseQ.Any(p => p.SportsPerPerson.Sports.Name == tag);
}
var resultQ = from p in baseQ
select new { p.Id };
Me and my colleague have found the solution, and we refactored the query so it would work properly. We now use the following query to retrieve the correct resultset:
var query = dataContext.Repository<ILead>();
foreach (var tag in tags)
{
String tagName = tag;
query = query.Where(l => dataContext.Repository<ISportsPerPerson>()
.Any(tpl => tpl.PersonId.Equals(l.Id) && tpl.Sports.Name.Equals(tagName)));
}
// Do something with query resultset :]