linq multi left join to same property - c#

Is it possible to write a LINQ for this query? (in .NET 3.5)
select tt.id
,tt.detailscount
,ttdmx.detailorder
,ttdmx.detailtype
,ttdm1.detailorder
,ttdm1.detailtype
,ttdm2.detailorder
,ttdm2.detailtype
from test.testtable tt
left join test.testtabledetails ttdmx on tt.id = ttdmx.causeid
and tt.maxcausedetailorder = ttdmx.detailorder
left join test.testtabledetails ttdm1 on tt.id = ttdm1.causeid
and tt.maxcausedetailorder - 1 = ttdm1.detailorder
left join test.testtabledetails ttdm2 on tt.id = ttdm2.causeid
and tt.maxcausedetailorder - 2 = ttdm2.detailorder
The classes are something like this:
public class Test {
public int id {get;set;}
public IList<TestDetails> details {get;set}
}
and
public class TestDetails {
public int id {get;set;}
public int detailOrder {get;set;}
public int detailType {get;set;}
}
I tried with queryOver but it can't join to the same property twice.

The easiest way would be to write:
...
from ttdmx in db.testtabledetails.Where(x =>
tt.id == x.causeid &&
tt.maxcausedetailorder == x.detailorder).DefaultIfEmpty()
...
This will be translated into a SQL left join

Related

Convert complex SQL query to LINQ

Please see my below SQL query and I need to convert it in LINQ, I tried several way but I can not get it working.
This is my SQL query
SELECT SL_Transactions.TransID, SL_Trans_Details.TransDetID AS Expr2, SL_Transactions.PAY, SL_Transactions.Allocated, SL_Debits.INV, SL_Debits.ADR, SL_Debits.RFD
FROM SL_Trans_Details AS SL_DebitDetails LEFT OUTER JOIN
SL_Transactions AS SL_Debits ON SL_DebitDetails.TransID = SL_Debits.TransID RIGHT OUTER JOIN
SL_Trans_Allocation_History ON SL_DebitDetails.TransDetID = SL_Trans_Allocation_History.DebitTransDetID RIGHT OUTER JOIN
SL_Transactions INNER JOIN
SL_Trans_Details ON SL_Transactions.TransID = SL_Trans_Details.TransID ON SL_Trans_Allocation_History.CreditTransDetID = SL_Trans_Details.TransDetID
WHERE (SL_Transactions.PAY = 254)
I have tried below way but not solved yet.
C# class
public class InvoiceReturnValue
{
public int TransID { get; set; }
public int? INV { get; set; }
public int? RFD { get; set; }
}
LINQ Query
var Invoiedata = from SL_DebitDetails in Db.SL_Trans_Details
join SL_Debits in Db.SL_Transactions on SL_DebitDetails.TransID equals SL_Debits.TransID
join SL_Trans_All in Db.SL_Trans_Allocation_History on SL_DebitDetails.TransDetID equals SL_Trans_All.DebitTransDetID
join SL_Trans in Db.SL_Transactions on SL_DebitDetails.TransID equals SL_Trans.TransID
where SL_Trans.PAY == 250
select new InvoiceReturnValue
{
TransID= SL_Trans.TransID,
INV= SL_Debits.INV,
RFD= SL_Debits.RFD
};
Table relation below.

How to query multiple tables using LINQ syntax in .NET core 3

I need to query two database tables for a search term and return the results. The following was working in EF Core 2:
var SearchTerm = "hello";
IQueryable<TableA> q;
q = (from a in context.TableA
join b in context.TableB on a equals b.A into leftjoin
from c in leftjoin.DefaultIfEmpty()
where c.Column1.Contains(SearchTerm)
|| a.Column1.Contains(SearchTerm)
|| a.Column2.Contains(SearchTerm)
select a);
return q.Include(a => a.TableD)
.GroupBy(a => a.Id)
.Select(group => group.First())
.ToList();
The idea of the above is to take a SearchTerm and query two columns from TableA, join to TableB and also query a column in this one then select distinct values from TableA.
In .NET 3 the above throws an error saying it can't be translated to SQL. I tried to rewrite this, the best I can do is the below:
var SearchTerm = "hello";
var q = (from a in context.TableA
join b in context.TableB on a equals b.A into leftjoin
from c in leftjoin.DefaultIfEmpty()
where c.Column1.Contains(SearchTerm)
|| a.Column1.Contains(SearchTerm)
|| a.Column2.Contains(SearchTerm)
select a.Id).Distinct().ToList();
return context.TableA
.Where(a => q.Contains(a.Id))
.Include(c => c.TableD)
.ToList();
Which works ok but involves two database queries, since I already have the list of TableA from the first query it would be great to be able to just use this without having to extract the Ids and performing the second query. Also making sure the database continues to handle the distinct part rather than C# would be preferable too.
The definitions of A and B are:
public class TableA
{
public int Id { get; set; }
public string Column1 { get; set; }
public string Column2 { get; set; }
public int TableDId { get; set; }
public TableD TableD { get; set; }
}
public class TableB
{
public int Id { get; set; }
public string Column1 { get; set; }
public int TableAId { get; set; }
public TableA TableA { get; set; }
}
If I understood you correctly you have one-to-many relation between TableA and TableB, so it should be possible to add collection navigation property to TableA like in this tutorial for example:
public class TableA
{
public int Id { get; set; }
...
public ICollection<TableB> TableBs { get; set; }
}
So you can try to do something like this:
context.TableA
.Where(ta => ta.TableBs.Any(tb => tb.Column1.Contains(SearchTerm))
|| ta.Column1.Contains(SearchTerm)
|| ta.Column2.Contains(SearchTerm))
.Include(c => c.TableD)
.ToList();
Another option is to try subquery:
var q = (from a in context.TableA
join b in context.TableB on a.Id equals b.TableAId into leftjoin
from c in leftjoin.DefaultIfEmpty()
where c.Column1.Contains(SearchTerm)
|| a.Column1.Contains(SearchTerm)
|| a.Column2.Contains(SearchTerm)
select a.Id); // remove Distinct and ToList
return context.TableA
.Where(a => q.Contains(a.Id))
.Include(c => c.TableD)
.ToList();

LINQ to SQL and LINQ to Entity Join with static AND condition

I'm trying to convert the following Join statement into LINQ TO SQL or LINQ to Entity. I know how to join tables in either implementation; but, i'm struggling with the AND clause in the Join statement.
SELECT DISTINCT
p.LastName,
p.FirstName
FROM
dbo.Patient p INNER JOIN dbo.FormPat fp ON p.PatientID = fp.PatientID
INNER JOIN dbo.TxCyclePhase tcp ON fp.TxCyclePhase = tcp.TxCyclePhaseID AND tcp.Type = 2
As far as LINQ to SQL is concerned, I have the followings:
var query = (from p in Context.Set<Patient>().AsNoTracking()
join fp in Context.Set<PatientForm>().AsNoTracking() on p.Id equals fp.PatientId
join tcp in Context.Set<TxCyclePhase>().AsNoTracking() on new { fp.TxCyclePhaseId, seconProperty = true } equals new { tcp.Id, seconProperty = tcp.Type == 2 }
select new
{
p.FirstName,
p.LastName,
}).Distinct();
However, I'm getting an ArgumentNullException on the second join statement.
For the LINQ to Entity, I have the followings, however, this is giving me a distinct IQueryable of FormPat, instead of Patient.
var patients = Context.Set<Patient>().AsNoTracking()
.SelectMany(p => p.Forms)
.Where(fp => fp.Phase.Type == 2)
.Distinct();
As far as the LINQ to Entity is concerned, I was able to figure it out. I'd still like to know how to do it in LINQ to SQL tho.
I'm using the EF fluent API. My Patient object looks like:
public Patient()
{
Programs = new HashSet<Program>();
}
public virtual ICollection<PatientForm> Forms { get; set; }
My PatientForm object looks like:
public class PatientForm
{
public int FormId { get; set; }
public Patient CurrentPatient { get; set; }
public TxCyclePhase Phase { get; set; }
}
And the CyclePhase object looks like:
public TxCyclePhase()
{
this.FormPats = new HashSet<PatientForm>();
}
public int Id { get; set; }
public virtual ICollection<PatientForm> FormPats { get; set; }
In the entity configurations, I have the relationships set. So, in the repository, all I have to do is to use the Any() function when selecting the Patient forms.
var patients = Context.Set<Patient>().AsNoTracking()
.Where(p => p.Forms.Any(f => f.Phase.Type == 2))
.Distinct();

LINQ - Searching one to many for exact match of array

Let me preface by saying that this is self study and I am trying to self learn LINQ and Entity Framework. I've spent a few days attempting to turn the SQL statement at the bottom of this question into LINQ with terrible results. I've also included the SQL diagram at the bottom.
My goal is to select all stories that have the same characters as the passed in string array. I'm not wanting stories returned that have additional characters or missing characters. This is what my feeble LINQ skills have come up with so far:
var characters = new string[] { "Harry", "Tom" };
var cq = _context.TblCharacter.AsNoTracking().Where(c => characters.Contains(c.NameVc));
var q = from c in cq
join sc in _context.TblStoryCharacter.AsNoTracking()
on c.IdI equals sc.CharacterIdI
join s in _context.TblStory.AsNoTracking().Include(s => s.TblStoryCharacter).ThenInclude(sc => sc.CharacterIdINavigation)
on sc.StoryIdI equals s.IdI
where s.TblStoryCharacter.Count() == characters.Length
where s.TblStoryCharacter.Where(sc => characters.Contains(sc.CharacterIdINavigation.NameVc)).Count() == characters.Length
select s;
The above code is spawning a bunch of queries (SQL profiler image below) and is loading a bunch of objects into memory. Is there any LINQ magic for this scenario?
LINQ spawned queries:
SELECT [t0].[StoryId_i]
FROM [tbl_story_character] AS [t0]
SELECT [sc1].[StoryId_i]
FROM [tbl_story_character] AS [sc1]
INNER JOIN [tbl_character] AS [sc.CharacterIdINavigation0] ON [sc1].[CharacterId_i] = [sc.CharacterIdINavigation0].[Id_i]
WHERE [sc.CharacterIdINavigation0].[Name_vc] IN ('Harry', 'Tom')
And this is the SQL I started out trying to convert to LINQ:
select *
from tbl_story
where Id_i in (
select sc.StoryId_i
from tbl_story_character sc
inner join tbl_character c
on c.Id_i = sc.CharacterId_i
where c.Name_vc in ('Harry', 'Tom')
and not exists (
select *
from tbl_story_character subsc
inner join tbl_character subc
on subc.Id_i = subsc.CharacterId_i
where subc.Name_vc not in ('Harry', 'Tom')
and subsc.StoryId_i = sc.StoryId_i
)
group by sc.StoryId_i
having count(*) = 2
)
Database diagram:
EDIT:
The models were generated by EFCore based on an existing database, each model contains navigation properties based on the foreign keys in the diagram.
New LINQ after taking advice of Jon Skeet and Munzer.
from s in _context.TblStory.AsNoTracking()
.Include(s => s.AuthorIdINavigation)
.Include(s => s.TblStoryCharacter)
.ThenInclude(sc => sc.CharacterIdINavigation)
where s.TblStoryCharacter.All(sc => characters.Contains(sc.CharacterIdINavigation.NameVc))
where s.TblStoryCharacter.Count == 2
select s;
This results in the following SQL which seems correct.
SELECT [s].[Id_i], [s].[AuthorId_i], [s].[Published_dt]
FROM [tbl_story] AS [s]
INNER JOIN [tbl_author] AS [t2] ON [s].[AuthorId_i] = [t2].[Id_i]
WHERE NOT EXISTS (
SELECT 1
FROM [tbl_story_character] AS [sc]
INNER JOIN [tbl_character] AS [sc.CharacterIdINavigation] ON [sc].[CharacterId_i] = [sc.CharacterIdINavigation].[Id_i]
WHERE ([s].[Id_i] = [sc].[StoryId_i]) AND [sc.CharacterIdINavigation].[Name_vc] NOT IN ('Harry', 'Tom')) AND ((
SELECT COUNT(*)
FROM [tbl_story_character] AS [t]
WHERE [s].[Id_i] = [t].[StoryId_i]
) = 2)
ORDER BY [s].[Id_i]
I believe All is what you are looking for here
it should be something like this
var q = from c in cq
join sc in _context.TblStoryCharacter.AsNoTracking()
on c.IdI equals sc.CharacterIdI
join s in _context.TblStory.AsNoTracking().Include(s => s.TblStoryCharacter).ThenInclude(sc => sc.CharacterIdINavigation)
on sc.StoryIdI equals s.IdI
where s.TblStoryCharacter.All(sc => characters.Contains(sc.CharacterIdINavigation.NameVc))
select s;
Try something like this :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] names = {"Harry", "Tom"};
var results = (from sc in Story_Character.story_character
join chr in Character.character on sc.Id_i equals chr.Id_i
join st in Story.story on sc.Id_i equals st.Id_i
join auth in Author.author on sc.Id_i equals auth.Id_i
where names.Contains(chr.Name_vc)
select new { sc = sc, chr = chr, st = st, auth = auth })
.GroupBy(x => x.sc.StoryId_i).Where(x => x.Count() >= 2).ToList();
}
}
public class Story_Character
{
public static List<Story_Character> story_character = new List<Story_Character>();
public int Id_i { get; set; }
public int StoryId_i { get; set; }
public int CharacterId_i { get; set; }
}
public class Character
{
public static List<Character> character = new List<Character>();
public int Id_i { get; set; }
public string Name_vc { get; set; }
}
public class Story
{
public static List<Story> story = new List<Story>();
public int Id_i { get; set; }
public DateTime Published_dt { get; set; }
public string Title_vc { get; set; }
public string AuthorId_i { get; set; }
}
public class Author
{
public static List<Author> author = new List<Author>();
public int Id_i { get; set; }
public string Name_vc { get; set; }
public string Url_vc { get; set; }
}
}

Linq query with many to many relations using entity framework

I have two tables: tbA and tbB .Between them I have a relationship n to n, so a table tbAB was generated in the database. I am using an Entity Framework Database First, then when I mapped these tables , it does not generate a specific entity for tbAB. Thus , I'm not seeing how I can create a query relating the two tables if I can't call directly thetbAB.
What I want to do in SQL would be as follows :
SELECT *
FROM tbA
INNER JOIN tbAB
ON tbAB.idA = tbA.idA
INNER JOIN tbB
ON tbB.idB = tbAB.idB
That's what I'm trying to do with Linq:
var table = (from l in db.tbA
join k in db.tbB on l.? equals k.?
where ?.IDCONCESSAO == objectTbB.IDCONCESSAO
select l).ToList();
The question is how can I do this in a Linq expression ?
Thanks in advance.
Following the model proposed by #Michal, you could do this:
var query= from a in db.TableAs
from b in a.TableBs
where b.Id==10
select new{A_Id=a.Id,a.Name, B_Id=b.Id,b.Price,...};
In the select you can choose the properties you need from both entities(I also select a Name from TableA and a Price from TableBto help you understand better this example).From each direction of the relationship, you don’t ever interact with the junction table, you just follow a relationship from each direction as if it were a one-to-many. The query that I show above will be translated in a sql query where the joins between the tables will be made this way:
{SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Join1].[Id] AS [Id1],
[Join1].[Price] AS [Price]
FROM [dbo].[TableAs] AS [Extent1]
INNER JOIN (SELECT [Extent2].[TableA_Id] AS [TableA_Id], [Extent3].[Id] AS [Id], [Extent3].[Price] AS [Price]
FROM [dbo].[TableBTableAs] AS [Extent2]
INNER JOIN [dbo].[TableBs] AS [Extent3] ON [Extent3].[Id] = [Extent2].[TableB_Id] ) AS [Join1] ON [Extent1].[Id] = [Join1].[TableA_Id]
WHERE 10 = [Join1].[Id]}
public void Test()
{
var db = new DbContext();
// This will automatically do you inner join for you.
db.TableAs.Include(a => a.TableBs);
}
Context:
public class DbContext
{
public IDbSet<TableA> TableAs { get; set; }
public IDbSet<TableB> TableBs { get; set; }
}
Models:
public class TableA
{
public int Id { get; set; }
public virtual List<TableB> TableBs { get; set; }
}
public class TableB
{
public int Id { get; set; }
public virtual List<TableA> TableAs { get; set; }
}
var table = from a in db.tbA
join ab in db.tbAB on a.idA equals ab.idA
join b in db.tbB on ab.idB equals b.idB
where a.Anything = 10
select a;
var results = table.ToList();

Categories

Resources