2014-10-15 31 views
7

Czyim obowiązkiem jest zapełnianie wartości w architekturze ASP MVC 5 (C#, EF), np. jeśli mamy PurchaseRecordsViewModel , PurchaseRecords Domain Model , PurchaseControllerKto zapełnia ViewModel w ASP MVC 5

  • Czy kod do wypełnienia danych (czas, koszty itp) go go ViewModel, prawo w swoim własnym ViewModel iść w PurchaseRecordsViewModel?

  • Albo, czy kod odchodzenie w sposobie działania PurchaseController

Odpowiedz

6

Rozszerzając odpowiedź Tommy'ego, oto kod, który pasuje do jego opisu.

//Controller 

public ActionResult Index() 
{ 
    List<OrderViewModel>() model = new List<OrderViewModel>(); 
    model = new ServiceClass().GetOrders(); 

    return View(model); 
} 

//here is your Service Class, this layer transfers the Domain Model into your ViewModel 
public List<OrderViewModel> GetOrders() 
{ 
    List<OrderDomain> model = new List<OrderDomain>(); 

    model = new DataAccess().GetOrders(); 

    List<OrderViewModel> viewModel = new List<OrderViewModel>(); 

    foreach (var order in model) 
    { 
     OrderViewModel vm = new OrderViewModel(); 
     vm.OrderId = order.OrderId; 
     vm.OrderName = order.OrderName; 

     viewModel.Add(vm); 
    }  

    return viewModel;   
} 

//some DataAccess class, this class is used for database access 

Public List<OrderDomain> GetOrders() 
{ 
    List<OrderDomain> model = new List<OrderDomain>(); 

     using (var context = new MyEntities()) 
     { 
      model = (from x in context.Order 
        select new OrderDomain 
        { 
        OrderId = x.OrderId, 
        OrderName = x.OrderName 
        }).ToList();      
     } 
    return model; 
} 
+0

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

+0

Ż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

+1

@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? –

11

Zobacz modeli są zwykle tylko głupie zbiory właściwości. Wypełnianie modelu widoku zazwyczaj spoczywa wewnątrz twojej warstwy usługi lub, jeśli nie masz, twojej metody działania.

Pomyśl o rolach w ten sposób.

  • Model domeny to bezpośrednie odwzorowanie do tabeli bazy danych.
  • Model widoku to zbiór właściwości potrzebnych do wyświetlenia widoku.
  • Warstwa usługi pobiera/używa jednego lub więcej modeli domen i zapełnia model widoku.
  • Warstwa usługi może również przyjmować model widoku i tworzyć/aktualizować jeden lub więcej modeli domen.
  • Metoda działania kontrolera jest klejem pomiędzy tymi dwoma. Wywołuje warstwę usługi, aby uzyskać (GET) model widoku i przekazuje go do widoku. Te metody działania również pobierają (POST) model widoku i przekazują go do warstwy usługi, aby zrobić wszystko, co należy z tym zrobić.

Kolejne pytanie najczęściej zadawane to: dlaczego nie mogę używać modeli domen do widoku? Możesz, ale zazwyczaj masz do czynienia z takimi rzeczami, jak: potrzebujesz danych z więcej niż jednego modelu domeny, nie potrzebujesz wszystkich właściwości, które są w modelu domeny, i na koniec, teraz musiałbyś się martwić o aktualizowane właściwości w modelu domeny, który nie zamierzałem.

+0

Nawet z Twojej odpowiedzi Warstwa serwisowa pachnie jak dodatkowa warstwa. Z wyjątkiem aplikacji dla przedsiębiorstw warstwa usługi jest przesadna. – DarthVader

+1

@DarthVader - w rzeczywistości większość aplikacji MVC, jakie kiedykolwiek widziałem, korzysta z warstwy usług (warstwa biznesowa). Niezależnie od tego używam tutaj warstwy "usługi", aby pokazać, w jaki sposób korzystasz z domen i wyświetlasz modele.Masz rację, nie ma znaczenia, gdzie generowane są modele widoku. Jednak wiele przykładów tutaj na SO i gdzie indziej (http://codebetter.com/iancooper/2008/12/03/the-fat-controller/) ostrzega przed "grubymi" kontrolerami. – Tommy

+0

Zgadzam się z Tommy'm, chude kontrolery są zdecydowanie lepsze od grubych – CSharper

3

Najlepiej, gdyby Twój model widoku nie był świadomy twojego modelu domeny, więc powiedziałbym, że umieściłeś swoją logikę populacji w kontrolerze, być może spakowane w jakiejś klasie klasy mapping/populacji.

Pamiętaj jednak, że jeśli chodzi o pytania o to, gdzie umieścić określoną logikę, osobiste preferencje znacznie się różnią.

+0

Ok, więc dyskrecja użytkownika/devleoper !! – aggie

6

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ć.

+2

Wszystkie trzy były dobrymi odpowiedziami, które pomogły ... Spodobała mi się również odpowiedź C Shapera ... ale masz szczegółową próbkę! – aggie