2009-10-20 101 views
3

在MVC模型中,加載視圖模型的責任在哪裏?誰負責加載數據?

控制器應該加載數據嗎? 視圖模型本身應該加載數據ala: MyViewModel viewModel = MyViewModel.Create(someValue); 服務層應該加載ala: MyViewModel viewModel = MembershipService.Instance.Load(someValue);

回答

2

見這個例子中,真正的清潔技術: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx

或者您也可以手動做到這一點:請參見「ASP.NET MVC在行動」一書或CodeCampServer源的例子。基本上你注入IViewModelMapper {public ViewModel Map(data); }到控制器。整潔的事情是,它使你的IoC自動將服務和存儲庫傳遞給你的ViewModel映射器。然而,這確實可以使控制器臃腫與映射器接口,所以類似吉米Bogard的技術,即使沒有AutoMapper,但與動作過濾器比挑選IViewModelMapper,會更好。

如果你不能做到這一點,那麼我建議堅持使用視圖模型處理映射,馬蒂亞斯建議。

更新:這是用CodeCampServer方式位automapper樣配置的例子。不知道它是否會按原樣工作(或者有用),只是演示。

public abstract class ViewModelMapper<Source, ViewModel> where Source: class, ViewModel: IViewModel 
{ 
    public abstract ViewModel Map(Source source); 
} 

public class ProductDetailsViewModel 
{ 
    public ProductViewModel Product { get; set; } 
    punlic IList<Language> AvailableProductLanguages { get; set; } 
} 

public class ProductDetailsViewMapper: ViewModelMapper<Product, ProductDetailsViewModel> 
{ 
    private ILanguageRepository languages; 
    public ProductDetailsViewMapper(ILanguageRepository languages) 
    { 
    this.languages = languages; 
    } 
    public override ProductDetailsViewModel Map(Product source) 
    { 
    var vm = new ProductDetailsViewModel(); 
    AutoMapper.Map<Product, ProductDetailsViewModel>(product, vm); 
    vm.AvailableProductLanguages = languages.GetAppropriateFor(product); 
    } 
} 

public class ViewModelMapperActionFilter: ActionFilter 
{ 
    Type mapperType; 
    public ViewModelMapperActionFilter() 
    { 
    } 
    public ViewModelMapperActionFilter(Type mapperType) 
    { 
    } 
    public void OnActionExecuted(ControllerContext context) 
    { 
    var model = context.Result.ViewData.Model; 
    var mapperType = this.MapperType ?? this.GetMapperTypeFromContext(context); 
    // this is where magic happens - IoC grabs all required dependencies 
    var mapper = ServiceLocator.GetInstance(mapperType); 
    var method = mapperType.GetMethod("Map"); 
    Check.Assert(method.GetArguments()[0].ArgumentType == model.GetType()); 
    context.Result.ViewData.Model = method.Invoke(mapper, new[]{model}); 
    } 
} 

public class ProductsController: Controller 
{ 
    [ViewModelMapper(typeof(ProductDetailsViewMapper))] 
    // alternatively [ViewModelMapper()] will auto-pick mapper name by controller/action 
    public ActionResult Details(EntityViewModel<Product> product) 
    { 
    // EntityViewModel is a special type, see 
    // http://stackoverflow.com/questions/1453641/my-custom-asp-net-mvc-entity-binding-is-it-a-good-solution 
    return View(product.Instance); 
    } 
} 

//Global.asax.cs: 
IoC.Register(AllTypes.DerivedFrom(typeof(ViewModelMapper<>))); 
+0

良好的鏈接 - 但是,當我在他們的特定情況下閱讀它們的ViewModel包含一個域對象的精簡版本。它不彙總來自各種服務的數據。 我想我開始覺得現在Mathias解決方案可能是最好的。 :) – Micael 2009-10-20 17:56:14

+0

這就是爲什麼我建議將這個和IViewModelMapper結合在一起。我沒有現成的解決方案;但是我認爲你仍然可以使用AutoMapper並且執行諸如CreateMap ()之類的東西,對於(vm => vm.Languages,ServiceLocator.Get >()。GetAll()),即使使用AutoMapper仍有可能從不同來源彙總。真正的事情是避免重複的工作;構造函數參數等;在我看來,描述映射的動作過濾器是一個非常好的主意,因爲ViewModel映射通常是每個動作。 – queen3 2009-10-20 18:42:10

+0

感謝您使用源代碼進行更新 - 它確實有助於將其可視化。 – Micael 2009-10-22 08:47:08

0

控制器不應該加載數據。應該在模型或數據層中。

我的偏好是在一個數據層,所以我可以從模型中分離出來,我認爲只有存儲/表示數據的控制器和視圖。

我實現用於數據檢索的存儲庫模式

+0

我們使用存儲庫從數據庫加載數據,並使用存儲庫作爲系統的API使用服務。但將視圖模型邏輯放在API層中似乎有點奇怪,因爲它非常特定於視圖。也許需要在API <->查看模型之間的頂部翻譯? 哦,這麼多層次...... – Micael 2009-10-20 09:37:25

+0

整點我認爲是關注點分離。保持模型遠離數據收集意味着您可以替換任何一個,並保持另一個沒有重大變化。我相信你知道這一點,是的,在你的情況下可能看起來很古怪,但保持它獨立是一條路。我不得不做出改變了過去,它是如此輕鬆切換從一個數據庫到下一個瓦特/觸摸模式 – griegs 2009-10-20 09:52:18

+0

所以對我們來說這將是: 查看 - >控制器 - >服務 - > API - >持久性 我可以看到,我們似乎有一個額外的層--API,duffymo沒有 - 這是我將放置任何業務邏輯的地方 - 例如,當接到訂單時發送通知電子郵件。我認爲這應該與瞭解視圖模型的Service分離開來,因爲API可以從其他類型的應用程序/服務中使用 - 你會同意嗎? – Micael 2009-10-20 11:24:51

0

I層的東西這種方式:

視圖 - >控制器 - >服務 - >持久

請求由前向後流動;迴應從前到後。

持久層擔心數據庫;它使模型數據可用。它對任何其他層都一無所知。

服務層的方法映射到用例。它知道持久層,工作單元和事務。它驗證其輸入,獲取數據庫連接,使它們可用於持久層,清理資源並使用模型對象來滿足用例。如果需要,它可以作爲Web服務或EJB公開。

控制器和視圖放在一起。它知道服務層。它將請求映射到服務,綁定和驗證傳入的請求參數,將它們傳遞到服務層,並將響應路由到適當的視圖。

該視圖只關心顯示控制器爲其提供的數據。它知道控制器。

所以這是處理數據庫的服務。控制器和視圖協作顯示信息,僅此而已。

1

我喜歡讓ViewModel加載數據。

ViewModels可從所有控制器訪問。這樣可以避免代碼重複使用。

參見該樣品ASP.NET MVC - Job of Controllers

+0

我必須恭敬地不同意這種方法。爲什麼「視圖模型」應該做除視圖所消費的數據外的其他任何東西?讓你的控制器在你的視圖模型中調用'FillPackageList()'方法而不是直接從conotrlller調用服務方法有什麼好處?在我看來,這是一個不必要的抽象,增加了額外的複雜性。控制器應加載所有數據,然後根據需要填充視圖模型屬性。只需從conotroller調用服務方法即可。 – 2009-10-20 15:01:14

+0

如果你可以通過對服務層的一次調用來填充ViewModel,那麼我猜這就是要走的路。對於複雜的ViewModel來說,這並不適用。經常有一個地方發生某種聚集。讓我們在SO上說這個頁面:你需要一個非常特殊的方法來加載一個ViewModel的對象 - RelatedPosts,Question,Answers,Posts,User。這種特殊的方法在服務中看起來不正確。 我同意:它看起來不對。到目前爲止,它工作得非常好。 – 2009-10-20 15:23:44

+0

我明白你在說什麼。這真的都歸結爲我的猜測。 「好設計」當然是主觀的。在個人情況下,我個人希望讓控制器在我的服務/存儲庫類中調用單獨的方法來檢索每個數據項。例如,我不會調用'viewModel.FillAnswers()','viewModel.FillPosts()'等,我會盡快直接從控制器調用服務方法。 – 2009-10-20 16:42:35

2

控制器是結合模型和視圖在一起的粘合劑。您希望模型類和視圖在應用程序的其他圖層上儘可能少地依賴。因此,控制器應始終加載視圖的數據,而不管數據來自何處,或者您在模型中使用哪種設計模式來檢索數據。

您可能在模型圖層中爲數據加載操作提供了一堆抽象層,但控制器應該調用一些方法或方法,這些方法或方法在調用鏈的某個點上會轉到您持久的數據存儲區正在使用並獲取您的視圖所需的數據。

控制器應提供所有對象認爲需要的,因爲這是真正的關鍵職責之一。無論這意味着使用適當的模型對象從數據庫中獲取數據,還是初始化「視圖模型」類來包裝視圖顯示所需的所有對象和屬性,都無關緊要。

就個人而言,我一直使用LINQ到SQL與ASP.Net MVC相結合,並有使用抓住從數據方面必要的對象存儲庫類了巨大的成功。爲了讓我的控制器能夠進行單元測試,我使用了一個依賴注入框架(在我的情況下是StructureMap)讓ASP.Net MVC爲我的控制器提供了我的存儲庫接口的默認實例。