Linq data structuring - linq

I have two issues I'm struggling with LINQ. I appreciate if you could advise. I have 2 lists rawStates (storing rows of entity-downtime-uptime-eventtype) and rawData list storing products' in and out times from entity.
I want to select those elements from rawStates that occurred when still waiting for that entity to be processed
foreach(var t in rawData)
var s = rawStates
//I am not sure if this single logic clause in Where is enough;
.Where(o => o.Entity == t.Entity
&& o.DownDate > t.InTime
&& o.Update < t.OutTime)
.ToList();
If I group my rawData by productID (there are multiple rows with same ProductID), how can I revert back this "s" to these groups so that for a productID I can group by eventtype, and summarise durations by productID?

Related

NotSupportedException for LINQ Queries

I am trying to get a list of a database table called oracleTimeCards whose employee id equals to the employeeID in employees list. Here is what I wrote:
LandornetSQLEntities db = new LandornetSQLEntities();
List<OracleEmployee> employees = db.OracleEmployees.Where(e => e.Office.Contains(officeName) && e.IsActive == true).Distinct().ToList();
var oracleTimeCards = db.OracleTimecards.Where(c => employees.Any(e => c.PersonID == e.PersonID)).ToList();
Anyone has any idea?
I'm going to assume you're using Entity Framework here. You can't embed calls to arbitrary LINQ extension methods inside your predicate, since EF might not know how to translate these to SQL.
Assuming you want to find all the timecards for the employees you found in your first query, you have two options. The simplest is to have a navigation property on your Employee class, named let's say TimeCards, that points to a collection of time card records for the given employee. Here's how that would work:
var oracleTimeCards = employees
.SelectMany(e => e.TimeCards)
.ToList();
If you don't want to do this for whatever reason, you can create an array of employee IDs by evaluating your first query, and use this to filter the second:
var empIDs = employees
.Select(e => e.PersonID)
.ToArray();
var oracleTimeCards = db.OracleTimecards
.Where(tc => empIDs.Contains(tc.PersonID))
.ToList();

linq query with count

I would like to create a simple linq query, but I don't really know, how it should be. I have searched the net, but found nothing, what I can use or I don't know yet, that I could use it.
So, basically, I have a table with this fields: reference, vat_code, amount, vat_amount, supplier.
Now, I would like to query the records, where reference<>'' and the reference is more than once in the table. But I need all the occupians.
F.e. from
1;VF;100;27;345
2;VF;200;54;123
2;VF;-200;-54;123
2;VF;200;54;123
3;VF;300;81;888
to
2;VF;200;54;123
2;VF;-200,-54;123
2;VF;200;54;123
How would be look the linq query to this?
Thanks.
If rows with same reference should go together in results, then you should filter out rows with reference equal to empty string, then group all rows by reference and select only those groups which have more than one row.
C# sample with DataTable:
var result = dt.AsEnumerable()
.Where(r => r.Field<string>("reference") != "")
.GroupBy(r => r.Field<string>("reference"))
.Where(g => g.Count() > 1)
.SelectMany(g => g); // flatten group to sequence of rows

Restricting deeply-nested child records returned using LINQ

Given this object structure, how do I bring back a WorkItem, it's report(s), row(s) and student(s), but only the row whose Student's name is 'Bob' (I want to omit the rows containing 'Alice' and 'Claire').
WorkItem
----Report1 (held in WorkItem.Reports collection)
--------ReportRow1 (held in Report.ReportRows collection)
------------Student.Name = 'Alice'
--------ReportRow2 (held in Report.ReportRows collection)
------------Student.Name = 'Bob'
--------ReportRow3 (held in Report.ReportRows collection)
------------Student.Name = 'Claire'
(sorry about the formatting)
I thought something like this would work, but it still brings back all 3 rows
WorkItem found = (from workItem in session.Query<WorkItem>()
from report in workItem.Reports
from row in report.ReportRows
where workItem.Id == 1 && row.Student.Name == "Bob"
select workItem)
.SingleOrDefault<WorkItem>();
Update
I also tried this, thinking it would only bring back the results when I actually try to use them (which it does) but looking at the logs, it still does a select for each student (I was hoping the 'where' clause in the final foreach loop would bring back just the one I was interested in.
var query = from workItem in session.Query<WorkItem>()
where workItem.Id == 1
select workItem;
WorkItem found = query.SingleOrDefault<WorkItem>();
foreach (var report in found.Reports)
{
foreach (var row in report.ReportRows.Where(x => x.Student.Name == "Bob"))
{
Console.WriteLine("--" + row.Student.Name);
}
}
This is my current best stab at it based on help from Ocelot20:
var result = (from workItem in session.Query<WorkItem>()
.FetchMany(x => x.Reports)
.ThenFetchMany(y => y.ReportRows.Where(z => z.Student.Name == "Bob"))
where workItem.Id == 1
select workItem);
The only thing that doesn't work is the Where clause for the student name. If I remove that, I get a result back (albeit with too many rows). If I can get the where clause right I think it will be bringing back what I want
From what I can see here, you are only selecting a WorkItem. You are doing joins that limit the WorkItems that are returned, but you are still only selecting a WorkItem and not telling it to select specific Reports, ReportRows, etc. So essentially your query is saying: "Give me only WorkItems with an Id of 1 that can be joined with students named Bob". Note the lack of: "Then select that WorkItem with only the appropriate ReportRows.
My guess is that you're doing something like this:
WorkItem found = (from workItem in session.Query<WorkItem>()
from report in workItem.Reports
from row in report.ReportRows
where workItem.Id == 1 && row.Student.Name == "Bob"
select workItem).SingleOrDefault<WorkItem>();
// Doing something to select `ReportRows` without filtering them:
var someSelection = found.Reports.First().ReportRows;
Depending on how you have lazy loading set up, ReportRows will not even be queried until you call it on the someSelection line. At this point, it knows nothing about what you want to filter on. You have a couple of options here. First, you can just filter the items in the second query once you have already loaded the WorkItem like so:
// Filter on the second query:
var someSelection = found.Reports.First().ReportRows
.Where(rr => rr.Student.Name == "Bob");
Or you could just change up your query to explicitly select the work item and related rows: select new { workItem, reportRows = // select specific rows with a where clause here. }.
Lastly, there's the nhibernate ability to specify what related entities should be preloaded into your selected entity. I know entity framework lets you add Where clauses on the related entities, but I haven't used nhibernate to know whether or not you can do something like this:
var customers = session.Query<Customer>()
.FetchMany(c => c.Orders.Where(o => o.Amount == 100);
If this works, this could be applied to your query to tell nhibernate to load the WorkItem with related rows where the Student name is "Bob".

group by and joining tables in linq to sql

I have the following 3 classes(mapped to sql tables).
Places table:
Name(key)
Address
Capacity
Events table:
Name(key)
Date
Place
Orders table:
Id(key)
EventName
Qty
The Places and Events tables are connected through Places.Name = Events.Place, while the Events and Orders tables: Events.Name = Orders.EventName .
The task is that given an event, return the tickets left for that event. Capacity is the number a place can hold and Qty is the number of tickets ordered by someone. So some sort of grouping in the Orders table is needed and then subtract the sum from capacity.
Something like this (C# code sample below)?
Sorry for the weird variable names, but event is a keyword :)
I didn't use visual studio, so I hope that the syntax is correct.
string eventName = "Event";
var theEvent = Events.FirstOrDefault(ev => ev.Name == eventName);
int eventOrderNo = Orders.Count(or => or.EventName == eventName);
var thePlace = Places.FirstOrDefault(pl => pl.Name == theEvent.Place);
int ticketsLeft = thePlace.Capacity - eventOrderNo;
If the Event has multiple places, the last two lines would look like this:
int placesCapacity = Places.Where(pl => pl.Name == theEvent.Place)
.Sum(pl => pl.Capacity);
int ticketsLeft = placesCapacity - eventOrderNo;
On a sidenote
LINQ 101 is a great way to get familiar with LINQ: http://msdn.microsoft.com/en-us/vcsharp/aa336746

Max sequence from a view containing multiple record using Linq lambda

I've been at this for a while. I have a data set that has a reoccurring key and a sequence similar to this:
id status sequence
1 open 1
1 processing 2
2 open 1
2 processing 2
2 closed 3
a new row is added for each 'action' that happens, so the various ids can have variable sequences. I need to get the Max sequence number for each id, but I still need to return the complete record.
I want to end up with sequence 2 for id 1, and sequence 3 for id 2.
I can't seem to get this to work without selecting the distinct ids, then looping through the results, ordering the values and then adding the first item to another list, but that's so slow.
var ids = this.ObjectContext.TNTP_FILE_MONITORING.Select(i => i.FILE_EVENT_ID).Distinct();
List<TNTP_FILE_MONITORING> vals = new List<TNTP_FILE_MONITORING>();
foreach (var item in items)
{
vals.Add(this.ObjectContext.TNTP_FILE_MONITORING.Where(mfe => ids.Contains(mfe.FILE_EVENT_ID)).OrderByDescending(mfe => mfe.FILE_EVENT_SEQ).First<TNTP_FILE_MONITORING>());
}
There must be a better way!
Here's what worked for me:
var ts = new[] { new T(1,1), new T(1,2), new T(2,1), new T(2,2), new T(2,3) };
var q =
from t in ts
group t by t.ID into g
let max = g.Max(x => x.Seq)
select g.FirstOrDefault(t1 => t1.Seq == max);
(Just need to apply that to your datatable, but the query stays about the same)
Note that with your current method, because you are iterating over all records, you also get all records from the datastore. By using a query like this, you allow for translation into a query against the datastore, which is not only faster, but also only returns only the results you need (assuming you are using Entity Framework or Linq2SQL).

Resources