Es ist keine schlechte Praxis für einen Controller, ein Repository direkt aufzurufen. Ein "Service" ist nur ein weiteres Tool. Verwenden Sie es also dort, wo es Sinn macht.
NikolaiDante kommentierte:
... Wählen Sie das richtige Muster für die richtige Anwendung. Was ich sagen würde, ist, dass Sie Ihre Bewerbung konsistent machen sollten.
Ich denke nicht, dass Beständigkeit der wichtigste Aspekt ist. Eine "Service" -Klasse soll eine Logik höherer Ebene kapseln, damit der Controller sie nicht implementieren muss. Wenn für eine bestimmte Operation keine "übergeordnete Logik" erforderlich ist, gehen Sie direkt zum Repository.
Um eine gute Trennung von Bedenken und Testbarkeit zu fördern, sollte das Repository eine Abhängigkeit sein, die Sie über einen Konstruktor in den Service einfügen:
IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);
service.DoSomething(...);
Wenn die Suche nach Datensätzen in der Datenbank eine Art parametrisierte Abfrage erfordert, ist eine Serviceklasse möglicherweise ein guter Ort, um Ihr Ansichtsmodell einzusehen und eine Abfrage zu erstellen, die dann vom Repository ausgeführt wird.
Wenn Sie über ein komplexes Ansichtsmodell für ein Formular verfügen, kann eine Serviceklasse die Logik des Erstellens, Aktualisierens und Löschens von Datensätzen einschließen, indem Methoden für Ihre Domänenmodelle / -entitäten aufgerufen und diese anschließend mithilfe eines Repositorys beibehalten werden.
In die entgegengesetzte Richtung gehen: Wenn Ihr Controller einen Datensatz anhand seiner ID abrufen muss, ist das Delegieren an ein Dienstobjekt wie das Schlagen eines Reißzwecks mit einem Vorschlaghammer - es ist weit mehr als erforderlich.
Ich habe festgestellt, dass der Controller in der besten Position ist, um die Transaktion oder ein Unit Of Work-Objekt abzuwickeln . Der Controller oder das Unit Of Work-Objekt wird dann für komplexe Vorgänge an Serviceobjekte delegiert oder für einfache Vorgänge (z. B. das Suchen eines Datensatzes anhand der ID) direkt an das Repository gesendet.
public class ShoppingCartsController : Controller
{
[HttpPost]
public ActionResult Edit(int id, ShoppingCartForm model)
{
// Controller initiates a database session and transaction
using (IStoreContext store = new StoreContext())
{
// Controller goes directly to a repository to find a record by Id
ShoppingCart cart = store.ShoppingCarts.Find(id);
// Controller creates the service, and passes the repository and/or
// the current transaction
ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);
if (cart == null)
return HttpNotFound();
if (ModelState.IsValid)
{
// Controller delegates to a service object to manipulate the
// Domain Model (ShoppingCart)
service.UpdateShoppingCart(model, cart);
// Controller decides to commit changes
store.SaveChanges();
return RedirectToAction("Index", "Home");
}
else
{
return View(model);
}
}
}
}
Ich halte eine Mischung aus Services und direkter Arbeit mit Repositories für absolut akzeptabel. Sie können die Transaktion auch in ein Unit Of Work-Objekt einkapseln, wenn Sie dies für erforderlich halten.
Die Aufteilung der Zuständigkeiten sieht folgendermaßen aus:
- Die Steuerung steuert den Fluss der Anwendung
- Gibt "404 Not Found" zurück, wenn sich der Warenkorb nicht in der Datenbank befindet
- Rendert das Formular mit Überprüfungsmeldungen erneut, wenn die Überprüfung fehlschlägt
- Speichert den Warenkorb, wenn alles ausgecheckt ist
- Der Controller delegiert an eine Serviceklasse, um Geschäftslogik auf Ihren Domänenmodellen (oder Entitäten) auszuführen. Serviceobjekte sollten keine Geschäftslogik implementieren ! Sie führen Geschäftslogik aus.
- Controller können für einfache Vorgänge direkt an Repositories delegieren
- Serviceobjekte nehmen Daten in das Ansichtsmodell auf und delegieren sie an Domänenmodelle, um die Geschäftslogik auszuführen (z. B. ruft das Serviceobjekt Methoden in den Domänenmodellen auf, bevor Methoden im Repository aufgerufen werden).
- Serviceobjekte werden für die Datenpersistenz an Repositorys delegiert
- Controller sollten entweder:
- Verwalten Sie die Lebensdauer einer Transaktion oder
- Erstellen Sie ein Unit Of Work-Objekt, um die Lebensdauer einer Transaktion zu verwalten