1

<form>在我正在做的一个项目中,我有很多没有s的“只读”页面;我还有很多表单页面,其中也有很多从控制器中提取的只读数据。

通常你会使用 ViewModel 并且每个视图有一个 ViewModel,而 ViewModel 包含该视图的所有数据。这似乎很公平,除了有一个问题:

在我的脑海中,我认为 ViewModel 是从视图发送回控制器的整个数据的表示和封装,但 ViewModel 可能包含由控制器填充的数据(例如 SelectListItem[] Html.DropDownListFor() 数据) 不能由视图填充并发送回控制器。

当然,可以将这些数据作为 ViewModel 的一部分并在控制器的 HttpPost 处理方法中返回视图之前手动重新填充它,但我觉得它不必要地使控制器的代码复杂化(你必须使用 UpdateModel () 而不是在将模型指定为操作方法的参数时发生的自动更新)。

我对此的解决方案是一个类型化的 ViewData 对象。我派生于ViewPage<TModel>提供ViewPage2<TModel,TData> where TData : ViewDataDictionary<TModel>并覆盖(或隐藏)该.ViewData属性以返回 TData 的实例。

我的问题是双重的:

  • 子类化 ViewPage 似乎很容易,但是我在哪里放置逻辑来处理我的ViewPage2<TModel,TData>类的初始化?
  • 我的方法有什么问题吗?
4

1 回答 1

0

我找到了一种方法来实现我的 ViewData / ViewModel 方法,而不会过多地使用 ASP.NET MVC。

我是这样做的:

这是我的ViewPage2类,它将强类型的 ViewData 对象公开给视图。不幸的是,您确实需要使用反射来避免 ViewPage 创建全新 ViewDataDictionary 的行为,但是对于静态缓存的 FieldInfo 对象,它并不比 MVC 在路由中的动态控制器/动作查找更昂贵。

public class ViewPage2<TModel,TData> : ViewPage<TModel> where TData : ViewDataDictionary<TModel> {

    public ViewPage2() : base() {
    }

    private Boolean _dataPresent;
    private TData   _data;

    public new TData ViewData {
        get {
            if( _dataPresent && _data == null ) {
                _data = (TData)base.ViewData;
            }
            return _data;
        }
    }

    // Cached in static class state for performance.
    private static readonly FieldInfo _viewPage1ViewData;
    private static readonly FieldInfo _viewPage2ViewData;

    static ViewPage2() {

        Type viewPage1     = typeof(ViewPage<TModel>);
        _viewPage1ViewData = viewPage1.GetField("_viewData", BindingFlags.Instance | BindingFlags.NonPublic );

        Type viewPage2     = typeof(ViewPage);
        _viewPage2ViewData = viewPage2.GetField("_viewData", BindingFlags.Instance | BindingFlags.NonPublic );
    }

    protected override void SetViewData(ViewDataDictionary viewData) {

        // ViewPage<TModel> creates a new ViewDataDictionary<TModel> when this method is called, even if viewData is of the correct type.

        // The trick is to reimplement SetViewData and set base._viewData and basebase._viewData

        if( viewData is TData ) {

            _viewPage1ViewData.SetValue( this, viewData );
            _viewPage2ViewData.SetValue( this, viewData );

            _dataPresent = true;

        } else {

            base.SetViewData( viewData );
        }

    }
}

然后对于每个 *.aspx 文件(我使用 WebFormViewEngine),我只需更改@Page指令:

<%@ Page Language="C#" MasterPageFile="~/Site.Master" Inherits="Me.ViewPage2<Me.FormModel,Me.FormData>" %>

我承认这两个泛型类型说明符使它有点繁琐,但你只需要设置一次。

然后在每个控制器中,只需这样做:

public ActionResult Edit() {
    FormData data = new FormData();
    data.SomeStronglyTypedField = "foo";
    this.ViewData = data;
}

在每个视图中,您现在都可以从强类型视图数据中受益:

<p><%= ViewData.SomeStronglyTypedField %></p>

由于 ViewData 不是模型的一部分,因此在处理 POST 提交的数据时关注点分离以及自动绑定的好处:

 [HttpPost]
 public ActionResult Edit(EditModel model) {
     if( !ModelState.IsValid ) {

        // See how I can return the model object without modifying it. All I need to do is re-create the View data.

        FormData data = new FormData();
        data.SomeStronglyTypedField = "foo";
        this.ViewData = data;

        return View( model );
     }
     // persist to DB here
     return RedirectToAction("View");
 }

在实践中,我使用一个通用的 BaseController 类来处理 ViewData 对象的自动创建和设置,因此我不需要FormData data...在每个操作中使用这三行(等)。

于 2012-07-09T16:29:35.493 回答