Simple Join - c#

This question is about subsonic
I am trying to build a select query HAVING joins in it using subsonic.
For Example if I want to extract data from 3 tables then how I will be able to do it in Subsonic.
Lets say if I have a TSQL given below then How I will be able to translate this into Subsonic?
Select la.LoanAppId, ci.FirstName, ci.LastName, la.ApplicationDateSubmitted,
la.LoanAmount, la.DueDate, lkUpD.Col1Value
from LoanApplication la, ContactInfo ci, LookUpDetails lkUpD
where la.UserId = ci.UserId
and la.StatusId = lkUpD.LookUpDetailId
Please Reply

SubSonic.SqlQuery q = new Select(
LoanApplication.LoanAppId, ContactInfo.FirstNa, [etc])
.From(LoanApplication.Schema)
.InnerJoin(ContactInfo.Schema)
.InnerJoin(LookUpDetails.Schema);
if all the inner joins are natural (else, you need to add more .InnerJoin calls to specify the inner-join conditions).

Related

Linq to sql alias fails

I'm working on a winforms project
and I have the following linq expressions
LinqQuery =
from t in newContext?.TOURNAMENT_D
from u in newContext.PLAYERS_LIST.Where(b => t.TD_ROWID == b.PL_TOURNAMENT_REFNO).DefaultIfEmpty()
select new
{
t.TD_ROWID,
t.TD_NAME,
t.TD_DATE,
startDate = t.TD_DATE,
endDate = t.TD_DATE,
u.PL_FULLNAME,
u.PL_COUNTRY,
u.PL_REENTRY
};
and the following query is build after the linq query:
SELECT
[Extent1].[TD_ROWID] AS [TD_ROWID],
[Extent1].[TD_NAME] AS [TD_NAME],
[Extent1].[TD_DATE] AS [TD_DATE],
[Extent2].[PL_FULLNAME] AS [PL_FULLNAME],
[Extent2].[PL_COUNTRY] AS [PL_COUNTRY],
[Extent2].[PL_REENTRY] AS [PL_REENTRY]
FROM [dbo].[TOURNAMENT_D] AS [Extent1]
LEFT OUTER JOIN [dbo].[PLAYERS_LIST] AS [Extent2] ON [Extent1].[TD_ROWID] = [Extent2].[PL_TOURNAMENT_REFNO]
I was expecting to see the dates with aliases am I missing something here?
is there a way that I can see the given aliases ?
Thanks!
EDIT:
I was expecting the query to be like
[Extent1].[TD_DATE] AS startDate,
however I can still access that property within the IQueryable
It's very important to realize that LINQ To SQL is not SQL itself. The query that is built is just the optimized query that LINQ To SQL runs on the database to pull the information it needs to pull the data into your C# object.
You should be able to call the following:
LinqQuery.startDate;
or
LinqQuery.endDate;
and those should hold the values that you defined in your select.
Please let me know if I can clarify.

Linq Join when second table will have multiple records

Trying to create a Linq query that can do multiple searches in one command instead of having multiple search result pages. It is working great when I am trying to find multiple records that have a subject (in my case CHIEF_COMPLAINT) and comments with a specific word. The problem is when I want to search with serial numbers.
There are two issues here. One is that multiple pieces of equipment can be attached to a specific ticket and also a single piece of equipment can be associated with multiple tickets. When I query the table used to associate equipment to tickets (VIEW_WT_EQUIP, using the view because it is where the serial number is seen) I potential get multiple results with the same Ticket_ID.
This is the query that I have right now, but it returns no results when I put in a serial number that I know is in the system.
var query = from a in db.VIEW_WT_HEADERs
join c in db.VIEW_WT_EQUIPs on a.TICKET_ID equals c.TICKET_NUMBER into c_group
from c2 in c_group
join b in db.WT_EVENTs on a.TICKET_ID equals b.TICKET_ID
where b.COMMENTS.Contains(input) || a.CHIEF_COMPLAINT.Contains(input) || c2.SERIAL_NUMBER.Contains(input)
orderby a.TICKET_ID descending
select new { a.TICKET_ID, a.ENTRY_DATE, a.CONTACT, a.CHIEF_COMPLAINT, a.STATUS };
I also tried a method where I used 2 linq queries and put all the ticket numbers from a serial number search into a list, but the second query didn't like that I was trying to compare an int array.
I think I am just going about this wrong. Join is probably not the right way to do this, but I don't know how to tell the main query to pull all the tickets associated with a piece of equipment.
Please let me know where I can clarify, because I know this explination is rough.
I would put this as comment instead of an answer, but I want to show you some code, so I had to choose "answer".
If you are using Linq to Entities, you probably have a relationship between the objects. It means that the join is not necessary. You should only use join when no navigation property is available.
I can't tell exactly what you should do, but here is some code the might be helpful:
var query = from a in db.VIEW_WT_HEADERs
from b in a.WT_EVENTs
from c in a.VIEW_WT_EQUIPs
where b.COMMENTS.Contains(input) || a.CHIEF_COMPLAINT.Contains(input) ...
orderby a.TICKET_ID descending
select new { a.TICKET_ID, a.ENTRY_DATE, a.CONTACT, a.CHIEF_COMPLAINT, a.STATUS };
you can also use let to store a sub-expression:
var query = from a in db.VIEW_WT_HEADERs
from b in a.WT_EVENTs
from c in a.VIEW_WT_EQUIPs
let x = c.FirstOrDefault()
where b.COMMENTS.Contains(input) || a.CHIEF_COMPLAINT.Contains(input) || x.SomeProperty ....
orderby a.TICKET_ID descending
select new { a.TICKET_ID, a.ENTRY_DATE, a.CONTACT, a.CHIEF_COMPLAINT, a.STATUS };
Those are just example, maybe it helps!

Linq To SQL equivalent group by with multiple table columns in the output

I have just started on a project that uses Linq To SQL (there are various reasons why this is so, but for the moment, that is what is being used, not EF or ANOther ORM).
I have been tasked with migrating old (and I'm talking VB6 here) legacy code.
I come from a predominantly T-SQL background, so I knocked up a query that would do what I want, but I have to use LINQ to SQL (c# 3.5), which I don't have much experience with.
Note that the database will be SQL Server 2008 R2 and/or SQL Azure
Here is the T-SQL (simplified)
SELECT TBS.ServiceAvailID, sum(Places) as TakenPlaces,MAX(SA.TakenPlaces)
FROM TourBookService TBS
JOIN TourBooking TB
ON TB.TourBookID=TBS.TourBookID
JOIN ServiceAvail SA
ON TBS.ServiceAvailID = SA.ServiceAvailID
WHERE TB.Status = 10
AND ServiceSvrCode='test'
GROUP BY TBS.ServiceAvailID
HAVING sum(Places) <> MAX(SA.TakenPlaces)
So, there is a TourBooking table which has details of a customer's booking. This hangs off the TourBookService table which has details of the service they have booked. There is also a ServiceAvail table which links to the TourBookService table. Now, the sum of the Places should equal the Taken places amount in the ServiceAvail table, but sometimes this is not the case. This query gives back anything where this is not the case. I can create the Linq to just get the sum(places) details, but I am struggling to get the syntax to also get the TakenPlaces (note that this doesn't include the HAVING clause either)
var q = from tbs in TourBookServices
join tb in TourBookings on tbs.TourBookID equals tb.TourBookID
join sa in ServiceAvails on tbs.ServiceAvailID equals sa.ServiceAvailID
where (tb.Status == 10)
&& ( tbs.ServiceSvrCode =="test")
group tbs by tbs.ServiceAvailID
into g
select new {g.Key, TotalPlaces = g.Sum(p => p.Places)};
I need to somehow get the sa table into the group so that I can add g.Max(p=>p.PlacesTaken) to the select.
Am I trying to force T-SQL thinking into LINQ ?
I could just have another query that gets all the appropriate details from the ServiceAvail table, then loop through both result sets and match on the key, which would be easy to do, but feels wrong (but that may just be me!)
Any comments would be appreciated.
UPDATE:
As per the accepted answer below, this is what Linqer gave me. I will have a play and see what SQL it actually creates.
from tbs in db.TourBookService
join sa in db.ServiceAvail on tbs.ServiceAvailID equals sa.ServiceAvailID
where
tbs.TourBooking.Status == 10
tbs.ServiceSvrCode == "test")
group new {tbs, sa} by new {
tbs.ServiceAvailID
} into g
where g.Sum(p => p.tbs.Places) != g.Max(p => p.sa.TakenPlaces)
select new {
ServiceAvailID = (System.Int32?)g.Key.ServiceAvailID,
TakenPlaces = (System.Int32?)g.Sum(p => p.tbs.Places),
Column1 = (System.Int32?)g.Max(p => p.sa.TakenPlaces)
}
In your case I would try to use some kind of converter in my personal experience I used this program http://sqltolinq.com/ it often works very well in convertitng sql to linq.

Working with Cross Context Joins in LINQ-to-SQL

Initially I had written this query using LINQ-to-SQL
var result = from w in PatternDataContext.Windows
join cf in PatternDataContext.ControlFocus on w.WindowId equals cf.WindowId
join p in PatternDataContext.Patterns on cf.CFId equals p.CFId
join r in ResultDataContext.Results on p.PatternId equals r.PatternId
join fi in ResultDataContext.IclFileInfos on r.IclFileId equals fi.IclFileId
join sp in sessionProfileDataContext.ServerProfiles on fi.ServerProfileId equals sp.ProfileId
join u in infrastructure.Users on sp.UserId equals u.Id
where w.Process.Equals(processName)
select u.DistributedAppId;
And when I executed it, and saw result in the QuickWatch.., it showed this message:
the query contains references to items defined on a different data context
On googling, I found this topic at Stackoverflow itself, where I learned simulating cross context joins and as suggested there, I changed my query a bit to this:
var result = from w in PatternDataContext.Windows
join cf in PatternDataContext.ControlFocus on w.WindowId equals cf.WindowId
join p in PatternDataContext.Patterns on cf.CFId equals p.CFId
join r in SimulateJoinResults() on p.PatternId equals r.PatternId
join fi in SimulateJoinIclFileInfos() on r.IclFileId equals fi.IclFileId
join sp in SimulateJoinServerProfiles() on fi.ServerProfileId equals sp.ProfileId
join u in SimulateJoinUsers() on sp.UserId equals u.Id
where w.Process.Equals(processName)
select u.DistributedAppId;
This query is using these SimulateXyz methods:
private static IQueryable<Result> SimulateJoinResults()
{
return from r in SessionDataProvider.Instance.ResultDataContext.Results select r;
}
private static IQueryable<IclFileInfo> SimulateJoinIclFileInfos()
{
return from f in SessionDataProvider.Instance.ResultDataContext.IclFileInfos select f;
}
private static IQueryable<ServerProfile> SimulateJoinServerProfiles()
{
return from sp in sessionProfileDataContext.ServerProfiles select sp;
}
private static IQueryable<User> SimulateJoinUsers()
{
return from u in infrastructureDataContext.Users select u;
}
But even this approach didn't solve the problem. I'm still getting this message in QuickWatch...:
the query contains references to items defined on a different data context
Any solution for this problem? Along with the solution, I would also want to know why the problem still exists, and how exactly the new solution removes it, so that from next time I could solve such problems myself. I'm new to LINQ, by the way.
I've had to do this before, and there are two ways to do it.
The first is to move all the servers into a single context. You do this by pointing LINQ-to-SQL to a single server, then, in that server, create linked servers to all the other servers. Then you just create views for any tables you're interested from the other servers, and add those views to your context.
The second is to manually do the joins yourself, by pulling in data from one context, and using just the properties you need to join into another context. For example,
int[] patternIds = SessionDataProvider.Instance.ResultDataContext.Results.Select(o => o.patternId).ToArray();
var results = from p in PatternDataContext.Patterns
where patternIds.Contains(p.PatternId)
select p;
Though the first is easier to work with, it does have its share of problems. The problem is that you're relying on SQL Server to be performant with linked servers, something it is notoriously bad at. For example, consider this query:
var results = from p in DataContext.Patterns
join r in DataContext.LinkedServerResults on p.PatternId equals r.PatternId
where r.userId = 10;
When you enumerate this query, the following will occur (let's call the normal and linked servers MyServer and MyLinkedServer, respectively)
MyServer asks MyLinkedServer for the Results
MyLinkedServer sends the Results back to MyServer
MyServer takes those Results, joins them on the Patterns table, and returns only the ones with Results.userId = 10
So now the question is: When is the filtering done - on MyServer or MyLinkedServer? In my experience, for such a simple query, it will usually be done on MyLinkedServer. However, once the query gets more complicated, you'll suddenly find that MyServer is requesting the entire Results table from MyLinkedServer and doing the filtering after the join! This wastes bandwidth, and, if the Results tables is large enough, could turn a 50ms query into a 50 second query!
You could fix unperformant cross-server joins using stored procedures, but if you do a lot of complex cross-server joins, you may end up writing stored procedures for most of your queries, which is a lot of work and defeats part of the purpose of using L2SQL in the first place (not having to write a lot of SQL).
In comparison, the following code would always perform the filtering on the server containing the Results table:
int[] patternIds = (from r in SessionDataProvider.Instance.ResultDataContext.Results
where r.userId = 10
select r.PatternId).ToArray();
var results = from p in PatternDataContext.Patterns
where patternIds.Contains(p.PatternId)
select p;
Which is best for your situation is up to your best judgement.
Note that there is a third potential solution which I did not mention, as it is not really a programmer-solution: you could ask your server admins to set up a replication task to copy the necessary data from MyLinkedServer to MyServer once a day/week/month. This is only an option if:
Your program can work with slightly stale data from MyLinkedServer
You only need to read, never write, to MyLinkedServer
The tables you need from MyLinkedServers are not exorbitantly huge
You have the space/bandwidth available
Your database admins are not stingy/lazy
Your SimulateJoins can't work because they return IQueryable. Your current solution is exactly the same as your former one and that is the reason why you get the same exception. If you check the linked question again you will see that their helper methods return IEnumerable which is the only way to make cross context operations. As you probably already know it means that join will be performed in memory on the application server instead of the database server = it will pull all data from your partial queries and execute join as linq-to-objects.
Cross context join on database level is IMO not possible. You can have different connections, different connection strings with different servers, etc. Linq-to-sql does not handle this.
You could work around it by "escaping from" Linq to SQL on the second context, i.e., calling for instance .ToList() on ResultDataContext.Results and ResultDataContext.IclFileInfos so that your query ended up looking like:
var result = from w in PatternDataContext.Windows
join cf in PatternDataContext.ControlFocus on w.WindowId equals cf.WindowId
join p in PatternDataContext.Patterns on cf.CFId equals p.CFId
join r in ResultDataContext.Results.ToList()
on p.PatternId equals r.PatternId
join fi in ResultDataContext.IclFileInfos.ToList()
on r.IclFileId equals fi.IclFileId
join sp in sessionProfileDataContext.ServerProfiles on
fi.ServerProfileId equals sp.ProfileId
join u in infrastructure.Users on sp.UserId equals u.Id
where w.Process.Equals(processName)
select u.DistributedAppId;
Or AsEnumerable() as long as you "get out" of Linq to SQL and into Linq to Objects for the "offending" context.
Old question, but as I happened to have the same problem, my solution was to pass the manually crafted T-SQL cross-server query (with linked servers) directly to the provider through the ExecuteQuery method of the first context:
db.ExecuteQuery(Of cTechSupportCall)(strSql).ToList
This just saves you from having to create a view server side, and Linq to SQL still maps the results to the proper type. This is useful when there is that one query that is just impossible to formulate in Linq.

Joins and subqueries in LINQ

I am trying to do a join with a sub query and can't seem to get it. Here is what is looks like working in sql. How do I get to to work in linq?
SELECT po.*, p.PermissionID
FROM PermissibleObjects po
INNER JOIN PermissibleObjects_Permissions po_p ON (po.PermissibleObjectID = po_p.PermissibleObjectID)
INNER JOIN Permissions p ON (po_p.PermissionID = p.PermissionID)
LEFT OUTER JOIN
(
SELECT u_po.PermissionID, u_po.PermissibleObjectID
FROM Users_PermissibleObjects u_po
WHERE u_po.UserID = '2F160457-7355-4B59-861F-9871A45FD166'
) used ON (p.PermissionID = used.PermissionID AND po.PermissibleObjectID = used.PermissibleObjectID)
WHERE used.PermissionID is null
Without seeing your database and data model, it's pretty impossible to offer any real help. But, probably the best way to go is:
download linqpad - http://www.linqpad.net/
create a connection to your database
start with the innermost piece - the subquery with the "where" clause
get each small query working, then join them up. Linqpad will show you the generated SQL, as well as the results, so build your small queries up until they are right
So, basically, split your problem up into smaller pieces. Linqpad is fantastic as it lets you test these things out, and check your results as you go
hope this helps, good luck
Toby
The LINQ translation for your query is suprisingly simple:
from pop in PermissibleObjectPermissions
where !pop.UserPermissibleObjects.Any (
upo => upo.UserID == new Guid ("2F160457-7355-4B59-861F-9871A45FD166"))
select new { pop.PermissibleObject, pop.PermissionID }
In words: "From all object permissions, retrieve those with at least one user-permission whose UserID is 2F160457-7355-4B59-861F-9871A45FD16".
You'll notice that this query uses association properties for navigating relationships - this avoids the need for "joining" and simplfies the query. As a result, the LINQ query is much closer to its description in English than the original SQL query.
The trick, when writing LINQ queries, is to get out of the habit of "transliterating" SQL into LINQ.

Categories