Tôi muốn nói rằng bạn sử dụng lại thuật ngữ ViewModel cho cả hai hướng tương tác của khách hàng. Nếu bạn đã đọc đủ mã ASP.NET MVC trong tự nhiên, bạn có thể thấy sự khác biệt giữa một ViewModel và một EditModel. Tôi nghĩ rằng đó là quan trọng.
ViewModel đại diện cho tất cả thông tin bắt buộc để hiển thị chế độ xem. Điều này có thể bao gồm dữ liệu được hiển thị ở những nơi không tương tác tĩnh và cũng có dữ liệu hoàn toàn để thực hiện kiểm tra để quyết định chính xác những gì sẽ hiển thị. Hành động của Controller GET thường chịu trách nhiệm đóng gói ViewModel cho View của nó.
EditModel (hoặc có thể là ActionModel) đại diện cho dữ liệu cần thiết để thực hiện hành động mà người dùng muốn thực hiện cho POST đó. Vì vậy, một EditModel thực sự đang cố gắng mô tả một hành động. Điều này có lẽ sẽ loại trừ một số dữ liệu từ ViewModel và mặc dù có liên quan tôi nghĩ rằng điều quan trọng là nhận ra chúng thực sự khác nhau.
Một Idea
Điều đó nói rằng bạn có thể rất dễ dàng có một cấu hình AutoMapper cho đi từ Model -> ViewModel và một chương trình khác để đi từ EditModel -> Mẫu. Sau đó, các hành động Controller khác nhau chỉ cần sử dụng AutoMapper. Địa ngục EditModel có thể có một chức năng trên nó để xác nhận tính chất của nó đối với mô hình và áp dụng các giá trị đó cho chính Model đó. Nó không làm bất cứ điều gì khác và bạn có ModelBinders trong MVC để ánh xạ các yêu cầu cho EditModel anyway.
Idea Một
Ngoài một cái gì đó mà tôi đã suy nghĩ về thời gian gần đây rằng loại công trình ra khỏi ý tưởng về một ActionModel là những gì khách hàng được đăng lại cho bạn thực sự là mô tả của một vài hành động người dùng được thực hiện và không chỉ một lượng lớn dữ liệu. Điều này chắc chắn sẽ yêu cầu một số Javascript ở phía khách hàng để quản lý nhưng ý tưởng là hấp dẫn tôi nghĩ.
Về cơ bản khi người dùng thực hiện các tác vụ trên màn hình bạn đã trình bày, Javascript sẽ bắt đầu tạo danh sách các đối tượng hành động. Một ví dụ có thể là người dùng đang ở màn hình thông tin của nhân viên. Họ cập nhật họ và thêm địa chỉ mới vì nhân viên gần đây đã kết hôn. Dưới nắp, sản phẩm này tạo ra một đối tượng ChangeEmployeeName
và AddEmployeeMailingAddress
đối tượng trong danh sách. Người dùng nhấp vào 'Lưu' để thực hiện các thay đổi và bạn gửi danh sách hai đối tượng, mỗi đối tượng chỉ chứa thông tin cần thiết để thực hiện từng hành động.
Bạn sẽ cần một ModelBinder thông minh hơn sau đó trình mặc định nhưng JSON serializer tốt sẽ có thể quản lý ánh xạ của các đối tượng hành động phía máy khách đến các đối tượng phía máy chủ. Các máy chủ bên (nếu bạn đang ở trong một môi trường 2 tầng) có thể dễ dàng có các phương thức hoàn thành hành động trên Mô hình mà chúng làm việc. Vì vậy, hành động Controller kết thúc chỉ nhận được một Id cho instance Model để kéo và một danh sách các hành động để thực hiện trên nó. Hoặc các hành động có id trong chúng để giữ chúng rất riêng biệt.
Vì vậy, có lẽ một cái gì đó như thế này được thực hiện ở phía máy chủ:
public interface IUserAction<TModel>
{
long ModelId { get; set; }
IEnumerable<string> Validate(TModel model);
void Complete(TModel model);
}
[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
var errors = new List<string>();
foreach(var action in actions)
{
// relying on ORM's identity map to prevent multiple database hits
var employee = _employeeRepository.Get(action.ModelId);
errors.AddRange(action.Validate(employee));
}
// handle error cases possibly rendering view with them
foreach(var action in editModel.UserActions)
{
var employee = _employeeRepository.Get(action.ModelId);
action.Complete(employee);
// against relying on ORMs ability to properly generate SQL and batch changes
_employeeRepository.Update(employee);
}
// render the success view
}
Đó thực sự làm cho việc niêm yết trở lại hành động khá chung chung kể từ khi bạn đang dựa vào ModelBinder của bạn để giúp bạn có được dụ IUserAction chính xác và dụ IUserAction của bạn để thực hiện chính xác logic hoặc (nhiều khả năng) gọi vào Mô hình với thông tin.
Nếu bạn ở trong môi trường 3 tầng, IUserAction chỉ có thể được thực hiện các DTO đơn giản được quay qua ranh giới và được thực hiện theo phương pháp tương tự trên lớp ứng dụng. Tùy thuộc vào cách bạn làm lớp đó nó có thể được chia ra rất dễ dàng và vẫn còn trong một giao dịch (những gì đến với tâm trí là yêu cầu/phản ứng của Agatha và tận dụng bản đồ nhận dạng của DI và NHibernate).
Dù sao tôi chắc chắn nó không phải là một ý tưởng hoàn hảo, nó sẽ yêu cầu một số JS về phía khách hàng để quản lý, và tôi đã không thể làm một dự án nào để xem nó mở ra như thế nào, nhưng bài viết đã cố gắng suy nghĩ về làm thế nào để đạt được điều đó và trở lại một lần nữa vì vậy tôi figured tôi sẽ đưa ra những suy nghĩ của tôi. Tôi hy vọng nó sẽ giúp và tôi rất thích nghe những cách khác để quản lý các tương tác.
Tôi không chắc Automapper hữu ích ở đây vì nó không thể đảo ngược làm phẳng. Xét cho cùng, Mô hình miền không phải là một DTO đơn giản như Chế độ xem mô hình, do đó có thể không đủ để gán một số thuộc tính cho nó. Có lẽ, một số hành động sẽ được thực hiện đối với Mô hình miền theo nội dung của Chế độ xem. Tuy nhiên, +1 để chia sẻ cách tiếp cận khá tốt. –
@Anton ValueInjecter có thể đảo ngược làm phẳng;) – Omu
với cách tiếp cận này bạn không giữ bộ điều khiển mỏng, bạn vi phạm SoC và DRY ... như Omu đã đề cập bạn nên có một lớp riêng biệt để quan tâm đến công cụ lập bản đồ. – Rookian