Ideally, PurchaseRecordViewModel
should populate itself by getting PurchaseRecordsDomainModel
. It should contain simple mapping of properties, and possibly some formatting of the output you're going to use in your view.
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
}
}
What your action
method on PurchaseController
should do, is orchestrating the process of getting your PurchaseRecordsDomainModel
, creation of PurchaseRecordsViewModel
from PurchaseRecordsDomainModel
and passing it to the View
. Action
method itself shouldn't contain any code that deals with connecting and retrieving data from database (in your case querying EF
context), or any business logic. You should try to have loosely coupled modules, talking to each other via abstractions
, this way you will ensure your application is maintainable
, extensible
and testable
.
Also, try to draw clear separation between various layers of your system. For example, it is not a good idea to have EF entities
as Domain Model Entites
. You don't want your business logic layer
to depend on data access layer
, think of it this way, what if at some point of time in the future, you are moving away from EF
and using some other ORM
or even other technology to store and query data. You don't want to change business logic layer
just because you're changing your data access layer
. So to go from words to code in your case.
Considering that you already have your view
and view model
, I would create PurchaseRecordsService
class in domain layer
(please note depending in your case you might not use Repositories
, but some other technique, this example is mainly to illustrate my point)
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();
}
}
Then in your domain layer
, you could define IPurchaseRecordsRepository
public interface IPurchaseRecordsRepository
{
IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords();
}
The idea is, our PurchaseRecordsService
needs a way to communicate with databases, so whoever uses it, must supply implementation of IPurchaseRecordsRepository
. Next step is to move to our data access layer
and create implementation class of 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());
}
}
And the last piece - we need to define our 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);
}
}
To recap, what we have is loosely coupled code, our Presentation
and Data Access
Layers don't know about each other, and they depend only on Domain
layer. If you need, you can replace MVC
front end with WPF
for example, move from EF
to another technology, your code is testable.