Writing a subquery using LINQ in C# - c#

I would like to query a DataTable that produces a DataTable that requires a subquery. I am having trouble finding an appropriate example.
This is the subquery in SQL that I would like to create:
SELECT *
FROM SectionDataTable
WHERE SectionDataTable.CourseID = (SELECT SectionDataTable.CourseID
FROM SectionDataTable
WHERE SectionDataTable.SectionID = iSectionID)
I have the SectionID, iSectionID and I would like to return all of the records in the Section table that has the CourseID of the iSectionID.
I can do this using 2 separate queries as shown below, but I think a subquery would be better.
string tstrFilter = createEqualFilterExpression("SectionID", strCriteria);
tdtFiltered = TableInfo.Select(tstrFilter).CopyToDataTable();
iSelectedCourseID = tdtFiltered.AsEnumerable().Select(id => id.Field<int>("CourseID")).FirstOrDefault();
tdtFiltered.Clear();
tstrFilter = createEqualFilterExpression("CourseID", iSelectedCourseID.ToString());
tdtFiltered = TableInfo.Select(tstrFilter).CopyToDataTable();

Although it doesn't answer your question directly, what you are trying to do is much better suited for an inner join:
SELECT *
FROM SectionDataTable S1
INNER JOIN SectionDataTable S2 ON S1.CourseID = S2.CourseID
WHERE S2.SectionID = iSectionID
This then could be modeled very similarily using linq:
var query = from s1 in SectionDataTable
join s2 in SectionDataTable
on s1.CourseID equals s2.CourseID
where s2.SectionID == iSectionID
select s1;

When working in LINQ you have to think of the things a bit differently. Though you can go as per the Miky's suggestion. But personally I would prefer to use the Navigational properties.
For example in your given example I can understand that you have at-least 2 tables,
Course Master
Section Master
One Section must contain a Course reference
Which means
One Course can be in multiple Sections
Now if I see these tables as entities in my model I would see navigational properties as,
Course.Sections //<- Sections is actually a collection
Section.Course //<- Course is an object
So the same query can be written as,
var lstSections = context.Sections.Where(s => s.Course.Sections.Any(c => c.SectionID == iSectionID)).ToList();
I think you main goal is, you are trying extract all the Sections where Courses are same as given Section's Courses.

Related

Querying SQL Database using LINQ

SELECT Meter.SerialNumber as serial,
SupplyPoint.Id as supplypoint,
SupplyType.Id as supplytype,
SupplyType.Name as supplytypename
FROM Meter
INNER JOIN SupplyPoint ON Meter.SupplyPointId = SupplyPoint.Id
INNER JOIN SupplyType ON SupplyPoint.SupplyTypeId = SupplyType.Id;
I have this query so that I might find the Supply Type of a meter based on it's serial. So far I've written this function:
var query = from meters in db.Meters
join supplyPoint in db.SupplyPoints on meters.SupplyPointId
equals supplyPoint.Id
join supplyType in db.SupplyTypes on supplyPoint.SupplyTypeId equals
supplyType.Id
select new { serial = meters.SerialNumber, type = supplyType.Name };
foreach (var meter in query)
{
if (meter.serial == serial)
return meter.type;
}
return "Meter Type Not Specified";`
So I call FindType(string serial) and it returns the type. Can anyone suggest better code for converting such a query? Any directions on where to learn more about LINQ are welcome too.
You are up for reading up quite a bit of material before you should get started.
Read about entity framework here.
Read about LINQ to SQL
here.
Hopefully, once you have you will realize that your problem boils down to the following code.
var result = context.YourTable.FirstOrDefault(r => r.Field == searchValue);
Without knowing much about what you are working with there's little that can be done to give you concrete help. First you want to get familiar with translating the sql statement to LINQ.
LINQ has a query syntax that starts with the from because that is how to define the datasource. SELECT FirstName FROM Authors becomes
from auth in db.Authors
select auth.FirstName
Where db is a reference to a datacontext as suggested in slugster's comment.
You can look for more information on linq at the following url

Linq correlated subquery to same table on multiple columns

I've looked at several other questions related to correlated subqueries but it's still not clear to me how to accomplish what I need. I'm using Entity Framework and C#, and have a table called STEWARDSHIP with the following columns:
STEWARDSHIP_ID (the primary key)
SITE_ID
VISIT_DATE
VISIT_TYPE_ID
I need to identify cases where the same combination of SITE_ID, VISIT_DATE, VISIT_TYPE_ID exists more than once because it could represent a duplicate entry made by end users in error, and then I need to report on the details of these entries. In SQL I would do this by joining to the temporary result of a GROUP BY/HAVING like so:
SELECT * FROM stewardship AS s2,
(SELECT site_id, visit_type_id, CAST(visit_date AS DATE) AS visit_date
FROM stewardship
GROUP BY site_id, visit_type_id, CAST(visit_date AS DATE)
HAVING COUNT(*) > 1) AS s
WHERE s2.site_id = s.site_id
AND s2.visit_type_id = s.visit_type_id
AND CAST(s2.visit_date AS DATE) = s.visit_date
What's the best way to accomplish this in Linq?
Since you're open to a different approach that should be more performant, here is the new SQL to get what I think you're after.
select distinct s1.*
from stewardship s1
inner join stewardship s2 on
s1.stewardship_id <> s2.stewardship_id and
s1.site_id = s2.site_id and
s1.visit_type_id = s2.visit_type_id and
cast(s1.visit_date as date) = cast(s2.visit_date as date)
order by s1.site_id, s1.visit_type_id
Now, to translate that to LINQ, you can use the following statement.
var duplicates = (
from s in Stewardships
join s2 in Stewardships
on new { s.Site_id, s.Visit_type_id, s.Visit_date.Date } equals new { s2.Site_id, s2.Visit_type_id, s2.Visit_date.Date }
where s.Stewardship_id != s2.Stewardship_id
select s)
.Distinct()
.OrderBy(s => s.Site_id)
.ThenBy(s => s.Visit_type_id)
Note that you cannot use anything other than an equijoin for expression joins, so I had to put the non-equijoin (ensuring our matches aren't on the same record via PK) in the where expression. You could also accomplish this with lambdas via the Except() extension method.
The order by is there for readability of the results and to match the SQL statement above.
I hope this helps!
It would be fairly similar to what you've already got.
from s in context.stewardships
group s by new {s.site_id, s.visit_type_id, visit_date} into g
where g.Count() > 1
select g;
This would give you groups of stewardships with similar values. You could "flatten" those results with a SelectMany afterward, but you might find them more useful to work with in groups.
Note that you may need to use SqlFunctions or something to do the equivalent of the cast to date.

How get a specific value from a query in Linq

I am just starting learning about Linq. I wrote some examples but there is a doubt that I haven't been able to solve.
I am working with the Northwind database, I just used the tool called SqlMetal in order to make all the modeling.
Right now I just wrote the following query:
var q = from c in db.Customers
from o in c.Orders
where c.City == "London"
select new { c, o };
I know that this query brings to me a resulting set of data that contains all the columns of the Customers and Orders tables for the given condition (c.City == "London"). But my question is:
After executing that query, How can I get the value of the column Customers.CustomerID and Orders.OrderID FROM THE VARIABLE q?
Maybe this is a silly question but I have been struggling with this for a couple hours, thanks in advance.
var theSoleItem = q.First();
theSoleItem.CustomerID
theSoleItem.OrderID
Also, if that was the only columns you cared about, your initial query would be faster with:
select new {c.CustomerID, o.OrderID}
You could also change the names as you go:
select new {CustID = c.CustomerID, OrdID = o.OrderID}
The latter being the only option if rather than directly referencing a property, you reference a method call.
The variable q is now a sequence of anonymous objects that have two objects in it: Customer and Order. You can easy iterate over it:
foreach(var item in q)
{
Console.WriteLine("CustomerID = " + item.CustomerID + " order ID = " + item.OrderID);
}

Is this LINQ Query "correct"?

I have the following LINQ query, that is returning the results that I expect, but it does not "feel" right.
Basically it is a left join. I need ALL records from the UserProfile table.
Then the LastWinnerDate is a single record from the winner table (possible multiple records) indicating the DateTime the last record was entered in that table for the user.
WinnerCount is the number of records for the user in the winner table (possible multiple records).
Video1 is basically a bool indicating there is, or is not a record for the user in the winner table matching on a third table Objective (should be 1 or 0 rows).
Quiz1 is same as Video 1 matching another record from Objective Table (should be 1 or 0 rows).
Video and Quiz is repeated 12 times because it is for a report to be displayed to a user listing all user records and indicate if they have met the objectives.
var objectiveIds = new List<int>();
objectiveIds.AddRange(GetObjectiveIds(objectiveName, false));
var q =
from up in MetaData.UserProfile
select new RankingDTO
{
UserId = up.UserID,
FirstName = up.FirstName,
LastName = up.LastName,
LastWinnerDate = (
from winner in MetaData.Winner
where objectiveIds.Contains(winner.ObjectiveID)
where winner.Active
where winner.UserID == up.UserID
orderby winner.CreatedOn descending
select winner.CreatedOn).First(),
WinnerCount = (
from winner in MetaData.Winner
where objectiveIds.Contains(winner.ObjectiveID)
where winner.Active
where winner.UserID == up.UserID
orderby winner.CreatedOn descending
select winner).Count(),
Video1 = (
from winner in MetaData.Winner
join o in MetaData.Objective on winner.ObjectiveID equals o.ObjectiveID
where o.ObjectiveNm == Constants.Promotions.SecVideo1
where winner.Active
where winner.UserID == up.UserID
select winner).Count(),
Quiz1 = (
from winner2 in MetaData.Winner
join o2 in MetaData.Objective on winner2.ObjectiveID equals o2.ObjectiveID
where o2.ObjectiveNm == Constants.Promotions.SecQuiz1
where winner2.Active
where winner2.UserID == up.UserID
select winner2).Count(),
};
You're repeating join winners table part several times. In order to avoid it you can break it into several consequent Selects. So instead of having one huge select, you can make two selects with lesser code. In your example I would first of all select winner2 variable before selecting other result properties:
var q1 =
from up in MetaData.UserProfile
select new {up,
winners = from winner in MetaData.Winner
where winner.Active
where winner.UserID == up.UserID
select winner};
var q = from upWinnerPair in q1
select new RankingDTO
{
UserId = upWinnerPair.up.UserID,
FirstName = upWinnerPair.up.FirstName,
LastName = upWinnerPair.up.LastName,
LastWinnerDate = /* Here you will have more simple and less repeatable code
using winners collection from "upWinnerPair.winners"*/
The query itself is pretty simple: just a main outer query and a series of subselects to retrieve actual column data. While it's not the most efficient means of querying the data you're after (joins and using windowing functions will likely get you better performance), it's the only real way to represent that query using either the query or expression syntax (windowing functions in SQL have no mapping in LINQ or the LINQ-supporting extension methods).
Note that you aren't doing any actual outer joins (left or right) in your code; you're creating subqueries to retrieve the column data. It might be worth looking at the actual SQL being generated by your query. You don't specify which ORM you're using (which would determine how to examine it client-side) or which database you're using (which would determine how to examine it server-side).
If you're using the ADO.NET Entity Framework, you can cast your query to an ObjectQuery and call ToTraceString().
If you're using SQL Server, you can use SQL Server Profiler (assuming you have access to it) to view the SQL being executed, or you can run a trace manually to do the same thing.
To perform an outer join in LINQ query syntax, do this:
Assuming we have two sources alpha and beta, each having a common Id property, you can select from alpha and perform a left join on beta in this way:
from a in alpha
join btemp in beta on a.Id equals btemp.Id into bleft
from b in bleft.DefaultIfEmpty()
select new { IdA = a.Id, IdB = b.Id }
Admittedly, the syntax is a little oblique. Nonetheless, it works and will be translated into something like this in SQL:
select
a.Id as IdA,
b.Id as Idb
from alpha a
left join beta b on a.Id = b.Id
It looks fine to me, though I could see why the multiple sub-queries could trigger inefficiency worries in the eyes of a coder.
Take a look at what SQL is produced though (I'm guessing you're running this against a database source from your saying "table" above), before you start worrying about that. The query providers can be pretty good at producing nice efficient SQL that in turn produces a good underlying database query, and if that's happening, then happy days (it will also give you another view on being sure of the correctness).

How to create a criteria in NHibernate that represents an OR between two EXISTS?

This one has been making my head hurt (which is easy since I'm a NHibernate newbie): how can I represent the following query (T-SQL) through the Criteria API?
DECLARE #pcode VARCHAR(8)
SET #pcode = 'somecode'
SELECT d.*
FROM document d
WHERE EXISTS (
SELECT 1
FROM project p
WHERE p.id = d.projectId AND p.code = #pcode)
OR EXISTS (
SELECT 1
FROM job j INNER JOIN project p ON p.id = j.projectId
WHERE j.id = d.jobId AND p.code = #pcode)
(A Document has two possible associations, Project or Job. Only one of them has a value at a given time; the other has null.)
The goal is to load all Documents that are directly associated with a given Project or indirectly through a Job.
Thanks.
I believe this could do the trick:
DetachedCriteria dCriteria1 = DetachedCriteria.For<Project>("project")
.SetProjection(Projections.Property("project.Id"))
.Add(Restrictions.EqProperty("doc.projectId", "project.Id"));
DetachedCriteria dCriteria2 = DetachedCriteria.For<Job>("job")
.SetProjection(Projections.Property("job.Id"))
.CreateCriteria("Projects", "p")
.Add(Restrictions.EqProperty("doc.jobId", "job.Id"))
.Add(Restrictions.Eq("p.code", "somecode"));
var documents = NHibernateSessionManager.Session.CreateCriteria<Document>("doc")
.Add(Restrictions.Or(
Subqueries.Exists(dCriteria1),
Subqueries.Exists(dCriteria2))).List<Document>();
The above code is derived from the sample query that you provided. If it is not entirely accurate you can change it a bit to make it workable in your solution.

Categories