Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Entity framework] Context global vs parameter vs attach

Pagina: 1
Acties:

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 19-10 08:18
Beste mensen,

Toch maar een keer met EF aan de slag gegaan na al een x andere ORM's geprobeerd te hebben.

Echter zit ik in dubio.

Veel ORM's hebben een 'context'-achtig object. maar hoe ga je er nu het beste mee om?

Ik heb voor EF nu 3 manieren gevonden:

1: maak dat ding static ergens in een helper class, zodat je er altijd bij kan.
2: geef hem mee aan de functies die hem nodig hebben
3: gebruik elke keer eigen context voor wijzigingen, maar doe dit soort dingen:
C#:
1
2
3
4
5
6
7
8
var owner = (from o in context.Owners
            select o).FirstOrDefault(o => o.ID == oId);
var child = (from o in context.Children
            select o).FirstOrDefault(c => c.ID == cId);

owner.Children.Add(child);
context.Attach(owner);
context.SaveChanges();


Persoonlijk vind ik het netter om mijn context netjes met een using block binnen een methode te maken, maar omdat je soms een eerder opgehaald object wil koppelen met een object wat je nu gebruikt hebben die dan vaak niet dezelfde context.

Optie 1, 2 en 3 bieden allemaal een oplossing, maar ik ben op zoek naar de meest praktische. (zonder echt ranzig te doen)

Ik ken andere ORM's die bijv vaak hun contextObject als parameter meegeven. Dit is praktisch hetzelfde als 'global' maken, maar je heb het nadeel dat al je methoden extra parameters krijgen.

Optie drie ben ik een beetje bang dat dit nog wel werkt met simpele objecten, maar dat het een hel wordt als het wat complexer wordt.
Klopt dat?

Wat doen jullie?

This message was sent on 100% recyclable electrons.


  • Alex)
  • Registratie: Juni 2003
  • Laatst online: 18-11 20:57
Static maken moet je sowieso niet doen. EF's ObjectContext is niet thread safe waardoor er niet kan worden gegarandeerd dat alle objecten goed worden opgeslagen en opgehaald wanneer je vanuit meerdere threads ermee aan de haal gaat.

Een ObjectContext is een implementatie van het Unit of Work-pattern. Alle wijzigingen die je doet binnen die UOW zullen als één transactie worden opgeslagen in de database (of juist niet). Het kan in mijn ogen geen kwaad om een ObjectContext mee te geven als parameter aan een andere method, zolang die method iets doet met de doorgegeven ObjectContext.

We are shaping the future


  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 19-10 08:18
Kijk de context maakt een transaction. Klinkt logisch om hem dan niet static te maken.

Meegeven is een optie. Echter heb ik begrepen dat je transacties niet te lang open moet laten staan.
hoe gaat entity framework om met bijv. het volgende scenario:

Ik heb een object dat ik wil opslaan, laten we zeggen een order, en ik moet hierbij een eigenaar opgeven.
Dus nadat ik wat spulletjes bij elkaar geklikt heb zoek ik in een schermpje een persoon. Klik rustig het schermpje dicht, kijk nog wat rond en druk dan op de bestelknop.

De context voor het ophalen van de persoon wil je eigenlijk hetzelfde hebben als die van je order. Omdat je anders ze niet aan elkaar kan linken (objecten opgehaald in context1 kun je niet koppelen aan objecten uit context2)
Dan kom je dus al heel snel uit op de dingen uit mijn topicstart of voor het voorbeeld van nu:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
private void SaveOrder(int klantId) 
{
    using (var db = new ShopContext())
    {
        var klant = db.Klanten.Where(x => x.id = klantId).Single(); //throws
        var order = new Order() { klant = klant };
        db.Orders.Add(order);

        ....

        db.SaveChanges();
    }
}

This message was sent on 100% recyclable electrons.


  • Alex)
  • Registratie: Juni 2003
  • Laatst online: 18-11 20:57
Als je een klant hebt opgehaald in ObjectContext1 en je wilt deze gebruiken in ObjectContext2, dan zul je deze moeten Attach'en aan de tweede ObjectContext. Daarna zou het - in theorie tenminste - moeten werken.

C#:
1
2
3
4
db.Klanten.Attach(klant);

var order = new Order() { klant = klant };
db.Orders.Add(order);


Een alternatief is dat je in je Order-class een property toevoegt voor de primary key van je klant, dat je het [ForeignKey] attribuut gebruikt en dat je bij het opslaan van de order de key instelt op de ID die je eerder al had.

We are shaping the future


  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 19-10 08:18
Alex) schreef op zaterdag 27 september 2014 @ 23:28:
Als je een klant hebt opgehaald in ObjectContext1 en je wilt deze gebruiken in ObjectContext2, dan zul je deze moeten Attach'en aan de tweede ObjectContext. Daarna zou het - in theorie tenminste - moeten werken.

C#:
1
2
3
4
db.Klanten.Attach(klant);

var order = new Order() { klant = klant };
db.Orders.Add(order);


Een alternatief is dat je in je Order-class een property toevoegt voor de primary key van je klant, dat je het [ForeignKey] attribuut gebruikt en dat je bij het opslaan van de order de key instelt op de ID die je eerder al had.
Ja met attach heb ik eerder iets werkend kunnen krijgen, maar momenteel heb ik een stukje waarbij het niet werkt.. (met dezelfde foutmelding, dus echt verder kom je er niet mee)

Ik vind je alternatief wel het proberen waard. Dan hoef ik in ieder geval niet nog een keer naar me database om het object op te halen.

edit:
nadeel van ForeignKey is dat ik opeens weer een keyname in moet gaan vullen. Die heb ik niet want ik werk code-first. (bovendien hou ik niet van literals in me code) :(


edit2:
misschien gerelateerd bijvraagje:

Als ik een order heb van een klant defineer ik dat dan vanuit klant of vanuit order?
Ik kan beide voorbeelden vinden op internet. Momenteel heb ik twee kanten op, en dat werkt :S
C#:
1
2
3
4
5
6
7
8
9
10
11
12
public class Order
{
    public int Id { get; set; }
    [Required]
    public Klant Klant { get; set; }
}

public class Klant 
{
    public int Id { get; set; }
    public List<Order> Orders { get; set; }
}

maar hoort dit wel?

Het geeft trouwens ook direct de reden aan waarom ik niet gewoon kan zeggen:
C#:
1
Order.KlantId = Klant.Id;

Ik heb namelijk geen KlantId. Ik heb alleen een link naar het object Klant.

[ Voor 30% gewijzigd door BasieP op 27-09-2014 23:54 ]

This message was sent on 100% recyclable electrons.


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Foreign Key is de naam voor het veld of de velden in entity E die wijzen naar de PK velden van entity P. Als E order is en P is klant, dan is de foreign key in order dus een veld dat wijst naar de pk van klant.

EF werkt met of zonder FK fields. Werk je zonder FK fields, dan is de foreign key voor Klant in Order gelijk aan Order.Klant.Id. Werk je met FK fields, dan heeft Order een field 'KlantId'.

Je vraag 'Hoort dit wel' laat doorschemeren dat je niet helemaal doorhebt wat relationships zijn in een entity model.

Als Order en Klant een relatie hebben, en in jouw en vele andere domains met order en klant is dat een m:1 (dus een klant kan meerdere orders hebben, een order hoort maar bij 1 klant), dan is de FK side 'Order' en de PK side 'Klant'.

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


  • Megamind
  • Registratie: Augustus 2002
  • Laatst online: 10-09 22:45
Het is inderdaad verstandig om zoiets te doen:

C#:
1
2
3
4
5
6
7
8
9
10
11
    public class Payment
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid PaymentId { get; set; }

        [Required]
        public Guid OrderId { get; set; }

        [ForeignKey("OrderId")]
        public Order Order { get; set; }
    }


Dit zorgt ervoor dat je een Payment object kan inserten zonder dat je het daadwerkelijke Order object eraan hoeft te hangen maar je ook de FK waarde kan gebruiken.
Pagina: 1