Laden...

Wie kann ich ein zusätzliches Property mit Linq-Join beim SaveChanges() wieder ignorieren?

Erstellt von m.grauber vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.706 Views
M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 3 Jahren
Wie kann ich ein zusätzliches Property mit Linq-Join beim SaveChanges() wieder ignorieren?

Hallo!

Ich nutze VS 2017, C# und den SQL-Server.

Hier ein vereinfachtes Beispiel (was natürlich nicht sehr sinnvoll ist):

Eine SQL-Server Tabelle "Einladung" enthält folgende Spalten:
EinladungID
KundeID
Hinweis

Eine zweite Tabelle "Kunden" enthält folgende Spalten:
KundeID
Name

1.) Select: Alle Kunden zur Einladung Nr. 100 heraussuchen:


var i = (from e in dataEntities.Einladung
                         where e.EinladungID==100
                         select o).ToObservableCollection<Einladung>(...

-> Das klappt. Man erhält eine ObservableCollection mit den Spalten EinladungID, KundeID und Hinweis und ich kann auch Änderungen mit SaveChanges() speichern.

2.) Nun erweitere ich im C#-Code die partielle Klasse "Einladung"
(public partial class Einladung)
um ein Feld "Name" (public string Name { get; set; }), welches nicht in der SQL-Tabelle "Einladung" existiert, aber den Benutzer in der Observable Collection angezeigt werden soll. Damit würde er auch immer den aktuellen Namen aus der Tabelle "Kunden" sehen, auch wenn der sich einmal ändert.

Ohne Probleme habe ich durch diese Erweiterung ein zusätzliches Feld "Name", das ich auch beliebig beschreiben und ändern kann und welches gewünschterweise bei SaveChanges() natürlich nicht in die Tabelle "Einladung" zurückgeschrieben wird. Alle anderen Änderungen an der Tabelle "Einladung" werden aber mit SaveChanges() korrekt in den SQL-Server zurückgeschrieben.

--> Frage. Wie kann ich aber per Linq-Join dieses Feld "Name" zusätzlich auslesen, so dass es bei SaveChanges() ignoriert wird. Das hier klappt leider nicht:


var i = (from e in dataEntities.Einladung
                         where e.EinladungID==100
                         join a in dataEntities.Kunden on e.KundeID equals a.KundeID
                         select e, a.Name).ToObservableCollection<Einladung>(...

  • Es sollte so ausgelesen werden, dass alle Änerungen per SaveChanges() in die Tabelle "Einladung" gespeichert werden können, ohne das das Feld "Name" berücksichtigt wird.

  • Ich möchte ungern einen zweiten Select auf die Kundentabelle ausführen und dann per foreach alle Datensätze in der Observable Collection einzeln durchgehen müssen.

  • Ein T-SQL-Befehl würde diesen Join zwar machen, jedoch kann ich dann nicht mehr mit SaveChanges() Änderungen zurückspeichern.

Folgenden Lösungsvorschlag habe ich entdeckt:

https://stackoverflow.com/questions/23848259/linq-updating-different-table-after-join-process

Umformuliert wäre das so, das klappt leider nicht:


var i = (from e in dataEntities.Einladung
                         where e.EinladungID==100
                         join a in dataEntities.Kunden on e.KundeID equals a.KundeID
                         select new { e, a }).Select(result => { result.e.EinladungID = Einladung.EinladungID; result.e.KundeID = Einladung.KundeID; result.e.Hinweis = Einladung.Hinweis; result.a.Name = Name; return result}).ToObservableCollection<Einladung>(…

Wie bekommt man so etwas hin?

Danke sehr!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

16.807 Beiträge seit 2008
vor 3 Jahren

Man erhält eine ObservableCollection mit den Spalten EinladungID, KundeID und Hinweis und ich kann auch Änderungen mit SaveChanges() speichern.

Wobei Du hier halt die Schichten mischt. Eine Entität hat in der UI eigentlich nichts zu suchen.
Einer der vielen Gründe dafür ist, dass Du nun eben die UI Problematik im DAL lösen willst - was halt unpraktisch ist.

Wie kann ich aber per Linq-Join dieses Feld "Name" zusätzlich auslesen, so dass es bei SaveChanges() ignoriert wird.

Gar nicht, da es sich hierbei nicht mehr um eine einfach gemappte Projektion handelt.
Projektionen eines Joins sind nie gemappt. Schreiben kannst Du immer nur Entitäten (oder Teile) davon, aber keine virtuellen Projektionen.

Leider schreibst Du nicht was Du genau verwendest; ich nehme an EF 6 (nicht Core), sodass es hier von Haus aus auch keine Möglichkeit gibt.
Die Grundidee von jeder Datenbank-Operation ist ja eigentlich nur das zu laden, was man wirklich laden muss und nur das zu schreiben, was sich geändert hat.

In EFCore gibt etwas Neues, das genau Deinen Anwendungsfall abdeckt und sich Query Types bzw. seit EF Core 3 Keyless Types nennt:
Man kann hier Entitäten definieren, die nur die Eigenschaften haben, die man für eine Abfrage (was auch ein Join sein kann) benötigt.
Aber: diese Entitäten kann man nur für das Lesen verwenden.

Das gleiche bei Deinem Linq-Befehl: es gibt hier keinerlei echtes Entitätsmapping, auch wenn es für Dich so aussieht.
Du kannst damit nur Daten lesen aber nicht schreiben.

Mir ist etwas unklar was Du genau für eine Umgebung hast, aber das Vorgehen skaliert nicht: Du lädst die Entitäten in den Speicher der Anwendung und gehst davon aus, dass sie einfach gespeichert werden können.
Wenn mehrere Personen parallel an gleichen (geladenen) Datensätzen arbeiten, dann würde das ganze entsprechend zu einer ConcurrencyException führen, da sich die Datensätze unterscheiden und unklar ist, welcher der gültige Datensatz ist.

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 3 Jahren

Hallo Abt!

Danke für die ausführliche Antwort! Das hilft wirklich sehr!

Ja, ich nutze noch nicht den EF Core - Sollte ich irgendwann umstellen, weil der normale EF irgendwann nicht mehr unterstützt wird? Wann wird das vermutlich sein? - Microsoft behält sich da sehr bedeckt.

https://docs.microsoft.com/de-de/ef/efcore-and-ef6/porting/

Hier schreibt Microsoft:

"...Aufgrund der wesentlichen Änderungen in EF Core wird nicht empfohlen, eine EF 6-Anwendung auf EF Core umzustellen – es sei denn, es gibt einen zwingenden Grund für eine solche Änderung. Sie sollten den Wechsel von EF 6 nach EF Core weniger als Upgrade, sondern als eine Portierung betrachten..."

Könnte es sein, dass das normale EF ab einem bestimmten VS nicht mehr genutzt werden kann? (z. B. VS 2021, 2023, 2025, 2027 etc.?)

Nun nochmals zum Problem:

Kunden.Name soll nur beim Lesen gleichzeitig aus der Tabelle Kunde ausgelesen werden (damit der Benutzer den Kundennamen im Klartext sieht, aber beim folgenden Speichern natürlich nicht zurückgeschrieben werden.

Ich dachte, vielleicht kann man im Linq mit "select new {…." alle Felder angeben und dann das Ergebnis in eine Entität vom Typ "Einladung" vornehmen. - Und in der partial class Einladung ist ja dieses (public string Name { get; set; } definiert, in das der Kundenname beim Linq-Befehl übernommen werden kann.

Wenn es so nicht geht, habe ich vermutlich den Post in Stack Overflow falsch verstanden? (letzter Eintrag von Sean, ganz unten)

Ein Locking ist in diesem Programmteil nicht notwendig.

Wenn ich nach dem Linq auf die Tabelle "Einladung" das Feld "Name" manuell beschreibe, klappt ja ein anschließender SaveChanges() auch korrekt und der Befehl ignoriert einfach dieses "lokale" Feld "Name".

Einen zweiten Select und eine foreach-Ersetzung würde ich gerne vermeiden.

Du bist sehr weit und hast eine enorm große Kenntnis und würdest vieles auch ganz anders realisieren. Evtl. kann man diesen Select aber dennoch in einer möglichen Form realisieren.

Danke und schöne Grüße!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

16.807 Beiträge seit 2008
vor 3 Jahren

Ja, ich nutze noch nicht den EF Core - Sollte ich irgendwann umstellen, weil der normale EF irgendwann nicht mehr unterstützt wird? Wann wird das vermutlich sein? - Microsoft behält sich da sehr bedeckt.

Eigentlich nicht.

Microsoft hat offen und von Anfang an (vor circa fünf(!) Jahren) kommuniziert, dass EF6 keinerlei Funktionalitäten mehr erhält und NICHT in .NET Core / .NET 5 kommt.
Es gibt nur noch Funktionalitäten um die Portierung auf EFCore zu erleichern.
Das wird auch in allen letzten Release Notes so erwähnt.
Microsoft kann nichts dafür, wenn Du das nicht liest 😉

EF6 ist genauso wie .NET 4.x End of Life und wird nicht mehr weiterentwickelt.
Die Zukunft ist .NET 5 und hier gibt es nur noch EF Core.

Hier schreibt Microsoft:

"...Aufgrund der wesentlichen Änderungen in EF Core wird nicht empfohlen, eine EF 6-Anwendung auf EF Core umzustellen – es sei denn, es gibt einen zwingenden Grund für eine solche Änderung. Sie sollten den Wechsel von EF 6 nach EF Core weniger als Upgrade, sondern als eine Portierung betrachten..."

Du hast gesehen, dass dieser Beitrag von Ende Oktober 2016, also fast 4 Jahre alt ist?

Könnte es sein, dass das normale EF ab einem bestimmten VS nicht mehr genutzt werden kann? (z. B. VS 2021, 2023, 2025, 2027 etc.?)

VS ist eine Entwicklungsumgebung; keine Runtime.
Du kannst auch mit Notepad Entity Framework programmieren. Der Editor hat mit der Runtime nichts, aber auch gar nichts am Hut.

Nun nochmals zum Problem:

Dein Wunsch ist nicht umsetzbar. Das kann das Mapping nicht.

Der von Dir genannte Beitrag auf Stack Overflow hat einen anderen Context.
Du arbeitest mit anonymen Projektionen; was der von Dir gezeigte Beitrag von Sean macht ist was ganz anderes: er arbeitet mit den vollständigen Entitäten.
Und genau so sollte man es eigentlich nicht machen, sondern nur das Schreiben, was sich auch geändert hat.

W
955 Beiträge seit 2010
vor 3 Jahren

Man kann die Anfrage auch einfach umdrehen: Gebe alle Kunden die die Einladung mit Id =100 bekommen haben, also irgendwas wie


kunden.Where(p => p.Einladungen.Any(q => q.Id == 100))

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 3 Jahren

Hallo Witte!

Danke, aber damit wäre nur ein Select möglich, kein zurückschreiben in die Tabelle "Einladungen".

Hallo Abt!

Danke auch Dir nochmals! Zeitlich ist es für mich sehr schwer mich neben meiner Arbeit auch mit den Änderungen etc. zu befassen. Ich hatte das schon im Hinterkopf auf dem Radar, aber mich noch niemals damit befasst. Erschwerend kommt hinzu, dass ich beim EF den Designer nutze und den "Database First"-Ansatz.

Ich weiß, es gibt einige Infos zur Umstellung auf EF Core. Aber kennst Du hierzu zufällig eine gut gemachte und verständliche Seite auf Deutsch?

Vielen Dank!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

16.807 Beiträge seit 2008
vor 3 Jahren

Zeitlich ist es für mich sehr schwer mich neben meiner Arbeit auch mit den Änderungen etc. zu befassen.

Sorry wenn das nun zu direkt sein sollte; aber letzten Endes gehört es zu den Aufgaben eines Entwicklers sich über die Änderungen seiner verwendeten Technologien zu informieren.
Muss ja nicht tagesgenau sein; aber hey: hier sind es Infos von vor 5 Jahren 😃 Wenn nicht der Entwickler, wer dann? 😉

Aber ein Tutorial auf Deutsch ist mir nicht bekannt.

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 3 Jahren

Hallo Abt!

Es ist schwierig, wenn man immer angetrieben wird, sofort neue Ergebnisse zu liefern und keine Zeit für diese Dinge einplanen kann.

Ich denke in größeren Firmen ist das besser gelöst.

Kennst Du auch keine gute Seite zur Umstellung von EF 6 auf EF Core in englisch? Vielleicht hat noch jemand einen Tipp.

Nochmals vielen Dank!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

5.657 Beiträge seit 2006
vor 3 Jahren

Nur weil eine neue Technologie deine Herangehensweise unterstützt, heißt das nicht, daß man es auch so machen sollte. Eine Eigenschaft, die dem Benutzer angezeigt werden soll und nicht in der DB gespeichert wird, gehört nicht ins Datenmodell sondern ins ViewModel, ebenso ObservableCollections. Siehe dazu:

[Artikel] Drei-Schichten-Architektur
[Artikel] MVVM und DataBinding

Und wenn man mit EF einen Datensatz ändern will, dann liest man zuerst den aktuellen Datensatz aus, mappt die geänderten Eigenschaften (z.B. mit AutoMapper) und schreibt dann diesen geänderten Datensatz wieder zurück. So kann EF die Änderungen tracken und automatisch aktualisieren.

Weeks of programming can save you hours of planning

16.807 Beiträge seit 2008
vor 3 Jahren

Es ist schwierig, wenn man immer angetrieben wird, sofort neue Ergebnisse zu liefern und keine Zeit für diese Dinge einplanen kann.

Ich denke in größeren Firmen ist das besser gelöst.

Ich persönlich halte das für eine Ausrede. Wie willst Du mit dieser Art gute Lösungen schaffen?

Gerade in kleineren Firmen ist es wichtig nachhaltige Lösungen zu schaffen. Größere Firmen haben oft einen längeren Atem; sind daher aber oft auch langsamer.
Oft gefährdest Du daher eher das kleine Unternehmen als das große mit so einem Vorgehen.

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 3 Jahren

Hallo Abt und MrSparkle!

Ich bin jetzt aus dem Urlaub zurück; Gesund.

Danke für die Antworten und den Link. Ich werde es beherzigen und mir nun Planungszeit für die Umsetzung in den Kalender eintragen.

Ich wünsche eine vielversprechende Woche!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

3.170 Beiträge seit 2006
vor 3 Jahren

Hallo zusammen,

nur der Vollständigkeit halber, EF unterstützt seit Version 6.3.0 .NET Standard 2.1 und ist damit auch in .NET Core lauffähig.
Das steht auch so in den von Abt verlinkten Release Notes:

Support for .NET Core 3.0
- The EntityFramework package now targets .NET Standard 2.1 in addition to .NET Framework 4.x.
- This means that EF 6.3 is cross-platform and supported on other operating systems besides Windows, like Linux and macOS.
- The migrations commands have been rewritten to execute out of process and work with SDK-style projects.

Weiterentwickelt wird es aber trotzdem nicht mehr, und für Neuentwicklungen ist EF Core daher wärmstens zu empfehlen.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca