Does the second from clause in this LINQ query do anything? - linq

I am trying to simplify a C# method that includes the following query:
var matchingDeviceKeys =
from dev in data.Elements("Key1")
where dev.Element("Key2").Element("Key3").Element("VendorID").Value == device.Vendor &&
dev.Element("Key2").Element("Key3").Element("ProductType").Value == device.ProductType &&
dev.Element("Key2").Element("Key3").Element("ProductCode").Value == device.ProductCode
from rev in dev.Element("Key2").Element("Key3").Element("Key4").Elements("MajorRev")
where rev.Attribute("Number").Value == device.MajorRevision &&
rev.Attribute("DefaultMinorRev").Value == device.MinorRevision.ToString(CultureInfo.InvariantCulture)
select dev.Element("Key6").Element("Key7").Element("Key8");
The "rev" target for the second from clause is never used in the select line, making me wonder if that entire second from clause is doing anything. If it is doing something, what is it doing?
My apologies for obfuscating the element names. I have to be careful in sharing proprietary code.

Related

Queries with local collections are not supported LINQ

everyone! I'm trying to do a rather simple LINQ query to find available rooms in a hotel. (That is, find a available room from a pool of rooms, and check that there are no pending cleaning etc in the room).
But when I try to execute the third query, I get the exception you see in the title. I don't actually get the exception when I execute it, but when I try to use the "unfinishedTasksInPool" variable.
I tried turning the "unfinishedTasksInPool" into a list, too see if that would help, but whenever I try to use "unfinishedTasksInPool", I get the exception.
EDIT : Whenever I exclude "availableRoomsFromPool.Contains(tasks.roomId" in the where clause in the third query, everything seems to work normally. But that doesn't exactly solve the problem tho.
var pendingReservation = database.Reservations.Where(res => res.reservationID == resId).First();
var reservationsInSameGroup = from otherReservations in database.GetTable<Reservation>()
where (otherReservations.beds == pendingReservation.beds
&& otherReservations.rank == pendingReservation.rank
&& otherReservations.roomID != null)
select otherReservations.roomID;
var availableRoomsFromPool = from rooms in database.GetTable<Room>()
where (!reservationsInSameGroup.Contains(rooms.roomId)
&& rooms.beds == pendingReservation.beds
&& rooms.roomRank == pendingReservation.rank)
select rooms.roomId;
var unfinishedTasksInPool = from tasks in database.GetTable<HotelTask>()
where (availableRoomsFromPool.Contains(tasks.roomId)
&& tasks.taskStatus < 2)
select tasks.roomId;
It's a LINQ-to-SQL restriction. You can use local sequences in queries (as long as you use them in Contains), but you can't use a local sequence that itself is the result of a query using another local sequence.
So it's alright to do...
var availableRoomsFromPool = (from ....).ToArray();
...because the query contains one local sequence (reservationsInSameGroup).
But...
var unfinishedTasksInPool = (from ...).ToArray();
...throws the exception.
The solution is to use the result of var availableRoomsFromPool = (from ....).ToArray(); in the third query, because that reduces availableRoomsFromPool to one local sequence.

LINQ: QuerySyntaxException

I've written some complex LINQ query which I'm trying to run against NHibernate, unfortunately this causes an exception when trying to evaluate the query (call .ToList()).
Here is the code, which is supposed to retrieve Issue objects with some extra data:
var list = from i in sess.Query<Issue>()
let readFlag = (from f in i.ReadFlags
where (f.User != null && f.User.Username == request.UserName)
||
(f.Device.Name == request.MachineName)
select f).FirstOrDefault()
let isUnread = readFlag == null
let unreadCommentsCount = isUnread ? 0 : (from c in i.Comments
where c.DateCreated > readFlag.LastSeenCommentDate
select c).Count()
let statusChanged = isUnread && i.Status != readFlag.LastSeenStatus
where isUnread || unreadCommentsCount > 0 || statusChanged
select new
{
Issue = i,
ReadFlag = readFlag,
IsUnread = isUnread,
UnreadCommentsCount = unreadCommentsCount
};
i.e. - for each Issue object look for related IssueReadFlag which matches current username or machine name. if the Flag exists, it also contains the "last seen comment date", so the code calculates the number of new comments for this Issue.
Unfortunately NHibernate is not able to handle this query (Exception details).
I'm looking for the most optimal solution for this problem.
Obtaining all the Issue objects and applying the query criteria on the client-side is unacceptable for me.
trying to find out what causes the issue: It started working as I've commented out evaluation of unreadCommentsCount and statusChanged both in "select" and "where" statements. Following this lead, It seems that referencing readFlag in any of these subqueries causes the issue (after removing reference to readFlag in "let unreadCommentsCount .." and "let statusChanged.." conditions, the query doesn't throw exceptions).
Now I need a tip on how to make it functional (working as expected) again ...
Further investigating the issue I've found out that NHibernate can't handle the uncertain state of readFlag existence.
I've ended up splitting the query into two parts, first of them takes all the Issues where readFlag does not exists, the second one queries the IssueReadFlag directly, calculating the Unread Comments Count and Changed Status.
This seems to be the only solution as for now.

Why is LINQ query to DataTable not working?

I have a DataTable, dt, that contains the following:
As you can see, there are two sets of files here: baseline (columns 1-3) and target (columns 4-6). So in this case I have 4 baseline files, m4, m5, m1, and m3 (m1 in row 5 is copied from row 3) and one target file, m1. The problem is that the information for the baseline file m1.txt is duplicated, so I'm trying to remove it using the following LINQ statement:
var extraneousRows = dt.Rows.Cast<DataRow>().
Where(
row => row["BASELINE_FOLDER"] == baselineSubfolder
&& row["BASELINE_FILE"] == baselineFilename
&& row["BASELINE_CHECKSUM"] == baselineChecksum
&& row["STATUS"] == "remove"
).ToArray();
foreach (DataRow dr in extraneousRows)
{
dt.Rows.Remove(dr);
}
This should remove the 3rd row from the DataTable but it doesn't. The code works fine if I omit the && row["STATUS"] == "remove" line, so I know it's functional. And the values for baselineSubfolder, baselineFilename, and baselineChecksum are correct, so that's not the problem.
But for some reason, when I include that line about the status column, it doesn't detect that that row is in the DataTable, even though it clearly is according to the DataSet Visualizer (photo above). So it's never entering the foreach loop and not removing the necessary files. Why???
I maybe should mention that the baseline file information (first 4 rows) are being retrieved from a database, whereas the target file fields are being generated according to user input. I don't see how it would matter where the information is coming from, though, since I'm querying the DataTable directly...
UPDATE
Ok, after following the suggestions of idipous and Jamie Keeling, I've determined that the problem had to do with the foreach loop, which was never being populated. Since this query should only ever return a single row, I eliminated the loop altogether. My revised code looks like this:
var extraneousRows = dt.Rows.Cast<DataRow>().
Where(
row => row["BASELINE_FOLDER"] == baselineSubfolder
&& row["BASELINE_FILE"] == baselineFilename
&& row["BASELINE_CHECKSUM"] == baselineChecksum
&& row["STATUS"] == "remove"
).SingleOrDefault();
dt.Rows.Remove(extraneousRows);
For whatever reason, extraneousRows remains null and that last line is generating a runtime error: IndexOutOfRangeException: The given DataRow is not in the current DataRowCollection
Why isn't this working?
It actually turns out that the problem was that I needed to cast the column values to strings. The solution was incredibly simple: just add a .ToString() after the column names and viola! The following code worked like a charm:
var extraneousRows = dt.Rows.Cast<DataRow>().
Where(
row => row["BASELINE_FOLDER"].ToString() == baselineSubfolder
&& row["BASELINE_FILE"].ToString() == baselineFilename
&& row["BASELINE_CHECKSUM"].ToString() == baselineChecksum
&& row["STATUS"].ToString() == "remove"
).SingleOrDefault();
dt.Rows.Remove(extraneousRows);
I also found a non-LINQ way to do this, by iterating through all the rows of the DataTable. It's not the most efficient, but it works:
for (int z = 0; z < dt.Rows.Count; z++)
{
if ((dt.Rows[z]["BASELINE_FOLDER"].ToString() == baselineSubfolder)
&& (dt.Rows[z]["BASELINE_FILE"].ToString() == baselineFilename)
&& (dt.Rows[z]["BASELINE_CHECKSUM"].ToString() == baselineChecksum)
&& (dt.Rows[z]["STATUS"].ToString() == "remove"))
{
dt.Rows[z].Delete();
}
}
dt.AcceptChanges();

Linq Where Clause Change based on Parameters

I have a linq statement that returns a list of records based on where clause
This where clause checks for two parameter values.
Out of which one parameter is optional.
so i need a suggestions if i can switch my where clause based on the optional Parameter
something like this
if(locid==0)
{
where (p.CustomerID == custid)
}
else{
where (p.CustomerID == custid) & (p.LocationID == locid )
}
can any one help me how can i get this work.
thanks
You could try writing it like this:
where (p.CustomerID == custid) && (locid == 0 || p.LocationID == locid )
Yes - queries can be composed (although you don't need this for this particular case as #rsbarro pointed out):
var query = p;
if(locid==0)
query = query.Where( p =>p.CustomerID == custid);
else
query = query.Where( p =>p.CustomerID == custid & p.LocationID == locid);
//any other conditions
As BrokenGlass mentioned, you should use composition:
IQueryable<Foo> query = unfiltered.Where(p => p.CustomerID == custId);
if (locid != 0)
{
query = query.Where(p => p.LocationID == locid);
}
Note that the query is not executed until you start reading data from it, so you needn't worry about this making multiple requests.
It looks like in your original post you were trying to use query syntax piecemeal - that won't work, but the "dot notation" is pretty simple here. You can always create your initial query using a query expression if you want - again, that query won't be executed immediately anyway.

Parenthesis Expressions in LINQ to SQL

If I want to generate the query (month(created) = 1 and year(created) = 2010) or (month(modified) = 1 and year(modified) = 2010) with linq, how would I go about it?
I have o.Created.Value.Month == month && o.Created.Value.Year == year. If I do (o.Created.Value.Month == month && o.Created.Value.Year == year) || (o.Modified.Value.Month == month && o.Modified.Value.Year == year) wouldn't the parenthesis just be ignored?
No, the parentheses won't be ignored by LINQ - they're important to indicate the logic. They're effectively present in the expression tree, in that you'll end up with an "OR" expression with two subexpressions each of which is an "AND" expression.
The query you've given should be fine - have you tried it, and checked the resulting SQL?

Resources