Separazione dell'accesso ai dati in ASP.NET MVC

34

Voglio assicurarmi di seguire gli standard e le best practice del settore con il mio primo vero crack su MVC. In questo caso, è ASP.NET MVC, utilizzando C #.

Userò Entity Framework 4.1 per il mio modello, con oggetti code-first (il database esiste già), quindi ci sarà un oggetto DBContext per il recupero dei dati dal database.

Nelle demo che ho visitato sul sito Web asp.net, i controller dispongono di un codice di accesso ai dati. Questo non mi sembra giusto, specialmente quando si seguono le pratiche DRY (non ripeterle).

Ad esempio, diciamo che sto scrivendo un'applicazione web da utilizzare in una biblioteca pubblica, e ho un controller per creare, aggiornare ed eliminare libri in un catalogo.

Diverse azioni possono richiedere un codice ISBN e richiedere di restituire un oggetto "Libro" (nota che probabilmente non è un codice valido al 100%):

public class BookController : Controller
{
    LibraryDBContext _db = new LibraryDBContext();

    public ActionResult Details(String ISBNtoGet)
    {
        Book currentBook = _db.Books.Single(b => b.ISBN == ISBNtoGet);
        return View(currentBook);
    }

    public ActionResult Edit(String ISBNtoGet)
    {
        Book currentBook = _db.Books.Single(b => b.ISBN == ISBNtoGet);
        return View(currentBook);
    }
}

Invece, dovrebbe in realtà ho un metodo nel mio oggetto contesto db per restituire un libro? Sembra una separazione migliore per me, e aiuta a promuovere DRY, perché potrei aver bisogno di ottenere un oggetto Book per ISBN da qualche altra parte nella mia applicazione web.

public partial class LibraryDBContext: DBContext
{
    public Book GetBookByISBN(String ISBNtoGet)
    {
        return Books.Single(b => b.ISBN == ISBNtoGet);
    }
}

public class BookController : Controller
{
    LibraryDBContext _db = new LibraryDBContext();

    public ActionResult Details(String ISBNtoGet)
    {
        return View(_db.GetBookByISBN(ISBNtoGet));
    }

    public ActionResult Edit(ByVal ISBNtoGet as String)
    {
        return View(_db.GetBookByISBN(ISBNtoGet));
    }
}

È un insieme valido di regole da seguire nella codifica della mia applicazione?

Oppure, immagino che una domanda più soggettiva sarebbe: "è questo il modo giusto per farlo?"

    
posta scott.korin 18.02.2012 - 16:34
fonte

2 risposte

54

In generale, vuoi che i tuoi Controllori facciano solo alcune cose:

  1. Gestire la richiesta in arrivo
  2. Delega l'elaborazione a un oggetto business
  3. Passa il risultato dell'elaborazione aziendale alla visualizzazione appropriata per il rendering

Non dovrebbero esserci nessun accesso ai dati o logica aziendale complessa nel controller.

[Nella più semplice delle app, è probabile che tu possa fare a meno delle azioni CRUD di dati di base nel tuo controller, ma una volta che inizi ad aggiungere più di semplici chiamate Get e Update, vorrai suddividere l'elaborazione in un classe separata.]

I controllori di solito dipendono da un "Servizio" per eseguire il lavoro di elaborazione effettivo. Nella tua classe di servizio potresti lavorare direttamente con la tua origine dati (nel tuo caso, il DbContext), ma ancora una volta, se ti trovi a scrivere molte regole aziendali oltre all'accesso ai dati, probabilmente vorrà separare la tua logica di business dal tuo accesso ai dati.

A quel punto, probabilmente avrai una classe che non fa altro che accedere ai dati. A volte questo è chiamato un repository, ma non importa quale sia il nome. Il punto è che tutto il codice per ottenere dati dentro e fuori dal database è in un unico posto.

Per ogni progetto MVC su cui ho lavorato, ho sempre trovato una struttura come:

Regolatore

public class BookController : Controller
{
    ILibraryService _libraryService;

    public BookController(ILibraryService libraryService)
    {
        _libraryService = libraryService;
    }

    public ActionResult Details(String isbn)
    {
        Book currentBook = _libraryService.RetrieveBookByISBN(isbn);
        return View(ConvertToBookViewModel(currentBook));
    }

    public ActionResult DoSomethingComplexWithBook(ComplexBookActionRequest request)
    {
        var responseViewModel = _libraryService.ProcessTheComplexStuff(request);
        return View(responseViewModel);
    }
}

Servizio aziendale

public class LibraryService : ILibraryService
{
     IBookRepository _bookRepository;
     ICustomerRepository _customerRepository;

     public LibraryService(IBookRepository bookRepository, 
                           ICustomerRepository _customerRepository )
     {
          _bookRepository = bookRepository;
          _customerRepository = customerRepository;
     }

     public Book RetrieveBookByISBN(string isbn)
     {
          return _bookRepository.GetBookByISBN(isbn);
     }

     public ComplexBookActionResult ProcessTheComplexStuff(ComplexBookActionRequest request)
     {
          // Possibly some business logic here

          Book book = _bookRepository.GetBookByISBN(request.Isbn);
          Customer customer = _customerRepository.GetCustomerById(request.CustomerId);

          // Probably more business logic here

          _libraryRepository.Save(book);

          return complexBusinessActionResult;

     } 
}

Repository

public class BookRepository : IBookRepository
{
     LibraryDBContext _db = new LibraryDBContext();

     public Book GetBookByIsbn(string isbn)
     {
         return _db.Books.Single(b => b.ISBN == isbn);
     }

     // And the rest of the data access
}
    
risposta data 18.02.2012 - 22:39
fonte
2

Questo è il modo in cui l'ho fatto, anche se sto iniettando il fornitore di dati come un'interfaccia generica per i servizi dati in modo da poter sostituire le implementazioni.

Per quanto ne so, il controller è destinato a essere dove si ottengono i dati, si esegue qualsiasi azione e si passano i dati alla vista.

    
risposta data 18.02.2012 - 18:34
fonte

Leggi altre domande sui tag