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();
Related
my aim is to return result via left join by linq. The io.IsDefault can be null but insted of this I want to return MyStronglyTypeObj obj with the rest data.
context.Image.Where(i => i.IsActive == true) have 3 rows. one of those have isDefault null because this ImageId- (io => io.ImageId == i.ImageId) dosent exist in ImageObject
var test2 = (from i in context.Image.Where(i => i.IsActive == true)
from io in ImageObject.Where(io => io.ImageId == i.ImageId).DefaultIfEmpty()
select new MyStronglyTypeObj() { Alt = i.Alt, Caption = i.Caption, DisplayName = i.DisplayName, Extension = i.Extension, IsDefault = io.IsDefault, Height = i.Height, Width = i.Width, Name = i.Name });
// return 2 imgs - the 3rd one without isDefault (isDefault = null) wasn't added to collection.
var test = (from i in context.Image.Where(i => i.IsActive == true)
from io in ImageObject.Where(io => io.ImageId == i.ImageId).DefaultIfEmpty()
select i); // return 3 imgs
Is something obvious to me that I don't see? - perhaps I totally misunderstood the .DefaultIfEmpty() function
please help
DefaultIfEmpty() only affects empty collections, and causes that collection to return a single element with value default(T) (where T == collection type).
For example, using strings (note default(string) == null):
So based on the code you provided:
DefaultIfEmpty() is not a factor
The only other difference is the select statement, which doesn't really make sense
I'm guessing i is type MyStronglyTypeObj (based on properties matching)? I suspect there's another factor when you're running this code that you're not taking into account.
Try putting a breakpoint on that line, and viewing the results in the debugger.
Also, because LINQ uses deferred execution, this query code doesn't actually "run" until it gets consumed, and depending on when that happens, the source data can change (essentially, easily causing timing bugs if you're changing the source data somewhere else). Even more frustrating, this can cause this bug to disappear when you use a debugger and view the results in that, as it causes the code to execute sooner. You can avoid this by adding a .ToList() at the end of the line to cause the results to be executed immediately.
When the linq query condition is not met, I'd expect a null to be returned from questions.FirstOrDefault() - but instead, an exception
Sequence contains no matching element
is thrown. Any ideas why?
var firstQ = questions.FirstOrDefault(a =>
a.Answers.Single(x => x.CourseAssignmentId ==
courseAssignmentId)?.Score == null) ?? questions.FirstOrDefault();
That's the difference between Single and SingleOrDefault.
Single throws an exception if there's any number of items different than 1 that match your predicate.
You should be using FirstOrDefault() instead. BTW you can combine the condition probably like
a.Answers.Single(x => x.CourseAssignmentId == courseAssignmentId && x.Score == null)
As others have already mentioned, it's the expected behavior of Enumerable.Single.
Anyway, it looks like an XY problem. Probably you should store the last scored question somewhere (e.g a dictionary).
That is, you could refactor your code as follows:
var assignmentScoredQuestionMap = new Dictionary<int, Question>();
// Fill the whole dictionary:
// You need to add for which assignment identifier you've answered a question
int assignmentId = 384;
// If the whole assignment exists, you set lastScoredQuestion, otherwise
// you set it to first question.
if(!assignmentScoredQuestionMap.TryGetValue(assignmentId, out var lastScoredQuestion))
lastScoredQuestion = questions.FirstOrDefault();
The following code shows how I am assigning data into IEnumerable<UnCompletedJobDetailsBO>.
There is a list (IEnumerable<UnCompletedJobDetailsBO>) that has another list (List<JobDetailsBO>), with that child list (List<JobDetailsBO>) having a list on it. But the AllocationDetailList only ever has one list item.
public IEnumerable<UnCompletedJobDetailsBO> GetControlDetails(DateTime startDate)
{
var controlDetails =
(from booking in db.BookingDetail
where booking.BookingDateTime >= startDate
orderby booking.DocketNo
select new UnCompletedJobDetailsBO()
{
CustomerName = booking.Customer.Name,
CompanyName = booking.CompanyDetail.Name,
JobList =
(from job in db.BookingJob.Where(x => x.BookingID == booking.BookingID) //get job list
select new JobDetailsBO()
{
JobID = job.JobID,
JobType = job.JobType,
ItemName = job.ItemName,
AllocationDetailList =
(from jobAllocationDetail in db.JobAllocation
join returnUnCollected in db.JobReturnUnCollected
on jobAllocationDetail.JobAllocationDetailID
equals returnUnCollected.JobAllocationDetailID
into returnJob
from returnUnCollected in returnJob.DefaultIfEmpty()
where (jobAllocationDetail.Booking.BookingID == booking.BookingID)
select new AllocationBO()
{
JobUnCollectedID = returnJob.JobUnCollectedID,
JobType = jobAllocationDetail.JobType,
CurrentStatus = jobAllocationDetail.CurrentStatus,
}).DefaultIfEmpty().ToList(),
}).DefaultIfEmpty().ToList(),
}).ToList();
return controlDetails;
}
I want to remove the JobList item if the inner list (AllocationDetailList) item satisfies the condition below. Sometimes AllocationDetailList may be null, so I check that also. But when I write below query, it does not remove that particular JobList item that satisfies the condition. Thanks in advance.
public List<UnCompletedJobDetailsBO> RemovePODFromSelectedList(
List<UnCompletedJobDetailsBO> unCompletedJobDetailsBO)
{
unCompletedJobDetailsBO
.SelectMany(y => y.JobList)
.ToList()
.RemoveAll(x => ((x.AllocationDetailList[0] != null) ?
x.AllocationDetailList[0].JobType == "D" &&
x.AllocationDetailList[0].JobUnCollectedID == null &&
x.AllocationDetailList[0].CurrentStatus == 5 :
x.AllocationDetailList.Count > 1));
return unCompletedJobDetailsBO;
}
Without a good, minimal, complete code example, I'm not sure that any performance concern can be addressed. It's hard enough to fully understand the question as it is, but without being able to actually test the code, to reproduce and observe a specific performance concern, it's hard to know for sure where your concern specifically lies, never mind how to fix it.
That said, from the code you posted, it is clear why items are not being removed from the list. The basic issue is that while the SelectMany() method does have the effect of allowing you to enumerate all of the elements from all of the different JobList objects as a single enumeration, the elements are enumerated as a new enumeration.
When you call ToList(), you are creating a whole new list from that new enumeration, and when you call RemoveAll(), you are only removing elements from that new list, not the lists from which they originally came.
You say you can get it to work with a for loop. I assume you mean something like this:
public List<UnCompletedJobDetailsBO> RemovePODFromSelectedList(
List<UnCompletedJobDetailsBO> unCompletedJobDetailsBO)
{
foreach (var item in unCompletedJobDetailsBO)
{
item.JobList.RemoveAll(x => ((x.AllocationDetailList[0] != null) ?
x.AllocationDetailList[0].JobType == "D" &&
x.AllocationDetailList[0].JobUnCollectedID == null &&
x.AllocationDetailList[0].CurrentStatus == 5 :
x.AllocationDetailList.Count > 1));
}
return unCompletedJobDetailsBO;
}
Note: there is no need to return unCompletedJobDetailsBO. That entire object is unchanged, never mind the variable. The only thing the code is modifying is each individual JobList object within the passed-in object's members. I.e. the above method could actually have a return type of void, and the return statement could be removed entirely.
It is possible you could speed the code up by removing the elements in a different way. The List<T>.RemoveAll() method is in fact reasonably efficient, with O(n) cost. But it still involves copying all of the data in the list after the first element that is removed (so that all the elements are shifted down in the list). If you have to have the list ordered, this may be as good as you can do, but if not, you could process the removal differently, or use a different data structure altogether, something unordered where removal of one or more elements costs less.
But again, without more details and without a good example to work with addressing that particular issue doesn't seem practical here.
The condition
x.AllocationDetailList[0] != null
will throw exception if there is no item in the AllocationDetailList. Instead you need to check
x.AllocationDetailList!=null && x.AllocationDetailList.Count>0.
Also .ToList() after SelectMany in your code will create a new list and items will be removed from that new list instead of unCompletedJobDetailsBO. You need to modify the remove function as below
unCompletedJobDetailsBO.ForEach(y => y.JobList.RemoveAll(x => ((x.AllocationDetailList != null && x.AllocationDetailList.Count>0)
?
x.AllocationDetailList[0].JobType == "D"
&& x.AllocationDetailList[0].JobUnCollectedID == null
&& x.AllocationDetailList[0].CurrentStatus == "5"
:
x.AllocationDetailList.Count > 1
)
));
I'd like to get the index of a DataGridViewRow, where the value of its first column matches.
My code so far:
string SearchForThis = "test";
int index = from r in dgv.Rows
where r.Cells[0].Value == SearchForThis
select r.Index;
The compiler error:
Could not find an implementation of the query pattern for source type 'System.Windows.Forms.DataGridViewRowCollection'. 'Where' not found. Consider explicitly specifying the type of the range variable 'r'.
DataGridViewRowCollection doesn't implement IEnumerable<T>, that is why you can't use LINQ, use Enumerable.Cast method.
int index = (dgv.Rows.Cast<DataGridViewRow>()
.Where(r => r.Cells[0].Value == SearchForThis)
.Select(r => r.Index)).First();
Or with Query Syntax:
int index = (from r in dgv.Rows.Cast<DataGridViewRow>()
where r.Cells[0].Value == SearchForThis
select r.Index).First();
You will need to get a single result back from the collection, that is why I have used First, but remember if there are no items matching the criteria, it will throw an exception. To overcome that see the solution at the end of answer.
See: Enumerable.Cast<TResult> Method
The Cast<TResult>(IEnumerable) method enables the standard query
operators to be invoked on non-generic collections by supplying the
necessary type information. For example, ArrayList does not
implement IEnumerable<T>, but by calling
Cast<TResult>(IEnumerable) on the ArrayList object, the standard
query operators can then be used to query the sequence.
(You can also use Enumerable.OfType method, that will ignore all those which are not DataGridViewRow, but with DataGridView, Cast is fine as well)
You can also use FirstOrDefault to get the row first and then get the index like:
int index = 0;
var item = dgv.Rows.Cast<DataGridViewRow>()
.FirstOrDefault(r => r.Cells[0].Value == (object)1);
if (item != null)
index = item.Index;
I generally like these forms, (although the fact that the Rows are not a proper collection is syntactically annoying):
var hit = dgv.Rows.Cast<DataGridViewRow>().First(row => row.Cells["MyColumnName"].Value.Equals(MyIndexValue));
var hit = dgv.Rows.Cast<DataGridViewRow>().FirstOrDefault(row => row.Cells["MyColumnName"].Value.Equals(MyIndexValue));
If you only want the first one, it gets simpler:
var hit = dgv.Rows.Cast<DataGridViewRow>().First(row => row.Cells[0].Value.Equals(MyIndexValue));
I am trying to get the first and last values in a list. The query operator First() is supported but Last() and LastOrDefault() give an error. Am I using the Last() operator incorrectly?
var purchaseBills = db.PurchaseBills.OrderBy(p => p.BillID);
if (purchaseBills.Count() >0)
{
var firstBill = purchaseBills.First(); // This is supported
// Attempt 1
var lastBill = purchaseBills.Last(); // Not supported
// Attempt 2
var lastBill = purchaseBills.LastOrDefault(); // Not supported
//Attempt 3
var lastBill = purchaseBills.Reverse().First(); // Not supported
textBoxPurchaseBillFrom.Text = firstBill.BillNo.ToString();
textBoxPurchaseBillTo.Text = lastBill.BillNo.ToString();
}
Update:
--Errors--
Attempt 1: The query operator 'Last' is not supported.
Attempt 2: The query operator 'LastOrDefault' is not supported.
Attempt 3: The query operator 'Reverse' is not supported.
Instead of putting it into an own list by calling ToList() or ToArray() i would prefer to use AsEnumerable().
Additionally like the others you should try OrderByDescending()
Instead of Count() i would use Any().
either you switch your OrderBy to
.OrderByDescending(p => p.BillID)
(and use first) or you do something like
purchaseBills.ToArray().Last()
if this is not to expensive.
Last is not supported by the back-end DB. You should try other techniques:
Run your query using OrderByDescending so your requested item comes first.
Code your LINQ query as usual, but enforce Linq2Sql to render it to a CLR collection and then you'll have free access to everything locally, including Last. Example:
var bills = purchaseBills.ToList();
var last = bills.Last();
The problem is that there's no easy translation into SQL for Last or Reverse, so either convert it to something in memory (ToList, ToArray) if there aren't going to be too many records, or you could run the query a 2nd time, with OrderByDescending instead of OrderBy and use First.
I try not to use LastOrDefault() because sometime it doesn't work or support.
I'm sorting id by desc and then grab the first records.
.OrderByDescending(o=>o.id)
.FirstOrDefault(s => s.Name == Name)
It has something to do with the fact that the Last operator is trying to be sent to the SQL server which has no corresponding command. Once solution is to put a ToArray() or Tolist() at the end of your db call which makes that one line an explicit call to get the data (instead of lazing loading on each of the other lines).
Yet another way get last element without orderbydescending and load all entities:
var lastBill = purchaseBills
.Where(f => f.BillID == purchaseBills.Max(f2 => f2.BillID))
.FirstOrDefault();
You can convert your IEnumerable into a List using the ToList() method, which will ensure that all your attempts are successful, as shown:
var purchaseBills = db.PurchaseBills.OrderBy(p => p.BillID);
if (purchaseBills.Any())
{
var firstBill = purchaseBills.First(); // This is supported
// Attempt 1
var lastBill = purchaseBills.ToList().Last();
// Attempt 2
var lastBill = purchaseBills.ToList().LastOrDefault();
//NoLogic
var lastBill = purchaseBills.ToList().Reverse().First();
textBoxPurchaseBillFrom.Text = firstBill.BillNo.ToString();
textBoxPurchaseBillTo.Text = lastBill.BillNo.ToString();
}