.NET IQueryable OrderBy Aggregated Field - c#

Is there any way to perform an OrderBy with an aggregated field (string.Join)?
var query = <Some IQueryable<T>>;
query = query.OrderBy(x => string.Join(",", x.Values.Select(v => v.Name)));
I am building dynamic queries and came up a need to order by a column that is agreggated like the above example.
Thanks in advance

You need to first project the aggregated column before you can do OrderBy.
query = query.Select(x=>new {
Item = x,
Agg = x => string.Join(",", x.Values.Select(v => v.Name))
}).OrderBy(a=>a.Agg).Select(a=>a.Item)

Related

How to filter a sublist inside parent and return parent with sublist filtered

I want to create a linq to sql query that will return a list of objects with a sublist that has been filtered.
It sounds easy but I'm not sure how to make this to work
Here the SQL Query which returns what I want:
select * from Texts t inner join Translations tt on t.TranslationId = tt.Id
inner join Pages p on tt.Id = p.TranslationId and tt.NeutralText = p.TitleNeutralTextId
where t.LanguageId = 1
Now I have to write this with linq.
What I've done so far is:
var query = this.Queryable() // Page entity
.AsNoTracking()
.Include(x => x.TitleTranslation.Texts);
return (from m in query
from l in m.TitleTranslation.Texts
where m.TitleTranslation.Texts.Any(l => l.LanguageId == 1)
select m);
But it didn't work because I got the sublist with all languages instead of language with id #1 only.
Thanks for helping,
David
Any specific reason you are writing query? Either you can use Eager Loading of EF to load all the child tables, Or below Linq statement can fetch the required result
var result = texts.Join(translations, t => t.TranslationId, tt => tt.Id, (t, tt) => new {t, tt})
.Join(pages, ttt => new { Id = ttt.tt.Id, NeutralTextId = ttt.tt.NeutralText }, p => new { Id = p.TranslationId, NeutralTextId = p.TitleNeutralTextId }, (ttt, p) => new {ttt, p})
.Where(tttt => tttt.ttt.t.LanguageId == 1);
Here replace texts, translations and pages with actual dbContext entities collection property.
I think you must try lime this. this will work for you .
This will be similar to sql query
One way to do this .
var result = from m in Texts
join Translations on Texts.TranslationId = Translation.Id
Join Pages on Translations.NeutralText = Pages.NeutralText
where Texts.LanguageId = 1
select m
There an other way to do this using entity framework
var result =
this.Queryable().AsNoTracking().Include(x=>x.Translations).Where(x=>x.LanguageId= 1)
I found the solution I wanted thanks to Hasnain Bukhari.
The solution was to start from the text table, assign the filter, include the desired Entity (Page) and put the results into memory (ToList()). Then select pages. It will give the result I want in the order I have to.
var query = textService.Queryable()
.AsNoTracking()
.Include(x => x.Translation.Pages)
.Where(x => x.LanguageId == languageId).ToList();
return query.SelectMany(x => x.Translation.Pages);

Casting Nhibernate result into IDictionary<string,int>

I am trying to convert the result of the query into IDictionary
Here string will contain orderId and the int will contain the TradedQuantity
The query below should join three objects Order, OrderRevision and OrderEvent.
1 Order can have many orderRevisions
1 OrderRevision can have many orderEvents
What the query is trying to do is to inner join three objects and get all order objects whose order id matches the list of orderids supplied to it. Then it does a group by based on orderId and gets the latest TradedQuantity from orderEvents object. LatestTradedQuantity will be the TradedQuantityFrom latest OrderEvent. For now the latest orderevent can be regarded as the one that has highest OrderEventId value.
OrderRevision revisionAlias = null;
Order orderAlias = null;
var query =
Session.QueryOver<OrderEvent>()
.JoinAlias(oe => oe.OrderRevision,() => revisionAlias)
.JoinAlias(oe => oe.OrderRevision.Order,() => orderAlias)
.Where(x => x.OrderRevision.Order.SourceSystem.Name.ToLower() == sourceSystem.ToLower())
.WhereRestrictionOn(x => x.OrderRevision.Order.Id).IsIn(orderIds.ToList())
.SelectList(list => list.SelectGroup(x => x.OrderRevision.Order.SourceOrderIdentifier)
.SelectMax(x => x.Id).Select(x => x.TradedQuantity))
.Select(x => new KeyValuePair<string, int?>(x.OrderRevision.Order.SourceOrderIdentifier, x.TradedQuantity)
);
As this query does not do what is supposed to. Could you please help and let me know how the result can be cast into IDictionary?
You have tagged your question with linq-to-nhibernate, so I guess using it instead of queryover would suit you. With Linq, use a sub-query for selecting the "max" order events ids for each order, then query them and project them to a dictionary.
using System.Linq;
using NHibernate.Linq;
...
var orderEventsIdsQuery = Session.Query<OrderEvent>()
.Where(oe => orderIds.Contains(oe.OrderRevision.Order.Id))
.GroupBy(oe => oe.OrderRevision.Order.SourceOrderIdentifier,
(soi, oes) => oes.Max(oe => oe.Id));
var result = Session.Query<OrderEvent>()
.Where(oe => orderEventsIdsQuery.Contains(oe.Id))
.ToDictionary(oe => oe.OrderRevision.Order.SourceOrderIdentifier,
oe => oe.TradedQuantity);
This should do the job. I do not use QueryOver and I will not try to give an answer for doing it with QueryOver.

Trying to simplify LINQ expression

I'm trying to simplify a LINQ expression but no matter what i try I'm unable to get it to work
var filterProfileIds = filter.Profiles.Select(s => s.ProfileId);
var newList = new List<FileMedia>();
foreach (var item in filterProfileIds)
{
newList.AddRange(query.Where(w => w.Profiles.Select(s => s.ProfileId).Contains(item)));
}
newList.AddRange(query.Where(w => !w.Profiles.Any()));
query = newList.AsQueryable();
query is of type "FileMedia" and has a relation to Profiles.
So what i want is all the results from the query that has the same profiles that filter.profiles has AND i also want all the results from the query that doesnt have any profiles at all.
Try as the below:
var filterProfileIds = filter.Profiles.Select(s => s.ProfileId);
query = query.Where(w =>
!w.Profiles.Any() ||
w.Profiles.Any(i => filterProfileIds.Contains(i.ProfileId))
).ToList();
If I understand correctly the requirement, you could use a combination of Any and All extension methods like this:
query = query.Where(m => !m.Profiles.Any() ||
filterProfileIds.All(id => m.Profiles.Any(p => p.ProfiledId == id)));
This is if you wish to get the items with exact the same profiles as the filter.
If you indeed want to get the item with any profile contained in the filter, then you could use this instead:
query = query.Where(m => !m.Profiles.Any() ||
m.Profiles.Any(p => filterProfileIds.Contains(p.ProfiledId));
Maybe something like this:
query = (from item in filter.Profiles.Select(s => s.ProfileId)
from fileMedia in query
where fileMedia.Profiles.Select(q => q.ProfileId).Contains(item)
select fileMedia).
Concat(query.Where(w => !w.Profiles.Any())).AsQueryable();

Linq Query To Group All Other Than Top 3

I have the following code in Linq, and I was wondering how to make it so that it groups all others beside the top 3 into an others category and sum their volumes.
var list = (from t in sortedCollection.DataItem
orderby t.volume
select t).Take(3);
You need to use Skip to ignore top 3 and group the rest like:
var list = (from t in sortedCollection.DataItem
orderby t.volume
select t).Skip(3);
From the comments, it seems you only want to get the sum of a particular field after skipping first 3 records.
var sum = (from t in sortedCollection.DataItem
orderby t.volume
select t).Skip(3).Sum(r=> r.VOLUME);
Or with a complete method syntax:
var Sum = sortedCollection.DateItem.OrderBy(t => t.volume)
.Skip(3)
.Sum(r=> r.volume);
If you need grouping , that it would look like:
With method syntax it should be something like:
var query = sortedCollection.DateItem.OrderBy(t => t.volume)
.Skip(3)
.GroupBy(t => t.YourGroupingField);
To do Sum based on a field you can do something like:
var query = sortedCollection.DateItem.OrderBy(t => t.volume)
.Skip(3)
.GroupBy(t => t.YourGroupingField)
.Select(grp => new SqlCommand(
{
Key = grp.Key,
Sum = grp.Sum(r=> r.ValueFieldForSum)
}));

Need a linq query where inclusion indicates property matches all values in secondary list

I want to duplicate the following logic in a single query.
var currentRows = resultsTable.AsEnumerable();
foreach (var wholeWord in excludeWholeWords)
{
currentRows = from row in currentRows
where !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))
select row;
}
resultsTable = currentRows.CopyToDataTable();
I had tried the following, but it results in matching if !FoundWholeWord is true for any wholeWord, instead of my intent (which is that a match means !FoundWholeWord is true for ALL items in excludeWholeWords
var matchGvRows = (from wholeWord in excludeWholeWords
from row in gvkeysTable.AsEnumerable()
where !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))
select row).Distinct();
Any ideas?
If I understand the question correctly, it should be something along the lines of:
var newRows = currentRows
.Where(r => !excludeWholeWords.Any(w => w == r.Field<string>("busdescl"));
I don't know what the FoundWholeWord is but if it does anything different than just comparing strings, you can use it like:
var newRows = currentRows
.Where(r => !excludeWholeWords.Any(w => FoundWholeWord(w, r.Field<string>("busdescl")));
How about this?
var matchGvRows = excludeWholeWords.Aggregate(currentRows, (current, wholeWord) => current.Where(row => !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))));
currentRows = excludeWholeWords.Aggregate(currentRows, (current, wholeWord) => (from row in current
where !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))
select row));
That's what ReSharper's "Convert to LINQ expression" does.

Categories

Resources