Laden...

Dependency Injected DbContext in Statischer Klasse für Hilfsmethoden nutzen legitim?

Erstellt von monsee vor 5 Jahren Letzter Beitrag vor 5 Jahren 3.237 Views
M
monsee Themenstarter:in
30 Beiträge seit 2018
vor 5 Jahren
Dependency Injected DbContext in Statischer Klasse für Hilfsmethoden nutzen legitim?

Morgen zusammen,

ist es eigentlich legitim und praxisnah, von einem Controller bzw. einer PageModel Klasse die injizierte DbContext in eine statische Methode einer statischen Klasse als Parameter mitzugeben?

Also z.B.: public static async Task<Auto> GetAutoById(int id, ApplicationDbContext _context)
...

Zweck ist, dass ich eine Datenbankabfrage mehrfach verwenden will, es aber aus meiner Sicht keinen Sinn macht jedes mal eine Instanz meiner Helper-Method zu machen und erst recht nicht die Methode jedesmal im jeweiligen PageModel neuzuschreiben, weswegen ich die alle statisch halte.

In meiner PageModel-Klasse sorgt dies sichtbar für mehr Überblick.

Noch ein Hinweis, das sind nur Hilfsmethoden. Die CRUD-Operations werden natürlich in den jeweiligen PageModels durchgeführt.

Oder baue ich mir damit Hürden auf?

monsee

16.806 Beiträge seit 2008
vor 5 Jahren

Eine statische Klasse ist so nicht testbar, geschweige denn die Methode.
Dadurch nein, so absolut nicht legitim.

M
monsee Themenstarter:in
30 Beiträge seit 2018
vor 5 Jahren

Hallo Abt,

also die statische Klasse dann in eine nicht statische umwandeln und die DbContext via DI injizieren und dann Instanzen in der PageModel Klasse davon nutzen oder wie macht man das sauber?

Gruß, monsee

1.029 Beiträge seit 2010
vor 5 Jahren

Hi,

ich denke eher, dass der Ansatz für eine derart simple Methode (wenn man dem Namen der Methode glauben darf) eine Hilfsklasse zu benötigen schon quer liegt.

Ich muss zugeben, dass ich nur sehr selten EF verwende und wenn dann auch nur für Quick&Dirty-Aufgaben - in wichtigeren Anwendungen verwende ich Dapper in Verbindung mit dem RepositoryPattern (wobei ich nicht sicher bin ob dieses für EF so intelligent ist, da EF selbst ja schon was ähnliches bietet) - das Thema ist - du willst offensichtlich eine Entity per Id aus der DB holen - eine Standardaufgabe für ein Repository, womit diese Funktion eben in's Repository - und nicht in eine Hilfsklasse gehört.

Wenn ich dort an EF denke - sind DbSets das, was einem Repository am nächsten kommt - und mir fällt adhoc ehrlich gesagt kein Grund ein vll ein eigenes bzw. abgeleitetes DbSet zu verwenden, dass genau eine solche Methode bereitstellt.

LG

PS: Abseits davon überhaupt eine solche Hilfsklasse zu brauchen - halte ich es für ziemlich ungeschickt eine solche auch noch static zu haben - wenn du das für mehr als ein Model machst - hast du ruckzuck soviele Helper wie Entitäten - alle statisch und nicht zu testen...

16.806 Beiträge seit 2008
vor 5 Jahren
public static async Task<Auto> GetAutoById(int id, ApplicationDbContext _context)

(1) die Methode sollte an dieser Stelle GetAutoByIdAsync heissen, um Konform mit dem Framework zu sein){gray}
2) diese Klasse ist ein typischer Fall für den Repository Pattern, wobei der DbContext den UnitOfWork darstellt

Sehe keine Notwendigkeit einer Hilfsklasse.

2.078 Beiträge seit 2012
vor 5 Jahren

Ohne der abschließenden Aussage bezüglich dem Repository widersprechen zu wollen:

Eine statische Klasse ist so nicht testbar, geschweige denn die Methode.

Für mich sieht die Methode so aus, als bräuchte sie keinen Zustand. Damit ist sie durchaus testbar, solange man ApplicationDbContext mocken kann.

Wenn ich unbedingt eine Quick&Dirty-Lösung schreiben will, dann würde ich das aber als Extension-Method für die DbSets bauen. Ist im Prinzip nichts anderes als eine statische Methode, nur die Nutzung sieht wie ein Instanz-Zugriff aus. Und weil das so ist, kann man das leichter durch eine saubere Repository-Lösung ersetzen, da die Aufrufe sich nicht ändern würden - zumindest solange die Signatur gleich bleibt.

Bei EF ist diese konkrete Methode aber gar nicht notwendig, denn EF bietet dafür die Find-Methode an. Bei EF6 nur am DbSet, bei EFCore findet man die sowohl am DbContext als auch am DbSet.

16.806 Beiträge seit 2008
vor 5 Jahren

Damit ist sie durchaus testbar, solange man ApplicationDbContext mocken kann.

Naja, aber Du kennst ja den DbContext von EF - der ist so nicht mockbar; nur über Shims.

M
monsee Themenstarter:in
30 Beiträge seit 2018
vor 5 Jahren

8o

Bitte helft mir als Hobbyprogrammierer, der sich grade das Repository Pattern einverleibt.
Was ist denn jetzt Mock schon wieder?

monsee

4.931 Beiträge seit 2008
vor 5 Jahren

s. Mock-Objekt

@Abt: Aber auch eine nicht-statische Methode wäre dann nicht testbar bzw. mock-fähig (solange der DbContext übergeben bzw. benutzt wird).
Es hat mit der Testbarkeit nichts zu tun, ob eine Klasse oder Methode selber statisch ist oder nicht - es kommt drauf an, ob die Methode intern andere Methoden aufruft, welche nicht austauschbar sind.
Wenn also eine nicht-statische Methode z.B. Console.WriteLine oder Math.Sin oder eine andere statische Methode aufruft, dann läßt sich diese nicht mocken. Aber dasselbe gilt genauso für Aufrufe innerhalb von nicht-statischen Methoden, solange man Objekte von konkreten Klassen (und Strukturen) benutzt! Erst bei der konsequenten Benutzung von Schnittstellen (interface), d.h. üblicherweise per DI übergeben, lassen sich Methoden soweit mocken, daß sie unabhängig von externen Klassen sind.

Beispiel:


interface IX
{
  int Value { get }; 
}

static int Add(IX x, IX y)
{
  return x.Value + y.Value;
}

Diese Methode ist nun test- und mock-bar, egal ob static oder nicht.

16.806 Beiträge seit 2008
vor 5 Jahren

Deswegen gibt es nicht nur Mock-Objekte, sondern auch Shim-Objekte.

Gut, wenn Du es jetzt auf static beziehst; ich hab meine Aussage dahingehend auch vereinfacht ausgedrückt.
Es macht ja keinen Sinn in diesem Kontext nun auf die Feinheiten einzugehen.

Statische Methoden (im überwiegenden Teil) sind nicht testbar, weil nicht austauschbar.
Dass dies natürlich für gewisse Pattern nicht gilt; macht jetzt den Braten nicht fett.

4.931 Beiträge seit 2008
vor 5 Jahren

Aber nur einfach zu schreiben (ist ja nicht das erste mal von dir):

Eine statische Klasse ist so nicht testbar, geschweige denn die Methode. finde ich nicht sehr hilfreich (für die Leser).
Einfach nur das static zu löschen, reicht eben nicht aus.

Leider gibt es keine Möglichkeit (kenne ich aber auch von anderen Programmiersprachen nicht), eine Methode so zu deklarieren, daß sie keine anderen (statischen) Methoden aufruft (z.B. strict).

16.806 Beiträge seit 2008
vor 5 Jahren

Nehme ich an, dass Du dies in dieser Form kritisierst.
Ist immer ein schmaler Grat zwischen "zu viel schreiben, was die Leute oft direkt erschlägt" und eben prägnant auf den Punkt zu kommen, und dadurch evtl. pauschal zu wirken.

Ich habe jedoch nicht geschrieben, dass eine statische Klasse/Methode pauschal nicht testbar sei.
Ich habe geschrieben, dass sie so nicht testbar sei. Das ist inhaltlich ein deutlicher Unterschied.
Schreibe schließlich auch nirgends, dass static löschen reichen würde.
Dahingehend kann ich die Kritik auch so nur teilweise nachvollziehen.

4.931 Beiträge seit 2008
vor 5 Jahren

Aber das testbar hat eben nichts mit "static" zu tun, und genau so klingt aber deine Aussage.
Ich schreibe ja z.B. auch nicht, daß async oder void nicht testbar ist...

Mir ging's nur darum, klarzustellen, daß statische Methoden nicht die "Wurzel allen Übels" sind.
So empfiehlt ja auch die Code Analysis (früher FXCop) CA1822: Mark members as static.

PS: Und selbst in [Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio wird als Beispiel eine statische Methode getestet.

16.806 Beiträge seit 2008
vor 5 Jahren

Tut mir leid, aber ich will Dein Goldwaagen-Spiel nicht wirklich mitmachen.

Denn...nun klingt Deine Aussage

So empfiehlt ja auch die Code Analysis (früher FXCop)
>
.

alleinstehend, als ob man alles static deklarieren soll.
Ne, sorry - Goldwaagenspiele sind nicht meins.

Ich stehe aber weiterhin zu meiner Aussage, es ist potentiell schlimmer static unbewusst zuviel zu verwenden als umgekehrt.

4.931 Beiträge seit 2008
vor 5 Jahren

Wer legt jetzt was auf die Goldwaage?
Lies doch den Link.

OK, ich zitiere den m.E. wichtigen Satz:

Cause

A member that does not access instance data is not marked as static.

Und gerade Hilfsmethoden (die nur auf den übergebenden Parametern beruhen), erfüllen dies (und sind genauso testbar oder untestbar wie nicht-statische Methoden auch).

Aber da du anscheinend es nicht einsehen willst, werde ich wohl weiter in diesem Forum von dir den (oder ähnlichen) Satz lesen (müssen):

Statische Klassen oder Methoden sind (so) nicht testbar.

16.806 Beiträge seit 2008
vor 5 Jahren

Richtig, sofern es auf die jeweilige Klasse zutrifft, werde ich das weiterhin so formulieren. Denn

Ich stehe aber weiterhin zu meiner Aussage, es ist potentiell schlimmer static unbewusst zuviel zu verwenden als umgekehrt.

Dass C# darüber hinaus über Sprachfeatures verfügt, die static sein müssen und dabei trotzdem testbar sind - sofern korrekt umgesetzt - ist sogar mir bewusst.

M
monsee Themenstarter:in
30 Beiträge seit 2018
vor 5 Jahren

Guten Morgen zusammen,

ich krame mal meinen alten Thread heraus, in dem es um den möglichen Einsatz des UnitOfWork-Pattern geht.

Ich habe mir einige Lernvideos dazu angesehen und in mein Projekt das Pattern eingebunden.

Die Videos, die ich mir angesehen habe, zeigen aber alle auf, wie ich mit der UoW Daten aus der Datenbank abhole. Während die CUD-Methoden dann zum Beispiel in einem ASP.NET-Projekt in den jeweiligen Controller (MVC) bzw. EventHandler-Methoden (Razor Pages) über die Unit Of Work durchgeführt werden.

Verfehlt das nicht irgendwie denn Sinn?

Wenn ich einen Datensatz erstellen will, dann könnte ich die Methode dafür doch auch im Repository der UoW unterbringen und in der EH-Methode dann sowas wie
_unitOfWork.Autos.CreateAuto(auto) aufrufen, anstatt den ganzen Salmon in die Code-Behind zu schreiben - oder?

Gruß, monsee

16.806 Beiträge seit 2008
vor 5 Jahren

Kannst Du bitte die Quelle verlinken, auf die Du Dich beziehst oder wenigstens den Code dazu zeigen?
Noch nicht jeder von uns kann hellsehen 😉

M
monsee Themenstarter:in
30 Beiträge seit 2018
vor 5 Jahren

Oh, hallo ABT, dass wird etwas Schwierig mit dem Link.
Das ist ein Udemy-Kurs.

Da geht es um die Einführung ins EF-Framework.

Der Autor zeigt da zwar die vollständige Einrichtung, aber geht überwiegend nur auf die Abfrage von Daten ein, nicht aber, ob die UoW auch logischerweise die Methoden für CUD enthalten sollten.

Gruß, monsee

16.806 Beiträge seit 2008
vor 5 Jahren

In EF ist der DbContext bereits der UoW Context.
Und das Repository bekommt den DbContext injiziert.


.. class UserRepository : BaseRepository, IUserRepository...


public async Task<UserEntity> AddAsync(string userName)
{
 ..... 

   UserEntity user ...
   await _dbContext.Users.AddAsync(user);

   return user;
}

Es gibt manchmal Situationen, da lohnt es sich den DbContext erneut zu abstrahieren - aber nicht immer.

Der DbContext - egal wie - gehört aber weder in die Action, noch in Razor Pages.
Das wird gern in einfachen Samples gemacht - ist aber eine starke Verletzung der [Artikel] Drei-Schichten-Architektur

Razor Pages und Controller/Action gelten als UI-Schicht.
Weder DbContext noch Repositories haben daher irgendwas in Razor Pages oder Actions zu suchen.
Da hat nur die Business Logik zu sein, die aufgerufen wird.

Ein Beispiel dazu siehst Du in einem GitHub Repository von mir, das ich für Talks in UserGroups und Konferenzen verwende/verwendet hab:
https://github.com/BenjaminAbt/2018-Talks-ModernApiDevelopment

Controller kennen hier nur die Business Schicht (hier Event-driven via MediatR) und in der Business Schicht (hier eben EventHandler) wird dann auf ein Repository zugegriffen.
Nur so kann Business Logik über Anwendungen/Projekte hinweg geteilt werden. Das klappt nicht, wenn man Business Logik Plain in die UI schubst - da gehört sie nicht hin.