Sequence contains no elements? - c#

I'm currently using a single query in two places to get a row from a database.
BlogPost post = (from p in dc.BlogPosts
where p.BlogPostID == ID
select p).Single();
The query is fine when retrieving the row to put data in to the text boxes, but it returns an error "Sequence contains no elements" when used to retrieve the row in order to edit it and put it back in to the database. I can't understand why it might find an appropriate row in one instance but not another.
(Using ASP.NET MVC and LINQ)

From "Fixing LINQ Error: Sequence contains no elements":
When you get the LINQ error "Sequence contains no elements", this is usually because you are using the First() or Single() command rather than FirstOrDefault() and SingleOrDefault().
This can also be caused by the following commands:
FirstAsync()
SingleAsync()
Last()
LastAsync()
Max()
Min()
Average()
Aggregate()

Please use
.FirstOrDefault()
because if in the first row of the result there is no info this instruction goes to the default info.

Well, what is ID here? In particular, is it a local variable? There are some scope / capture issues, which mean that it may be desirable to use a second variable copy, just for the query:
var id = ID;
BlogPost post = (from p in dc.BlogPosts
where p.BlogPostID == id
select p).Single();
Also; if this is LINQ-to-SQL, then in the current version you get a slightly better behaviour if you use the form:
var id = ID;
BlogPost post = dc.BlogPosts.Single(p => p.BlogPostID == id);

In addition to everything else that has been said, you can call DefaultIfEmpty() before you call Single(). This will ensure that your sequence contains something and thereby averts the InvalidOperationException "Sequence contains no elements". For example:
BlogPost post = (from p in dc.BlogPosts
where p.BlogPostID == ID
select p).DefaultIfEmpty().Single();

This will solve the problem,
var blogPosts = (from p in dc.BlogPosts
where p.BlogPostID == ID
select p);
if(blogPosts.Any())
{
var post = blogPosts.Single();
}

I had a similar situation on a function that calculates the average.
Example:
ws.Cells[lastRow, startingmonths].Value = lstMediaValues.Average();
Case Solved:
ws.Cells[lastRow, startingmonths].Value = lstMediaValues.Count == 0 ? 0 : lstMediaValues.Average();

Reason for error:
The query from p in dc.BlogPosts where p.BlogPostID == ID select p returns a sequence.
Single() tries to retrieve an element from the sequence returned in step1.
As per the exception - The sequence returned in step1 contains no elements.
Single() tries to retrieve an element from the sequence returned in step1 which contains no elements.
Since Single() is not able to fetch a single element from the sequence returned in step1, it throws an error.
Fix:
Make sure the query (from p in dc.BlogPosts where p.BlogPostID == ID select p)
returns a sequence with at least one element.

Related

Linq query “where [column] in (list of values)” that return single value for each values?

Let say we have this LinQ code (in LinqPad):
List<string> list = new List<string>();
list.Add("1611080010");
list.Add("1611080011");
list.Add("WRONGID");
var result = Orders.AsQueryable().Where(y => list.Contains(y.Id));
// And yes my Ids are string for this sample
result.Dump(); // To display the result in LinqPad
result.Count(); // equal 2
Is it possible to improve this query to force the system to return one element for each of my list element or throw an exception? So if I have 3 values in my list I should have 3 values in my result?
On your question: Is it possible to improve this query to force the system to return one element for each of my list element or throw an exception:
var result = Orders.AsQueryable().SingleOrDefault(y => list.Contains(y.Id));
SingleOrDefault
Returns the only element of a sequence, or a default value if the
sequence is empty; this method throws an exception if there is more
than one element in the sequence.
You can use any one from below one
Whenever you use SingleOrDefault, you clearly state that the query should result in at most a single result. On the other hand, when FirstOrDefault is used, the query can return any amount of results but you state that you only want the first one.
I personally find the semantics very different and using the appropriate one, depending on the expected results, improves readability.
var result = Orders.AsQueryable().SingleOrDefault(y => list.Contains(y.Id));
var result = Orders.AsQueryable()..FirstOrDefault(y => list.Contains(y.Id));
If I understand your question correctly you want to return one object from Orders where the Id of the object equals an Id in your list and if an Id in the list is not present in the Orders object an exception should be thrown.
To achieve that you could do:
var result = list.Select(id => Orders.AsQueryable().First(y => y.Id == id)).ToList();
This will throw an exception if an Id from the list has no match in the Orders object. If all Ids are found then your result will contain the same number of elements as there are Ids in the list.
Another option would be to use:
var result = list.Select(id => Orders.AsQueryable().FirstOrDefault(y => y.Id == id)).ToList();
This would not throw an error, but it would always return the same number of elements as there are Ids in the list. The Ids that would not be found would have a null entry though.

How to do a LINQ query with Split operation

I have the following linq query:
MyClass myobj = (from p in Session.All<MyClass>()
where p.tags.Split(' ').Contains(searchTag)
select p).FirstOrDefault();
When I run this, I get:
System.NotSupportedException: LINQ to Entities does not recognize the
method 'System.String[] Split(Char[])' method, and this method cannot
be translated into a store expression.
What is a good way to do what I am attempting to do?
The real problem here is the db design, but assuming you have no control over this one idea is to split the query.
First retrieve any rows that contain the search tag anywhere within them.
List<MyClass> myobjs = (from p in Session.All<MyClass>()
where p.tags.Contains(searchTag)
select p).ToList();
Then perform the correct tag search on the retrieved objects in memory.
MyClass myobj = myobjs.FirstOrDefault(m => m.tags.Split(' ').Contains(searchTag));
So, assuming tags = tag1 tag2 tag3, and searchTag is tag2 you can just do:
MyClass myobj = (from p in Session.All<MyClass>()
where p.tags.IndexOf(searchTag) > -1
and searchTag.IndexOf(" ") == -1
select p).FirstOrDefault();

Linq Union: How to add a literal value to the query?

I need to add a literal value to a query. My attempt
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
In the above example, I get an error:
"Local sequence cannot be used in LINQ to SQL implementation
of query operators except the Contains() operator."
If I am using Entity Framework 4 for example, what could I add to the Union statement to always include the "seed" ID?
I am trying to produce SQL code like the following:
select distinct ID
from product
union
select 0 as ID
So later I can join the list to itself so I can find all values where the next highest value is not present (finding the lowest available ID in the set).
Edit: Original Linq Query to find lowest available ID
var skuQuery = Context.Products
.Where(p => p.sku > skuSeedStart &&
p.sku < skuSeedEnd)
.Select(p => p.sku).Distinct();
var lowestSkuAvailableList =
(from p1 in skuQuery
from p2 in skuQuery.Where(a => a == p1 + 1).DefaultIfEmpty()
where p2 == 0 // zero is default for long where it would be null
select p1).ToList();
var Answer = (lowestSkuAvailableList.Count == 0
? skuSeedStart :
lowestSkuAvailableList.Min()) + 1;
This code creates two SKU sets offset by one, then selects the SKU where the next highest doesn't exist. Afterward, it selects the minimum of that (lowest SKU where next highest is available).
For this to work, the seed must be in the set joined together.
Your problem is that your query is being turned entirely into a LINQ-to-SQL query, when what you need is a LINQ-to-SQL query with local manipulation on top of it.
The solution is to tell the compiler that you want to use LINQ-to-Objects after processing the query (in other words, change the extension method resolution to look at IEnumerable<T>, not IQueryable<T>). The easiest way to do this is to tack AsEnumerable() onto the end of your query, like so:
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().AsEnumerable().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
Up front: not answering exactly the question you asked, but solving your problem in a different way.
How about this:
var a = Products.Select(p => p.sku).Distinct().ToList();
a.Add(0);
a.Dump(); // LinqPad's way of showing the values
You should create database table for storing constant values and pass query from this table to Union operator.
For example, let's imagine table "Defaults" with fields "Name" and "Value" with only one record ("SKU", 0).
Then you can rewrite your expression like this:
var zero = context.Defaults.Where(_=>_.Name == "SKU").Select(_=>_.Value);
var result = context.Products.Select(p => p.sku).Distinct().Union(zero).ToList();

Sequence contains no matching element

I have an asp.net application in which I am using linq for data manipulation. While running, I get the exception "Sequence contains no matching element".
if (_lstAcl.Documents.Count > 0)
{
for (i = 0; i <= _lstAcl.Documents.Count - 1; i++)
{
string id = _lstAcl.Documents[i].ID.ToString();
var documentRow = _dsACL.Documents.First(o => o.ID == id);
if (documentRow !=null)
{
_lstAcl.Documents[i].Read = documentRow.Read;
_lstAcl.Documents[i].ReadRule = documentRow.ReadRule;
_lstAcl.Documents[i].Create= documentRow.Create;
_lstAcl.Documents[i].CreateRule = documentRow.CreateRule;
_lstAcl.Documents[i].Update = documentRow.Update;
_lstAcl.Documents[i].UpdateRule = documentRow.UpdateRule;
_lstAcl.Documents[i].Delete = documentRow.Delete;
_lstAcl.Documents[i].DeleteRule = documentRow.DeleteRule;
}
}
}
Well, I'd expect it's this line that's throwing the exception:
var documentRow = _dsACL.Documents.First(o => o.ID == id)
First() will throw an exception if it can't find any matching elements. Given that you're testing for null immediately afterwards, it sounds like you want FirstOrDefault(), which returns the default value for the element type (which is null for reference types) if no matching items are found:
var documentRow = _dsACL.Documents.FirstOrDefault(o => o.ID == id)
Other options to consider in some situations are Single() (when you believe there's exactly one matching element) and SingleOrDefault() (when you believe there's exactly one or zero matching elements). I suspect that FirstOrDefault is the best option in this particular case, but it's worth knowing about the others anyway.
On the other hand, it looks like you might actually be better off with a join here in the first place. If you didn't care that it would do all matches (rather than just the first) you could use:
var query = from target in _lstAcl.Documents
join source in _dsAcl.Document
where source.ID.ToString() equals target.ID
select new { source, target };
foreach (var pair in query)
{
target.Read = source.Read;
target.ReadRule = source.ReadRule;
// etc
}
That's simpler and more efficient IMO.
Even if you do decide to keep the loop, I have a couple of suggestions:
Get rid of the outer if. You don't need it, as if Count is zero the for loop body will never execute
Use exclusive upper bounds in for loops - they're more idiomatic in C#:
for (i = 0; i < _lstAcl.Documents.Count; i++)
Eliminate common subexpressions:
var target = _lstAcl.Documents[i];
// Now use target for the rest of the loop body
Where possible use foreach instead of for to start with:
foreach (var target in _lstAcl.Documents)
Use FirstOrDefault. First will never return null - if it can't find a matching element it throws the exception you're seeing.
_dsACL.Documents.FirstOrDefault(o => o.ID == id);
From the MSDN library:
The First<TSource>(IEnumerable<TSource>) method throws an exception if source contains no elements. To instead return a default value when the source sequence is empty, use the FirstOrDefault method.
For those of you who faced this issue while creating a controller through the context menu, reopening Visual Studio as an administrator fixed it.
Maybe using Where() before First() can help you, as my problem has been solved in this case.
var documentRow = _dsACL.Documents.Where(o => o.ID == id).FirstOrDefault();

LINQ to XML + left join + group by = fail

I have two tables:
block: int id, [other columns...]
blockInstance: int id, int blockId (references Block.Id), [other columns...]
My goal is to generate an enumeration of block objects with two properties: (1) Id (the Id of the Block) and (2) InstanceCount (the number of Instances for the Block). Currently, my XML file contains no Block Instances (that table exists but has zero rows).
Here's my (non-working) query:
var groupedBlocks =
from
b in _tableDictionary["block"].Elements()
join
bi in _tableDictionary["blockInstance"].Elements()
on b.Element(_ns + "id").Value equals bi.Element(_ns + "blockId").Value into j
from
lj in j.DefaultIfEmpty()
group
lj by b.Element(_ns + "id").Value into g
select new
{
Id = g.Key,
InstanceCount = g.Count(i => i.Element(_ns + "blockId") != null)
};
The problem is with the predicate (lambda expression) for g.Count(). If I remove the predicate and just use g.Count(), the query generates the proper number of rows, but the InstanceCount for each row is 1, which is wrong (it should be zero). With the predicate in there, the query return zero rows, and if I attempt to view the ResultsView in the debugger, it says "Exception: object reference not set to an instance of an object".
My greatest confusion is what exactly is "i" in my lambda expression. I know it's an XElement, but what does this XElement contain when the data is the result of a join (in this case, a left outer join)?
Okay, well I got one more idea while I was typing this out, and it actually worked :-), but I have no idea why :-(, so I'm still going to ask the question ;-).
If I change the offending code to...
InstanceCount = g.Count(i => i != null)
...it works. But why?! Again, what is getting passed into the lamba expression? And why is it null?
Thanks!
i as referenced in your group count is equivalent to lj, which is returned from enumerating j.DefaultIfEmpty().
DefaultIfEmpty() in this context will return the default value for the type of the item, where the item does not have an output match from the previous join..into statement.
In the case of reference types (XElement is a reference type), the default value is always null, which is why you are getting a null reference exception, and why checking for null first removes the problem.
EDIT:
An alternative method that avoids the grouping by using a sub-query:
var groupedBlocks =
from b in _tableDictionary["block"].Elements()
select new
{
Id = b.Element(_ns + "id").Value,
InstanceCount = (from bi in _tableDictionary["blockInstance"].Elements()
where bi.Element(_ns + "blockId").Value ==
b.Element(_ns + "id").Value
select bi).Count()
};
The parameter passed to the Count lambda is the same type of the one you are grouping. That's because you are calling Count in the context of g which is an IGrouping<string, XElement>.
So, i is XElement.
In your first query you were actually checking for the first child of the grouped XElement, (accessing the i.Element method), that's why it didn't worked.

Categories