How to write this LINQ/EF query? - vb.net

So I have an Entity Framework 5 model that includes a many-to-many relationship.
CategoryValues --< CourseCategoryValues >-- Courses
I have a LINQ query that selects every Course in the database. I would really like to modify it to only select Courses that belong to a specific CategoryValue. My attempt thus far has failed?
Can anyone help me figure this out?
This is what I have tried:
Using database As SiteDataContext = New SiteDataContext
database.Configuration.ProxyCreationEnabled = False
database.Courses.Include("Classes")
database.Courses.Include("CourseCategoryValues")
query = (From c In database.Courses Select c Order By c.Name).Where(
Function(c) 0 < c.Classes.Where(Function([class]) [class].Status.ToLower = "open").Count
).Include(Function(r) r.Classes).Include(Function(r) r.CourseCategoryValues)
' Here is where I am trying to narrow down the query results
If (pid.HasValue) AndAlso (0 <> pid.Value) Then
query.Where(Function(c) c.CourseCategoryValues.Any(Function(v) v.CategoryValue.CategoryValueID = pid))
End If
model.PageData = query.ToList
End Using

I think you are only missing the assignment of the filter to the query variable. Where returns a new queryable, it doesn't modify the queryable you apply the Where to. So, you would need:
query = query.Where(...)
The Where expression itself looks correct to me.

Related

Better Performance Linq To SQl query

Is there a way to increase the fetching performance of my LINQ to SQL query where its total count is more than a hundred thousand? Should I separate the total data to 5 parts by using skip or take? My query, when fetching, took more than 40 minutes.
Dim query = From a In context.Orders
Join b In context.Status On a.OrderItem Equals b.OrderItem
Join c In context.Summary On a.OrderItem.Substring(0, 16) Equals c.OrderSuffix
Where Not (b.Status.Contains("E")) And a.Type = "AG" And a.OrderItem = b.OrderItem And a.OrderItem.Substring(0, 16) = c.OrderSuffix
Order By a.OrderItem.Substring(0, 16), a.Agn Ascending
Select a.OrderItem,
JobOr = a.auftrag_nr.Substring(0, 16),
Suffix = (a.auftrag_nr.Substring(0, 16).Substring(13)),
a.Agn,
a.Base,
b.Status,
a.Group,
a.Machine,
a.Article,
a.Height,
a.Sol, c.plan, a.DatePlanned
Actually you make a really big join, which you filter afterwards.
If you have a filter like a.type = "AG" than you should apply it before you join, so many unnecessary join results will not be even generated.
Another idea for you:
The other style of writing linq queries.
like:
dim foo as IQueryable(Of TypeInYourTable) = yourDBContext.yourTable.where(function(v) v.a = ...).SomeOtherLinqFunctions...
In this way you can build up the queriables for each table and filter them first (e.g. the a.type = "AG"), then you can use the .Join(...) function and make proper joins, where you not just compare one pair of values of two datasets.
Be aware, that as long as the Queryable is a Queryable, no call was made to the database. You need to call e.g. .ToArray() or .ToList() or ... to enforce the call.
If you don't and you return a Queryable, then close the context of it and then try to get the results of the Queryable you will get a runtime exception.

Applying the count function within the case function

I am relatively new to SQL and am trying to apply the case function within a view.
While I understand the fundamentals of it, I am having difficulty applying it in the way that I need.
I have 3 columns ApplicationID, ServerName and ServerShared? (true/false).
Each application can have many servers associated to it, while each server only has 1 server type.
I would like to use case to create a further field which can take three values dependent upon whether the values of ServerShared related to an application are all True = Shared, False = Non-shared, Both True and False = Partially shared.
My thoughts were using count function within the case function to set statements where:
if 'count true > 0 and count false > 0' then ServerShared? =
partially if 'count true > 0' and 'count false = 0' then
ServerShared = true and vice versa.
I believe the above logic a way of achieving my result, yet I would appreciate help in both how to structure this within a case statement and any wisdom if there is a better way.
Thanks in advance!
If I get your question right, this should do the trick. Maybe you need to add further columns or adapt the logic. But you should get the logic behind.
SELECT ServerName,
CASE
WHEN COUNT(distinct ServerShared) = 2
THEN N'Server shared'
WHEN MIN(ServerShared) = 0
THEN N'Server not shared'
WHEN MAX(ServerShared) = 1
THEN N'Server shared'
END as ServerShared
FROM myTable
GROUP BY ServerName
There are two main ways to do this problem (super generic answer from non expert :D)
less often executed (one off?), slow execution with potential exponential time increases as rows go up:
This is similar to your suggested solution and involves putting other queries in the Select / field list part of the query - this will get executed for every row returned by the main part of the query (bad news generally speaking):
select
applicationID
, Case (select count * from table as b where a.applicationid = b.applicationid and shareserver=true)
WHEN 0 then 'Non-Shared'
WHEN (select count * from table where a.applicationid = b.applicationid) then 'Shared'
ELSE 'Partially-Shared' END as ShareType
from
tabls as a
get all your data once then perform just the comparison row by row. this is what i would use by default.. its basically better as far as i know but sometimes can be harder to think through.
this line is here to fix formatting issue
select
a.applicationid
,case
when sharedservers = 0 then 'Non-Shared'
when totalservers=sharedservers then 'Shared'
else 'Partially-Shared' END as ShareType
FROM
(select applicationID, count(*) as TotalServers from table) as a
LEFT OUTER JOIN (select applicationID, count(*) as SharedServersfrom table where sharedserver = true) as b
ON a.applicationid=b.applicationid
these queries are just written off the top of my head let me know if there are bug :/
note also the two uses of case statement. one with CASE *value* WHEN *possible value* THEN .. and the second way CASE WHEN *statement that evaluates to boolean* THEN ..

LINQ not returning all child records

I have a query in the DB:
SELECT GreenInventoryBlendGradeID,bgx.blendgradeid,
bgX.GreenBlendGradeTypeID,[Description]
FROM [GreenInventory] gi
INNER JOIN [GreenInventoryBlendGradeXref] bgX
ON bgX.[GreenInventoryID] = gi.[GreenInventoryID]
INNER JOIN [BlendGrade] bg
ON bg.[BlendGradeID]=bgx.[BlendGradeID]
That returns 3 records:
TypeID Desc
1 XR
2 XR
1 XF2
The LINQ:
var GreenInventory = (from g in Session.GreenInventory
.Include("GreenInventoryBlendGradeXref")
.Include("GreenInventoryBlendGradeXref.BlendGrade")
.Include("GreenInventoryBlendGradeXref.GreenBlendGradeType")
.Include("GreenInventoryWeightXref")
.Where(x => x.GreenInventoryID == id && x.GreenInventoryBlendGradeXref.Any(bg=>bg.GreenBlendGradeTypeID > 0) )
select g);
I have tried different Where clauses including the simple - (x => x.GreenInventoryID == id)
but always have only the first 2 records returned.
Any Ideas?
If I try the following:
var GreenInventory = (from gi in Session.GreenInventory.Where(y => y.GreenInventoryID == id)
join bgX in Session.GreenInventoryBlendGradeXref.DefaultIfEmpty() on gi.GreenInventoryID equals bgX.GreenInventoryID
join bg in Session.BlendGrade.DefaultIfEmpty() on bgX.BlendGradeID equals g.BlendGradeID
select new { GreenInventory = gi, GreenInventoryBlendGradeXref = bgX, BlendGrade = bg });
I Get back 3 of each objects and the correct information is in the BlendGrade objects. It looks like the 3 GreenInventory objects are the same. They each include 2 of the GreenInventoryBlendGradeXref objects which show the the same 2 records as before.
So I not clear on what the original problem was. Also dont know if this is the best way to resolve it.
Thanks for the answers. If anyone has a further thoughts please let us know.
Based on the few details you present, I would assume that you are missing a join. I have no experience with EntityFramework (I assume that you use this ORM), but as far as I know, the ".Include" tries to ensure that the set of root entities will not change and will not contain duplicates.
Your manually created query seems to indicate that there is at least one 1:n relationship in the model. The result you get from LINQ show that only distinct GreenInventory entities are returned.
Therefore you need to adjust your query and explicitly declare that you want all results (and not only distinct root entities) - I would assume that with an explicit join EntityFramework will yield all expected results - or you need to adjust your mapping.
The first place I'd look in would be your model and joins you have defined between the entities. You might also want to check your generated SQL statement:
Trace.WriteLine(GreenInventory.Provider.ToString())
or use Visual Studio IntelliTrace to investigate what was sent to the database.

NHibernate: selecting top 1 ordered by date in child collection for each parent

I have a Client class and a Meeting class, I am trying to retrieve all the next meetings for each Client. So that I would end up with one meeting per client.
I'm ending up with a query like this:
var qry = from client in session.Query<Client>()
select new
{
c = client,
e = client.Meetings.OrderBy(x => x.Date).First()
};
The sql generated is:
select
client0_.ClientId as col_0_0_,
(select
meetings1_.EventId
from
Event meetings1_
where
client0_.ClientId=meetings1_.ClientId
order by
meetings1_.Date asc) as col_1_0_,
client0_.ClientId as ClientId13_,
client0_.ContactName as ContactN2_13_,
client0_.ClientStatus as ClientSt3_13_,
client0_.HomePhoneNumber as HomePhon4_13_,
client0_.FaxNumber as FaxNumber13_,
client0_.WorkPhoneNumber as WorkPhon6_13_,
from
Client client0_
I am expecting the sub query to have a top 1 but it does not, is this 'n problem with Linq to NHibernate or am I doing something wrong?
I dont know if this will work, but its worth a shot:
Session.Linq<Meeting>().OrderByDescending(x => x.DateOfMeeting).Distinct();
I'm trying to do the VERY same thing.
I tried using a Formula in my mapping, and this works when I can limit the rows returned without a top 1 or where rownum = 1 (like, if the state of a column in the row can be used: WHERE HasMeetingOccured = null, but obviously for this situation that won't work.)
For me - Oracle throws up about the syntax - for whatever reason it doesn't like the rownum restriction in the subquery - it breaks syntax somehow.
I haven't tried using the linq provider for it, nor do I know how to do this in the critiera API .. however, does Single() or Take(1) work?

Linq related table not queryable error

I've got a problem with Linq in VB.NET.
I've created a .dbml file from my db, and everything looks good - relation "arrows" between tables are visible, etc.
But when I want to use simple query (taken from 101 Linq Samples by MS):
Dim q = From h In dc.Hours, w In h.WorkType
I receive an error:
Expression of type 'INTegrity.WorkType' is not queryable. Make sure you are not missing an assembly reference and/or namespace import for the LINQ provider.
h.Worktype is visible in Intellisense...
I can manually join in the Linq query, but shouldn't the relations be automatic?
The full query that works, but using "manual joins" looks like this:
Dim q1 = From g In dc1.Hours _
Join pr In dc1.WorkType On g.WorkTypeId Equals pr.WorkTypeId _
Where g.WorkerId = workerid _
Select New With {g.EntryId, g.Date, pr.WorkTypeDesc, g.Minutes}
Worktype table has 1 to many relationship with Hours table (as you can see on the WorkTypeId column)
I don't speak LINQ in VB, so I'll try to adapt to C#
From h In dc.Hours, w In h.WorkType
from h in dc.Hours
from w in h.WorkType
.....
This implies that WorkType is a collection, which it's name does not suggest. I think you want something closer to
from h in dc.Hours
let w = h.WorkType
....
Of course, none of these will do anything without the parts in the "....". If you show us what you want to do there, we could probably fix it for you.
UPDATE#1:
Trying to piece together what you are doing, let's guess that Hours & WorkType are tables with a one-to-many relationship (WorkType on the "one" side), hence every Hours record matchs one WorkType record. In that case, Linq-to-SQL will automatically generate a scalar WorkType property in Hours. You don't need the "from ... in " to access it. It's just a property, use it directly where you need it.
UPDATE#2: (From comments)
I think this should work:
Dim q1 =
From g In dc1.Hours
Where g.WorkerId = workerid
Select New With {g.EntryId, g.Date, g.WorkType.WorkTypeDesc, g.Minutes}