Subsonic/Linq Not a sequence error what does it mean - linq

Hi I am getting this error.
The expression of type 'System.Collections.Generic.IEnumerable`1[System.String]' is not a sequence.
this is my code
_session.All<Sentence>()
.Select(T => new { Sentence = T, Descriptions = T.Sentence.Split(' ') })
.Where(S => S.Descriptions .Intersect(words).Any())
.Select(R => R.Sentence)
.Distinct();
words is a list of string.
what does not a sequence mean and how do i fix it.

EDIT: Okay, now that the question's been corrected...
I suspect this is a Subsonic restriction. You might try this instead:
.Where(S => S.Descriptions.Any(x => words.Contains(x)))

Related

Add non existing item to list

I am trying to fill a list with objects which haven't been added yet by random to a list. So I loop rInt times through a list and want to pick randomly objects and add them to the list if they does not already exist:
collectionList = new List<CollectionSccmCM>();
Random r = new Random();
int rInt = r.Next(0, 5);
for(int i=0; i<=rInt; i++){
collectionList.Add(_context.CollectionApplications.OrderBy(x => Guid.NewGuid()).Where(x => collectionList.Any(y => y.CollectionID !=(x.collection_id.ToString()))).Select(x => new CollectionSccmCM(){CollectionID= x.collection_id.ToString(), Name=x.collection_name}).FirstOrDefault());
}
I seems that I have a mistake in the orderby and where part, but I cannot figure out the error. When I put a toList between I dont receive any syntax error anymore, but also doesn't work.
Any tip what I am doing wrong?
Thanks
Edit:
I did a mistake and had to use contains, but still not working:
collectionList.Add(_context.CollectionApplications.OrderBy(x => Guid.NewGuid()).Where(x => collectionList.Any(y => !y.CollectionID.Contains(x.collection_id.ToString()))).Select(x => new CollectionSccmCM(){CollectionID= x.collection_id.ToString(), Name=x.collection_name}).FirstOrDefault());
Edit:
Got it working with a select, but not so happy with it and dont understand why the otherone wasnt working.
collectionList.Add(_context.CollectionApplications.OrderBy(x => Guid.NewGuid()).Where(x => !collectionList.Select(y => y.CollectionID).ToList().Contains(x.collection_id.ToString())).Select(x => new CollectionSccmCM(){CollectionID= x.collection_id.ToString(), Name=x.collection_name}).FirstOrDefault());
I would strongly suggest you consider adding some whitespace to your LINQ. I would break your first example down as follows:
collectionList.Add(
_context.CollectionApplications
.OrderBy(x => Guid.NewGuid())
.Where(x => collectionList.Any(y => y.CollectionID !=(x.collection_id.ToString())))
.Select(x => new CollectionSccmCM() {
CollectionID = x.collection_id.ToString(),
Name = x.collection_name
}).FirstOrDefault()
);
Looking at your Where call, you are including in the possible elements to add, those elements where any of the collection IDs doesn't match (collectionList.Any(y => ... )). That's all of them (unless you only have one element in collectionList).
You probably want to use All instead of Any -- where all of the collection IDs don't match:
.Where(x => collectionList.All(y => y.CollectionID != x.collection_id.ToString()))

Using Linq, How can I properly append multiple Where clauses so that they appear in the appropriate order?

The order I would like the end result to appear in is Exact Matches first given a input string, followed by other matches that are Contains for a given field. I tried to approach this in a very rudimentary way as shown here in this example:
var raw = Model.SearchResults.Where(m => m.EffectiveDateTime != null).OrderBy(m => m.EffectiveDateTime).ToList();
var exact = raw.Where(m => m.IssueNumber.ToLower() == Model.SearchText.ToLower());
var contains = raw.Where(m => m.IssueNumber.ToLower().Contains(Model.SearchText.ToLower()));
var list = exact.Union(contains);
This approach seems like it'd be a really bad way to do this. In fact, the Union portion seems to effectively crash my application. Is there an opposite to Intersection which would give me the remaining results outside the Exact matches that I could then append to a final list so that the order would be Exact Matches followed by StartsWith matches followed finally by Contains matches in that descending order?
To answer your original question, you can use a temporary expression to classify the match types, then order by the match type and other criteria, and it will translate to SQL as well:
var st = Model.SearchText.ToLower();
var list = Model.SearchResults.Where(m => m.EffectiveDateTime != null)
.Select(m => new {
m,
im = m.IssueNumber.ToLower()
})
.Select(mim => new {
mim.m,
Rank = mim.im == st ? 1 : mim.im.StartsWith(st) ? 2 : mim.im.Contains(st) ? 3 : 4
})
.Where(mr => mr.Rank < 4)
.OrderBy(mr => mr.Rank)
.ThenBy(mr => mr.m.EffectiveDateTime)
.Select(mr => mr.m)
.ToList();
I did the double Select to emulate let from fluent syntax, which I think is a bit clearer than lambda syntax in this case:
var lisx = (from m in Model.SearchResults
where m.EffectiveDateTime != null
let im = m.IssueNumber.ToLower()
let Rank = im == st ? 1 : im.StartsWith(st) ? 2 : im.Contains(st) ? 3 : 4
where Rank < 4
orderby Rank, m.EffectiveDateTime
select m)
.ToList();
Also, if you do the whole query in the database, the ToLower is likely unnecessary, as the default for SQL is probably to be case-insensitive anyway.
Actually, I went back to the drawing board and figured it out. This is a little bit better for me and returns the results I needed.
var list = Model.SearchResults
.Where(e => e.A.ToLower().Contains(Model.SearchText.ToLower()))
.GroupBy(d => new { d.A, d.B, d.C})
.OrderBy(x => x.Key.A)
.ThenBy(x => x.Key.B)
.ThenBy(x => x.Key.C)
.Select(x => new
{
A= x.Key.A,
B= x.Key.B,
C= x.Key.C
})
.ToList();

Linq All() / Any() but not empty

I have a Linq expression that is used in a few places. I went down the expression route as there wasn't a logical way to accomplish some searching logic without enumerating a very large table otherwise.
private Expression<Func<Property, bool>> PropertyIsCompliant()
{
return (p) => p.CalculationSets.OfType<SingleDocumentCalculationSet>()
.GroupBy(cs => cs.SourceDocument)
.Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults)
.SelectMany(cr => cr)
.All(cr => cr.Outcome == CalculationOutcome.Success);
}
My models are as such:
A Property has many CalculationSets
Each CalculationSet is also assigned to a Document
Each CalculationSet has a number of CalculationResults
Each CalculationResult has an Outcome
I'm trying to create an expression that will tell me if all the outcomes from the most recent calculationsets grouped by document ordered by most recent (ie the most recent distinct results) are Successful.
I can the SelectMany clause returns all the CalculationResults from the correct CalculationSets.
I just cant figure out how to return true ONLY if the collection isn't empty AND they are all Outcome.Success.
I understand the All operator automatically returns true on an empty collection. I just can't think of a way around it!
So your real condition is that there are not any unsuccessful outcomes. In that case use Any and reverse the condition:
//V-- notice the ! inverse operator here
return (p) => !(p.CalculationSets.OfType<SingleDocumentCalculationSet>()
.GroupBy(cs => cs.SourceDocument)
.Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults)
.SelectMany(cr => cr)
.Any(cr => cr.Outcome != CalculationOutcome.Success));
var countsBySuccess =
...
.GroupBy(cr => cr.Outcome == CalculationOutcome.Success) //group on success
.Select(g => new { IsSuccessful = g.Key, Count = g.Count() });
You can now examine the two result rows to make sure that the unsuccessful count is zero and the successful count is non-zero.
Regarding performance, this will need to materialize the entire result set server-side and aggregate it. But it does so only once.
If you must use the calculation result as part of a bigger query, you must use another trick:
!countsBySuccess.Any(g =>
g.IsSuccessful && Count == 0 ||
!g.IsSuccessful && Count != 0)
This boolean expression determines whether the condition you are looking for holds with one scan of the data.
It is important to only scan the data once. Do not simply write:
myItems.All(cr => cr.Outcome == CalculationOutcome.Success) && myItems.Any()
Because that does two scans. SQL Server does not optimize this out.
I think you're answering your question - if you know that All returns TRUE for empty then you have two checks to make. Excuse my C# (I'm not sure on the var query assignment, hopefully you get the idea) but you could do something like this:
private Expression<Func<Property, bool>> PropertyIsCompliant()
{
var query = (p) => p.CalculationSets.OfType<SingleDocumentCalculationSet>()
.GroupBy(cs => cs.SourceDocument)
.Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults)
.SelectMany(cr => cr);
return (query.Count > 0) & query.All(cr => cr.Outcome == CalculationOutcome.Success);
}
I didn't realise it was possible to use "&&" in expressions. So I've managed to combine 2 separate expressions that give the answer I need. The "&&" only returns true when both expressions evaluate "true"
return (p) =>
p.CalculationSets.OfType<SingleDocumentCalculationSet>()
.GroupBy(cs => cs.SourceDocument)
.Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults)
.SelectMany(cr => cr).Any()
&&
p.CalculationSets.OfType<SingleDocumentCalculationSet>()
.GroupBy(cs => cs.SourceDocument)
.Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults)
.SelectMany(cr => cr)
.All(cr => cr.Outcome == CalculationOutcome.Success);

Linq To Entities Optional Distinct

Earlier I put a question on Stackoverflow about how to remove duplicate records in a list of objects, based on a particular property within each object.
I got the answer I was looking for (see below), a query which returns a distinct list of objects using MainHeadingID as the property to remove duplicates.
public IList<tblcours> GetAllCoursesByOrgID(int id)
{
return _UoW.tblcoursRepo.All.
Where(c => c.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id))
.GroupBy(c => c.MainHeadingID)
.Select(g => g.FirstOrDefault())
.ToList();
}
However, now I need more help! Is there anyway of amending the query above so that, it only removes duplicate values when MainHeadingID is not equal to 180. I tried amending GroupBy line to
.GroupBy(c => c.MainHeadingID != 180)
However, this didn't work.
Any help would be much appreciated with this.
Thanks.
Following works for LINQ to SQL:
return _UoW.tblcoursRepo.All
.Where(c => c.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id))
.GroupBy(c => c.MainHeadingID)
//.SelectMany(g => g.Key == 180 ? g : g.Take(1))
.SelectMany(g => g.Take(g.Key == 180 ? Int32.MaxValue : 1))
.ToList();
Comments: SelectMany in query above selects all items from group where MainHeadingID equals to 180, but it takes only one item form other groups (i.e. distinct result). Linq to SQL cannot translate commented out part, but thanks to #usr there is way around.
Linq to Entities cannot translate even simplified query. I think only option for you in this case is simple concating result of two queries:
Expression<Func<tblcours, bool>> predicate = x =>
x.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id)
int headingId = 180;
return _UoW.tblcoursRepo.All
.Where(c => c.MainHeadingID != headingId)
.Where(predicate)
.GroupBy(c => c.MainHeadingID)
.Select(g => g.FirstOrDefault())
.Concat(_UoW.tblcoursRepo.All
.Where(c => c.MainHeadingID == headingId)
.Where(predicate))
.ToList();
lazyberezovsky's answer fails due to an EF bug (which is not surprising given the quality of EF's LINQ support). It can be made to work with a hack:
.SelectMany(g => g.Key == 180 ? g.Take(int.MaxValue) : g.Take(1))
or
.SelectMany(g => g.Take(g.Key == 180 ? int.MaxValue : 1))
Note that performance will not be particularly good due to the way this is translated to SQL.

Finding n-Most Popular Using Linq

How should I structure a Linq query to return a List or Ienumerable of the most popular Tags in my db (I am using EF4.1 by the way).
Currently I have:
var tagsListing = db.Tags
.GroupBy(q => q.Name)
.OrderByDescending(gp => gp.Count())
.Take(5)
.Select();
I think I am part of the way there, but I am unsure of how to structure the Select statement...
Your Select call could look like this:
.Select(gp => gp.Key)
That will give you an IEnumerable<string> of your most popular tags (assuming that Name is a string).
Assuming you want the name and the count, just:
.Select(g => new { Name = g.Key, Count = g.Count() });
EDIT: If you want the complete tags as well, you could use:
.Select(g => new { Tags = g, Count = g.Count() })
which would give you a sequence of groups of tags, all with the same name within a group. Or you might only want the first tag within each group, e.g.
.Select(g => g.First())
It's not clear what a Tag consists of, or what exactly you want in the results.
You've written a perfectly workable query and do not need to call .Select
IQueryable<IGrouping<string, Tag>> tagsListing = db.Tags
.GroupBy(q => q.Name)
.OrderByDescending(gp => gp.Count())
.Take(5);
List<IGrouping<string, Tag>> results = tagListing.ToList();
You probably want to select the names like this:
.Select(gp => gp.Key);

Resources