[Linq to SQL] Update / Insert probleem

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 15:24
Ik krijg via messagig een collectie objecten die ik moet updaten in een database dmv LINQ. Ik vind LINQ tot nu toe geweldig en eenvoudig in gebruik, maar hier heb ik nog geen oplossing voor gevonden. Aanschouw mijn versimpelde testcase.

Data classes:
Afbeeldingslocatie: http://www.davidjuffermans.nl/FileUpload/files/product-order.png
Testcode:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Product p1 = new Product();
p1.Code = "A";
p1.Name = "Box";

Product p2 = new Product();
p2.Code = "A";
p2.Name = "Box";

Order o1 = new Order();
o1.OrderNumber = "1";
o1.Product = p1;
            
Order o2 = new Order();
o2.OrderNumber = "2";
o2.Product = p2;

List<Order> orders = new List<Order>();
orders.Add(o1);
orders.Add(o2);

string connString = "Data Source=(local);Initial Catalog=TestDB;Integrated Security=True";
TestDataClassesDataContext dc = new TestDataClassesDataContext(connString);

foreach(Order o in orders
   dc.Orders.InsertOnSubmit(o);

dc.SubmitChanges();

Het probleem is hier dus dat ik twee orders heb met hetzelfde product. Al zou ik maar één order hebben werkt deze code. Ik zou nu in de loop willen controleren of het Product van een Order al bestaat. Probleem is dan dat voordat SubmitChanges() aangeroepen is het Product niet de de dc.Products collectie staat (is immers nog niet geinsert). De oplossing die ik nu heb is het aanroepen van SubmitChanges in elke iteratie van de loop + een controle of het Product al bestaat, maar hier krijg ik toch nare onderbuikgevoelens van... Hoe kan ik dit eleganter oplossen?

Roomba E5 te koop


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 13:23
Even voor de goede orde: jouw systeem moet dus toelaten dat een klant een order plaatst voor een product dat nog niet bestaat / nog niet in jouw DB zit ? Maw: een klant kan producten aanmaken door een order te plaatsen voor dat product ?
Dat wil dus zeggen dat, als jij een online boekenwinkel hebt, ik ahw een vliegtuig kan bestellen bij jou ?

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 15:24
sig69 schreef op woensdag 26 maart 2008 @ 16:51:
Aanschouw mijn versimpelde testcase.
Nee, mijn systeem heeft niets met producten of orders te maken (dus gaarne ook geen kritiek op het incorrecte datamodel voordat iemand hier over begint), maar dezelfde situatie doet zich voor. Dit is een testcase waarmee ik dit probleem probeer te tackelen aangezien de andere solution 2 minuten nodig heeft om te builden.

Roomba E5 te koop


Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
je maakt nieuwe entity class instances aan, en dan ga je je afvragen of ze bestaan of niet. Het antwoord is dan altijd: NEE, die bestaan niet. Tenzij, je dezelfde _DATA_ in die NIEUWE entity class instances stopt als product entities in de database.

Dat voorkom je door de products te laden, dan te assignen aan de orders en dan die te saven. Als je nieuwe product instances aanmaakt en je bent bang voor een UC/pk violation, dan moet je error handling inbouwen achteraf, want vooraf is dat niet te bepalen. Je kunt dan de conflicts collectie opvragen en de entities eruit gooien die een conflict hebben.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Heb heb wel eens gewerkt met een typed dataset en daaraan een nieuwe record toegevoegd? De DataSet kent aan elke nieuwe record dan een negatief autonumering ID toe, dus -1, -2, -3. Achter de schermen gebruikt linq to sql eenzelfde techniek.

Dat betekend dat je in je voorbeeld twee producten hebt aangemaakt met ID -1 en -2, maar beide producten hebben wel dezelfde beschijvingen (net zoals in een klant tabel meerdere keren dezelfde achternaam kan voorkomen). Beide orders hebben dus allebei 'hun' een eigen nieuwe product gekregen.

Als je wilt dat beide hetzelfde product zouden delen, dus zul je gebruik moeten maken van een broker (manager). ProductManager.GetProduct(string code, string desc);. Als deze het product niet kan vinden, maakt deze een nieuwe aan. Als je de functie kan voor een tweede maal aanroept wordt het eerder aangemaakt product terug gegeven.

Het probleem heeft niet zozeer met linq to sql te maken, maar eerder op de manier waarop jij het systeem misbruikt.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 15:24
Ik had eigenlijk verwacht dat dit wel zou werken, nou ja, jammer dan. Ik ben nog wat verder aan het spelen geweest en loop tegen nog iets vreemds aan. Dit is de inhoud van m'n database:
Afbeeldingslocatie: http://www.davidjuffermans.nl/FileUpload/files/db.png
Vervolgens ga ik dit product updaten dmv de Attach() functie (is veel over te lezen idd op internet, maar dit is iets anders).
C#:
1
2
3
4
5
6
7
8
9
10
11
12
Product p1 = new Product(); // Origineel
p1.Code = "A";
p1.Name = "Box";

Product p2 = new Product(); // Update
p2.Code = "A";
p2.Name = "Box2";

string connString = "Data Source=(local);Initial Catalog=TestDB;Integrated Security=True"; 
TestDataClassesDataContext dc = new TestDataClassesDataContext(connString); 

dc.Products.Attach(p2, p1);

Als ik nu in de debugger kijk:
Afbeeldingslocatie: http://www.davidjuffermans.nl/FileUpload/files/product1.png
Een select op Code levert het juiste product op met de geupdate Name.
Afbeeldingslocatie: http://www.davidjuffermans.nl/FileUpload/files/product2.png
Een select op de nieuwe Name levert niets op!

Roomba E5 te koop


Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Ik zou eerst de handel even saven ;) (Commit Changes)

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 15:24
Ja dat snap ik, maar het is toch vreemd dat als je een Product uit de DataContext haalt adv de key Code, de nieuwe waarde van Name wel in dat object staat, maar als ik uit diezelfde DataContext een Product wil halen adv de nieuwe waarde van Name, dit niet kan?

Roomba E5 te koop


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Nee, is niet vreemd, tenminste als je de theorie achter linq to sql kent. linq houd dat geen collecties bij, wel kan deze states bijhouden. Alle instructies worden namelijk omgezet naar SQL statements. Dus ondanks dat jij Product via Attach aan de table hebt gekoppeld, doet de select daar niets mee. Alleen SubmitChanges maakt gebruik van de object states.

Als jij dus probeert via p.name == "box2" een product op te halen voordat je de wijzigingen hebt doorgevoegd voert linq to sql de query select * from products where name='box2' uit.

Overigens kun je beter tijdens het debuggen op SingleOrDefault gaan staan en dat krijg zie je in de tooltip ook een vergrootglas. Als je daarop klikt zie je de query welke uitgevoerd gaat worden.

Linq To Sql is geen DataSet. Vrijwel alle acties van Linq To Sql worden onafhankelijk van elkaar uitgevoerd.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Toch valt er wel wat voor te zeggen voor wat sig69 wilt doen. Het punt is nl, dat hij een linq to OBJECTS query denkt uit te voeren IN MEMORY op de Products collection in de datacontext (en die heeft dat object dat hij zoekt!) maar de query wordt een linq to SQL query en daar bestaat dat object niet.

Dit komt omdat context.Products IQueryable<T> implementeert en de compiler dan een Expression tree bouwt (== code genereert die een expression tree bouwt at runtime) ipv code die delegates aanroept voor linq to objects via extension methods van IEnumerable<T>).

Het is niet de enige design flaw overigens.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Nou daar valt eigenlijk niets voor te zeggen. Linq To Sql is *GEEN* entity framework! Het is slechts een directe O/R mapper naar je database.

Je kunt het feit dat linq niet met in memory objecten werkt zien als een nadeel, maar het is juist ook de kracht (de expressie tree). Doordoor kun je op dezelfde manier gebruik maken van Linq To Xml, Linq To ClieOp. Linq is een univorme manier om je data te benaderen.

Ik maak regelmatig mee dat technieken voor de verkeerde doeleinde worden gebruikt. Meestal omdat men niet voldoende bekend is met de werking van de techniek. Eenzelfde probleem zag je 6 maanden geleden ook met de ajax technieken. Je moet een techniek niet gebruiken omdat het kan, maar omdat het een toegevoegde waarde is.

Maar daar zit eigenlijk ook meteen de knauw. Lezen over een techniek zorgt in elk geval dat je theoretische kennis over de techniek op orde, maar om een techniek echt op waarde te kunnen schatten (en daarmee dus de voor- en nadelen) zul je het moeten toepassen in proof-of-concept projecten en er de gekste dingen mee proberen.

Maar ik ben eigenlijk best wel benieuwd welke design flaws je ziet in Linq (To Sql).

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Niemand_Anders schreef op zaterdag 29 maart 2008 @ 16:24:
Nou daar valt eigenlijk niets voor te zeggen. Linq To Sql is *GEEN* entity framework! Het is slechts een directe O/R mapper naar je database.
Mja, wat wil je daarmee zeggen?
Je kunt het feit dat linq niet met in memory objecten werkt zien als een nadeel, maar het is juist ook de kracht (de expressie tree). Doordoor kun je op dezelfde manier gebruik maken van Linq To Xml, Linq To ClieOp. Linq is een univorme manier om je data te benaderen.
Linq werkt wel met in-memory objects, dat heet linq to objects (iedere IEnumerable<T> werkt daarmee). Omdat TS een in-memory collectie wil searchen middels linq (linq to objects) denkt hij dan resultaten terug te krijgen. Echter, de collectie waar de entity in verwacht wordt is een placeholder voor het bouwen van expression trees.

Dat is niet altijd even duidelijk. Linq is een taalconstructie en de compiler gaat OF code aanmaken die extension methods aanroept van IEnumerable<T> (en die roepen delegates aan zodat de in-memory object graph gequeried wordt. GEEN expression tree wordt gebouwd) OF code aanmaken die extension methods aanroept van IQueryable<T> (en die roepen Expression.<method> aan zodat een expression tree gebouwd wordt).

Dat hangt af van de initiele source van de query:
var q = from x in source.Col
select x;

is dit een query die in memory wordt uitgevoerd of een query die een expression tree oplevert? Dat hangt af van source.Col. Implementeert die IQueryable<T> dan krijg je een expression tree. Implementeert source.Col alleen IEnumerable<T> dan krijg je een serie delegate calls.
Ik maak regelmatig mee dat technieken voor de verkeerde doeleinde worden gebruikt. Meestal omdat men niet voldoende bekend is met de werking van de techniek. Eenzelfde probleem zag je 6 maanden geleden ook met de ajax technieken. Je moet een techniek niet gebruiken omdat het kan, maar omdat het een toegevoegde waarde is.

Maar daar zit eigenlijk ook meteen de knauw. Lezen over een techniek zorgt in elk geval dat je theoretische kennis over de techniek op orde, maar om een techniek echt op waarde te kunnen schatten (en daarmee dus de voor- en nadelen) zul je het moeten toepassen in proof-of-concept projecten en er de gekste dingen mee proberen.
Je punt mbt deze thread ontgaat me.
Maar ik ben eigenlijk best wel benieuwd welke design flaws je ziet in Linq (To Sql).
- Deferred execution is een major flaw:
var q = (from c in ctx.Customers select c).Count();
vs.
var q = from c in ctx.Customers select c;

de 1e wordt METEEN uitgevoerd. De 2e niet, pas wanneer deze geenumerate wordt. De query is niet een definitie maar een definitie EN een resultset in een. Die concerns hadden echt gescheiden moeten worden.

- var q = ...
IList results = q.ToList();

dit maakt een copy van de resultset in een NIEUWE collection, omdat volgens MS' design de query geexecuteerd wordt tijdens enumeration (indien niet een scalar).

- extension methods werken op sequences, dat zijn sets met een order, maar SQL werkt alleen met sets, er is geen order. Dit levert dingen op die niet kunnen, zoals .Last, .Reverse etc.

- De syntax van linq is kreupel. Er is geen manier om deze query te formuleren:
SELECT * FROM TableA a LEFT JOIN TableB b ON a.PK = b.FK AND b.SomeField > 10
WHERE b.PK IS NULL
Dat komt omdat de join syntax hardcoded een equi-join is en de predicates vaststaan. Je kunt de predicate b.SomeField > 10 niet in de ON clause plaatsen van de join. Die MOET in de where, maar dat levert niet altijd de juiste resultset op. Sowieso moet je bij joins waar je een custom predicate wilt gebruiken terugvallen op cross-joins (nested from clauses in een from in een linq query) en die zijn veelal tergend traag.

- Linq to Sql: hierarchische fetches (eager loading en sets in projections) zijn slecht geprogrammeerd, waardoor de fetches erg traag zijn doordat per parent per node een query wordt gebruikt. Dit geldt in mindere mate voor het entity framework waar het ook niet goed is gedaan: eager loading setup is middels strings (tja lekker onderhoudbaar) en gebruikt joins (waardoor het snel uit de hand loopt bij meerdere 1:n relaties).

- Veel dingen moet je krom opschrijven. Een left join bv is een aanroep van DefaultIfEmpty(). Als je het niet weet kom je er niet op. Een IN query doe je achterstevoeren: <list of values>.Contains(field)

- Veel dingen in de Linq syntax zijn eigenlijk bedoeld voor linq to objects. In feite is het queryen van databases middels linq en expression trees een 'afterthought' zo lijkt het: nullability is niet goed geimplementeerd (met 'lift' maar dan ook met 2 parameters... :/) en het is onduidelijk waar welke expression elements in de tree te verwachten zijn. Het vertalen van expression trees is niet triviaal mede hierdoor.

Ik kan nog wel even doorgaan, maar zoveel zin heeft het nu ook weer niet ;). Na 9 maanden full time aan een linq provider te hebben gewerkt heb ik de donkere krochten wel gezien van linq. Het is leuk, mits je snapt wat de syntax is en hoe je alle constructs moet gebruiken. Het idee dat men nu met SQL kennis wel even linq queries gaat schrijven is echt onzin, de learning curve is nl. behoorlijk stijl, al zou men dat niet zeggen.

[ Voor 3% gewijzigd door EfBe op 29-03-2008 18:52 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

  • Henrikop
  • Registratie: Februari 2008
  • Laatst online: 21-08 13:33
Mooi artikel sig69 en mooie discussie EfBe. Is leerzaam.
Pagina: 1