Idealmente, dovrebbe PurchaseRecordViewModel
popolarsi ottenendo PurchaseRecordsDomainModel
. Dovrebbe contenere una semplice mappatura delle proprietà e possibilmente una formattazione dell'output che verrà utilizzato nella visualizzazione.
PurchaseRecordsViewModel
public class PurchaseRecordsViewModel
{
public IEnumerable<PurchaseRecordViewModel> PurchaseRecords {get;set;}
}
PurchaseRecordViewModel
public class PurchaseRecordViewModel
{
public DateTime Date {get;set;}
public decimal Cost {get;set;}
// .... some other properties
public PurchaseRecordsViewModel(PurchaseRecordsDomainModel domainModel)
{
Date = domainModel.Date;
Cost = domainModel.Cost;
// .... some other property mappings
}
}
Che cosa il vostro metodo di action
su PurchaseController
dovrebbe fare, sta orchestrando il processo di ottenere il vostro PurchaseRecordsDomainModel
, creazione di PurchaseRecordsViewModel
da PurchaseRecordsDomainModel
e passandolo allo View
. Il metodo Action
non deve contenere alcun codice che riguarda la connessione e il recupero dei dati dal database (nel caso in cui si esegua l'interrogazione del contesto EF
) o qualsiasi logica aziendale. Dovresti provare ad avere moduli liberamente accoppiati, parlando tra loro tramite abstractions
, in questo modo assicurerai che la tua applicazione sia maintainable
, extensible
e testable
.
Inoltre, provare a tracciare una chiara separazione tra vari livelli del sistema. Ad esempio, non è una buona idea avere EF entities
come Domain Model Entites
. Non vuoi che il tuo business logic layer
dipenda da data access layer
, pensa in questo modo, e se in un determinato momento, ti allontani dallo EF
e usi qualche altro ORM
o anche altra tecnologia per archiviare e interrogare i dati. Non vuoi cambiare business logic layer
solo perché stai modificando il tuo data access layer
. Quindi, passare dalle parole al codice nel tuo caso.
Considerando che avete già il vostro view
e view model
, vorrei creare PurchaseRecordsService
di classe in domain layer
(si prega di notare seconda nel tuo caso non si potrebbe utilizzare Repositories
, ma qualche altra tecnica, questo esempio è principalmente per illustrare il mio punto)
public class PurchaseRecordsService
{
private readonly IPurchaseRecordsRepository _purchaseRecordsRepository;
public PurchaseRecordsService(IPurchaseRecordsRepository purchaseRecordsRepository)
{
if(purchaseRecordsRepository == null)
{
throw new ArgumentNullException("purchaseRecordsRepository");
}
_purchaseRecordsRepository = purchaseRecordsRepository;
}
public IEnumerable<PurchaseRecordsDomainModel> GetPurchaseRecords()
{
// trivial case, real code can be more complex
return _purchaseRecordsRepository.GetPurchaseRecords();
}
}
Poi, nel tuo domain layer
, è possibile definire IPurchaseRecordsRepository
public interface IPurchaseRecordsRepository
{
IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords();
}
L'idea è, il nostro PurchaseRecordsService
necessita di un modo per comunicare con i database, quindi chiunque lo utilizzi deve fornire l'implementazione di IPurchaseRecordsRepository
. Il passo successivo è passare al nostro data access layer
e creare la classe di implementazione di IPurchaseRecordsRepository
.
public class EfPurchaseRecordsRepository: IPurchaseRecordsRepository
{
private readonly EfObjectContext _objectContext;
public EfPurchaseRecordsRepository(string connectionString)
{
_objectContext = new EfObjectContext(connectionString);
}
public IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords()
{
var purchaseRecords = (from p in _objectContext.PurchaseRecords
....
select p).AsEnumerable();
return purchaseRecords .Select(p => p.ConvertToDomainPurchaseRecord());
}
}
E l'ultimo pezzo - abbiamo bisogno di definire la nostra Action
in PurchaseController
public class PurchaseController: Controller
{
private readonly IPurchaseRecordsRepository _repository;
public PurchaseController(IPurchaseRecordsRepository repository)
{
if(repository == null)
{
throw new ArgumentNullException("repository");
}
_repository = repository;
}
public ActionResult Index()
{
var purchaseRecordsService = new PurchaseRecordsService(_repository);
var purchaseRecordsViewModel = new PurchaseRecordsViewModel();
var purchaseRecords = purchaseRecordsService.GetPurchaseRecords();
foreach(var purchaseRecord in purchaseRecords)
{
var purchaseRecordViewModel = new PurchaseRecordViewModel(purchaseRecord);
purchaseRecordsViewModel.PurchaseRecords.Add(purchaseRecordViewModel);
}
return View(purchaseRecordsViewModel);
}
}
Per ricapitolare, quello che abbiamo è debolmente accoppiati codice, i nostri Presentation
e Data Access
Livelli non conoscono l'un l'altro e dipendono solo dal livello Domain
. Se è necessario, è possibile sostituire il front-end MVC
con WPF
ad esempio, passare da EF
a un'altra tecnologia, il proprio codice è verificabile.
ben spiegato! Nel popolo vuoi dire qualcosa come DBContext. Populate() - La mia domanda più profonda è ciò che accade con le relazioni tra le tabelle, che sta gestendo tale responsabilità, per es. come in uno scenario di griglia dettagli master –
aggie
No 'Populate()' è solo il nome del metodo che sto chiamando. La tua classe DataAccess gestirà le operazioni del database. Inverterò l'ordine in modo che il codice venga eseguito in sequenza mentre leggi, iniziando dal controller. – CSharper
@CSharper - Sto cercando di capire il giusto approccio per il mio progetto e ho trovato questo post e la tua risposta ... Ho un dubbio: in genere manteniamo ViewModel in Web Project, mentre Service in un progetto Business Layer separato, in questo caso, per popolare viewmodel nel livello di servizio, devo aggiungere un riferimento al progetto web. Non è un po 'strano? –