Idealnie PurchaseRecordViewModel
powinien zapełnić się przez coraz PurchaseRecordsDomainModel
. Powinien zawierać proste odwzorowanie właściwości i być może pewne formatowanie wyjścia, którego będziesz używać w widoku.
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
}
}
Co metoda action
na PurchaseController
należy zrobić, jest wzniecanie proces zaczyna swój PurchaseRecordsDomainModel
, tworzenie PurchaseRecordsViewModel
z PurchaseRecordsDomainModel
i przepuszczenie do View
. Action
sama metoda nie powinna zawierać żadnego kodu, który zajmuje się łączeniem i odzyskiwaniem danych z bazy danych (w twoim przypadku zapytanie o kontekst EF
) lub jakiejkolwiek logiki biznesowej. Powinieneś próbować mieć luźno połączone moduły, rozmawiając ze sobą przez abstractions
, w ten sposób upewnisz się, że twoja aplikacja jest maintainable
, extensible
i testable
.
Spróbuj również narysować wyraźną separację między różnymi warstwami systemu. Na przykład nie jest dobrym pomysłem posiadanie EF entities
jako Domain Model Entites
. Nie chcesz, aby Twój business logic layer
był zależny od data access layer
, pomyśl o tym w ten sposób, a jeśli w pewnym momencie w przyszłości odejdziesz od EF
i używając innej, innej technologii do przechowywania i wysyłania zapytań o dane. Nie chcesz zmieniać business logic layer
tylko dlatego, że zmieniasz swoją data access layer
. A więc od słów do kodu w twoim przypadku.
Biorąc pod uwagę, że masz już swoją view
i view model
, chciałbym stworzyć PurchaseRecordsService
klasę w domain layer
(uwaga zależności w przypadku, gdy nie można użyć Repositories
, ale jakaś inna technika, ten przykład jest głównie do zilustrowania mój punkt widzenia)
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();
}
}
Następnie w domain layer
, można zdefiniować IPurchaseRecordsRepository
public interface IPurchaseRecordsRepository
{
IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords();
}
Chodzi o to, nasz PurchaseRecordsService
potrzebuje sposobu na komunikację z bazami danych, więc ktokolwiek z nich korzysta, musi dostarczyć implementację IPurchaseRecordsRepository
. Kolejnym krokiem jest przeniesienie do naszego data access layer
i utworzenie klasy implementacji 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());
}
}
I ostatni kawałek - musimy określić nasze Action
w 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);
}
}
Podsumowując, co mamy jest luźno kod, nasze Presentation
i Data Access
Warstwy nie wiedzą o sobie nawzajem i zależą one tylko od warstwy Domain
. Jeśli potrzebujesz, możesz zastąpić przednią końcówkę MVC
, np. WPF
, przejść od EF
do innej technologii, Twój kod można przetestować.
dobrze wyjaśnione! W populacjach masz na myśli coś takiego jak DBContext .Populate() - Moje głębsze pytanie dotyczy tego, co dzieje się z relacjami między tabelami, kto zajmuje się tą odpowiedzialnością, np. jak w scenariuszu ze szczegółowymi danymi grid –
aggie
Żadne 'Populate()' nie jest po prostu nazwą metody, do której dzwonię. Twoja klasa DataAccess będzie obsługiwać operacje na bazach danych. Zamienię kolejność, aby kod był wykonywany sekwencyjnie podczas czytania, zaczynając od kontrolera. – CSharper
@ShShper - Próbuję znaleźć właściwe podejście do mojego projektu i natknąłem się na ten post i twoją odpowiedź ... Mam jedną wątpliwość: Generalnie trzymamy ViewModel w Web Project, podczas gdy serwis w oddzielnym projekcie Business Layer, w tym przypadku, aby zapełnić viewmodel w warstwie usługi, muszę dodać odwołanie do projektu WWW. Czy to nie jest dziwne? –