Avatar billede keepy Seniormester
13. september 2016 - 08:50 Der er 8 kommentarer og
2 løsninger

Linq left join

Hej
JEg prøver at lave denne lille stored procedure til LINQ men det vil bare ikke lykkes, håber nogle kan hjælpe.
SELECT
        Valgt,
        Table1.Id,
        Table1.Name,
        Table2.Id
        ,Isnull(Table2.IsDefault,0) as IsDefault
    FROM
            Table1 left join [Table2] on
            (Table1.Id = Table2.Id  AND
            Table2.Id  = @Id )
    WHERE Table1.Ready = 1

På forhånd tak
Avatar billede keepy Seniormester
13. september 2016 - 09:28 #1
Jeg har prøve noget i denne stil men det giver ingen resultat
    SqlConnection con = new SqlConnection(ConnectionString);
            Table1 con1 = new Table1(con);
            Table2 con2 = new Table2(con);
         

                Combinde_Model query = from p in con1.Table1
                            join s in con2.Table2 on p.Id equals s.Id into bag1
                            from to in bag1.DefaultIfEmpty()
                            select new
                            {
                       
                            };
Avatar billede keepy Seniormester
13. september 2016 - 09:38 #2
Jeg får forresten denne exception
ex = {"The query contains references to items defined on a different data context."}
Avatar billede softspot Forsker
13. september 2016 - 10:00 #3
Hvordan ser din objektmodel ud? Har du navigationsegenskaber på objektet, som peger fra den ene tabel til den anden tabel?
Avatar billede keepy Seniormester
13. september 2016 - 11:48 #5
Hvordan skal man gøre det sådan helt praktisk ?
Skal man bruge en "mellem" objectmodel, der refererer til de enheder man ønsker fra hver tabel, som samles i en mellem objectmodel?

Tabel1 + objectModel1
Tabel2 + objectModel2
= ObjectModel3 med enheds referancer til objectModel1 og objectModel2
Avatar billede softspot Forsker
13. september 2016 - 12:03 #6
Hvis du ikke har sat en ORM op til endnu, så er der umiddelbart et stykke vej inden du kan bruge LINQ.

Har du en database skal du have oversat den til nogle objekter. Prøv at kigge lidt på denne artikel: https://msdn.microsoft.com/en-us/library/jj200620.aspx som ser ud til at tage dig i hånden og få omsat din database til nogle Code First klasser.

Du bør dog overveje, at læse en del mere op på emnet inden du kaster det i produktion, da der er en masse forhold og arbejdsmetoder man skal være opmærksom på, når man bruger EF.
Avatar billede keepy Seniormester
13. september 2016 - 13:16 #7
Tak softspot, jeg fandt en løsning.
LINQ tillader ikke join under forskellige datacontexter. så jeg gjorde noget andet.
Avatar billede arne_v Ekspert
14. september 2016 - 03:58 #8
Det her bliver en lidt lang kommentar.

Det var dog et par gyselige SO svar.

Jeg er vildt uenig i hvordan det skal gribes an.

Men lad os starte med at slaa fast (som softspot ogsaa er inde paa i #3 og #6):
* EF er et ORM
* et ORM mapper fra en relationel database model til en objekt model
* INNER JOIN og LEFT JOIN er basale koncepter i en relationel database model
* INNER JOIN og LEFT JOIN giver ingen mening i en objekt model
* i en objekt model bruger man association/aggregation
* med et ORM saa er dine data gemt i en relationel database model men din applikation arbejder med en objekt model

Derfor giver det ingen mening at ville lave INNER JOIN eller LEFT JOIN med EF
(LINQ to EF). Man skal bruge association/aggregation.

Nok snak.

Lad mig demonstrere.

Database setup:


CREATE TABLE goodparent (
    id INTEGER NOT NULL,
    fulnam VARCHAR(20),
    PRIMARY KEY(id)
)
GO
CREATE TABLE goodchild (
    id INTEGER NOT NULL,
    val INTEGER,
    parent_id INTEGER,
    PRIMARY KEY(id),
    FOREIGN KEY (parent_id) REFERENCES goodparent(id)
)
GO

INSERT INTO goodparent VALUES (1, 'A')
GO
INSERT INTO goodparent VALUES (2, 'BB')
GO
INSERT INTO goodparent VALUES (3, 'CCC')
GO
INSERT INTO goodchild VALUES (11, 101, 1)
GO
INSERT INTO goodchild VALUES (12, 102, 1)
GO
INSERT INTO goodchild VALUES (21, 103, 2)
GO


I SQL virker en LEFT JOIN fint:


1> SELECT * FROM goodparent LEFT JOIN goodchild ON goodparent.id = goodchild.parent_id
2> GO
id          fulnam              id          val        parent_id
----------- -------------------- ----------- ----------- -----------
          1 A                            11        101          1
          1 A                            12        102          1
          2 BB                            21        103          2
          3 CCC                        NULL        NULL        NULL

(4 rows affected)


Sa genererer jeg data klasser fra databasen (ja - code first er paenere, men jeg er doven)

Og vi ser at goodparent faktisk har den noedvendige association/aggregation:

public partial class goodparent : EntityObject
{
    ...
        public EntityCollection<goodchild> goodchild
        {
            get
            {
                ...
            }
            set
            {
                ...
                }
            }
        }
...
}


og det er paerenemt at bruge:


            // good
            using(EF_LINQContext ctx = new EF_LINQContext())
            {
                foreach(goodparent gp in ctx.goodparent)
                {
                    Console.Write("{0,3} {1,-10}:", gp.id, gp.fulnam);
                    foreach(goodchild gc in gp.goodchild)
                    {
                        Console.Write(" {0}", gc.val);
                    }
                    Console.WriteLine();
                }
            }


Output:


  1 A        : 101 102
  2 BB        : 103
  3 CCC      :


Ovenstaaende er en "LEFT JOIN".
Avatar billede arne_v Ekspert
14. september 2016 - 04:07 #9
Lad os saa se paa de forkerte maader.

En ganske lille aendring af database oprettelse er nok til at bring os i problemer:


CREATE TABLE badparent (
    id INTEGER NOT NULL,
    fulnam VARCHAR(20),
    PRIMARY KEY(id)
)
GO
CREATE TABLE badchild (
    id INTEGER NOT NULL,
    val INTEGER,
    parent_id INTEGER,
    PRIMARY KEY(id)
)
GO

INSERT INTO badparent VALUES (1, 'A')
GO
INSERT INTO badparent VALUES (2, 'BB')
GO
INSERT INTO badparent VALUES (3, 'CCC')
GO
INSERT INTO badchild VALUES (11, 101, 1)
GO
INSERT INTO badchild VALUES (12, 102, 1)
GO
INSERT INTO badchild VALUES (21, 103, 2)
GO


Eneste forskel er at vi har fjernet foreign key constraint.

SQL virker fuldstaendigt ligesom foer.

Men ved kode generering, saa faar badparent ikke association/aggregration til badchild.

Og saa er det lige pludseligt lidt vanskeligere.

Og man ender hurtigt med noget spagetti kode.

Jeg synes at foelgende kode er rimelig laeselig (det er ikke sikker at den er hurtigst):


    public class Workaround : badparent
    {
        public IEnumerable<badchild> badchild;
    }
    ...
            // bad workaround
            using(EF_LINQContext ctx = new EF_LINQContext())
            {
                IQueryable<Workaround> q = ctx.badparent.GroupJoin(ctx.badchild,
                                                                  bp => bp.id,
                                                                  bc => bc.parent_id,
                                                                  (bp, allbc) => new Workaround{ id = bp.id, fulnam = bp.fulnam, badchild = allbc });
                foreach(Workaround w in q)
                {
                    Console.Write("{0,3} {1,-10}:", w.id, w.fulnam);
                    foreach(badchild bc in w.badchild)
                    {
                        Console.Write(" {0}", bc.val);
                    }
                    Console.WriteLine();
                }
            }


Det virker. Og jeg kan godt lide at man ligesom simulerer den rigtige loesning!

Du rendte saa ind i problemet at du ikke rigtigt kunne noget med to data context'er.

Men det problem er nemt at fixe. Bare materialiser dine queries, saa skifter du fra LINQ to EF til LINQ to Objects og saa har du ikke noget problem.

(ved store data maengder vil performance dog maaske give et problem)


            // bad two context workaround
            using(EF_LINQContext pctx = new EF_LINQContext())
            using(EF_LINQContext cctx = new EF_LINQContext())
            {
                IEnumerable<Workaround> q = pctx.badparent.ToList().GroupJoin(cctx.badchild.ToList(),
                                                                              bp => bp.id,
                                                                              bc => bc.parent_id,
                                                                              (bp, allbc) => new Workaround{ id = bp.id, fulnam = bp.fulnam, badchild = allbc });
                foreach(Workaround w in q)
                {
                    Console.Write("{0,3} {1,-10}:", w.id, w.fulnam);
                    foreach(badchild bc in w.badchild)
                    {
                        Console.Write(" {0}", bc.val);
                    }
                    Console.WriteLine();
                }
            }
Avatar billede keepy Seniormester
26. september 2016 - 11:54 #10
Fantastisk svar tak Arne
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester