asp.net – MVC通用ViewModel

栏目: ASP.NET · 发布时间: 5年前

内容简介:翻译自:https://stackoverflow.com/questions/4716491/mvc-generic-viewmodel

简而言之,我希望能够将通用的ViewModel传递给我的视图

这是我想要实现的要点的一些简化代码

public interface IPerson
{
    string FirstName {get;}
    string LastName {get;}
}

public class FakePerson : IPerson
{
    public FakePerson()
    {
        FirstName = "Foo";
        LastName = "Bar";
    }

    public string FirstName {get; private set;} 
    public string LastName {get; private set;} 
}

public class HomeViewModel<T> 
    where T : IPerson, new()
{
    public string SomeOtherProperty{ get; set;}
    public T Person { get; private set; }

    public HomeViewModel()
    {
        Person = new T();
    }
}

public class HomeController : Controller {
    public ViewResult Index() {
        return View(new HomeViewModel<FakePerson>());
    }
}

如果我按如下方式创建我的视图,则按预期工作

<%@ Page Language="C#" 
    MasterPageFile="~/Views/Shared/Site.Master"
    Inherits="System.Web.Mvc.ViewPage<HomeViewModel<FakePerson>>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%: Html.DisplayFor(m=>m.Person.FirstName) %>
    <%: Html.DisplayFor(m=>m.Person.LastName) %>   
</asp:Content>

但是,如果我想传递其他一些IPerson实现,我不想在视图中直接依赖FakePerson,所以我尝试将页面指令更改为

<%@ Page Language="C#" 
    MasterPageFile="~/Views/Shared/Site.Master"
    Inherits="System.Web.Mvc.ViewPage<HomeViewModel<IPerson>>" %>

但当然这不起作用,所以,经过一整天的磨砺,我有更多的白发,不知道接下来该做什么.

请有人帮忙吗?

[UPDATE]

有人建议我应该使用协变界面;定义非泛型接口并在View中使用它.不幸的是,我尝试过这一点,但有一个附带的含义.我希望HtmlHelper函数能够访问可能在IPerson派生类中定义的任何数据注释属性

public class FakePerson : IPerson
 {
    public FakePerson()
    {
        FirstName = "Foo";
        LastName = "Bar";
    }

    [DisplayName("First Name")]
    public string FirstName {get; private set;}

    [DisplayName("Last Name")]
    public string LastName {get; private set;} 
}

因此,在使用协变接口时,这种方式可以部分地通过ViewModel访问派生类型.由于视图是键入到界面,因此看起来属性不可访问.

在视图中,是否有一种方法可以通过反射访问这些属性.

或者可以在其他方面输入View到泛型.

我已成功地使用协变工作,即将View绑定到抽象基类.事实上,我所拥有的是对List<

MyBaseClass>的绑定.然后我创建一个特定的View强类型到每个子类.这照顾了绑定.

但是重新绑定会失败,因为DefaultModelBinder只知道抽象基类,你会得到一个例外,“无法创建抽象类”.解决方案是在您的基类上有一个属性,如下所示:

public virtual string BindingType
    {
        get
        {
            return this.GetType().AssemblyQualifiedName;
        }
    }

将其绑定到视图中的隐藏输入.然后用Global.asax中的自定义ModelBinder替换默认的ModelBinder:

// Replace default model binder with one that can deal with BaseParameter, etc.
    ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

在您的自定义模型绑定器中,您可以拦截绑定.如果它是针对您已知的抽象类型之一,则解析BindingType属性并替换模型类型,以便获取子类的实例:

public class CustomModelBinder : DefaultModelBinder
{
    private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
    {
        if (modelType.IsInterface || modelType.IsAbstract)
        {
            // This is our convention for specifying the actual type of a base type or interface.
            string key = string.Format("{0}.{1}", bindingContext.ModelName, Constants.UIKeys.BindingTypeProperty);            
            var boundValue = bindingContext.ValueProvider.GetValue(key);

            if (boundValue != null && boundValue.RawValue != null)
            {
                string newTypeName = ((string[])boundValue.RawValue)[0].ToString();
                logger.DebugFormat("Found type override {0} for Abstract/Interface type {1}.", modelType.Name, newTypeName);

                try
                {
                    modelType = System.Type.GetType(newTypeName);
                }
                catch (Exception ex)
                {
                    logger.ErrorFormat("Error trying to create new binding type {0} to replace original type {1}. Error: {2}", newTypeName, modelType.Name, ex.ToString());
                    throw;
                }
            }
        }

        return base.CreateModel(controllerContext, bindingContext, modelType);
    }

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        if (propertyDescriptor.ComponentType == typeof(BaseParameter))
        {
            string match = ".StringValue";
            if (bindingContext.ModelName.EndsWith(match))
            {
                logger.DebugFormat("Try override for BaseParameter StringValue - looking for real type's Value instead.");
                string pattern = match.Replace(".", @"\.");
                string key = Regex.Replace(bindingContext.ModelName, pattern, ".Value");
                var boundValue = bindingContext.ValueProvider.GetValue(key);
                if (boundValue != null && boundValue.RawValue != null)
                {
                // Do some work here to replace the base value with a subclass value...
                    return value;
                }
            }
        }

        return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }
}

在这里,我的抽象类是BaseParameter,我将StringValue属性替换为与子类不同的值(未显示).

请注意,虽然您可以重新绑定到正确的类型,但是仅与子类关联的表单值不会自动往返,因为模型绑定器只能看到基类的属性.在我的例子中,我只需要替换GetValue中的一个值,而不是从子类中获取它,所以很容易.如果你需要绑定很多子类属性,你需要做更多的工作并从表单中取出它们(ValueProvider [0])并自己填充实例.

请注意,您可以为特定类型添加新的模型绑定器,以避免泛型类型检查.

翻译自:https://stackoverflow.com/questions/4716491/mvc-generic-viewmodel


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

機器,平台,群眾

機器,平台,群眾

安德魯‧麥克費(Andrew McAfee)、艾瑞克‧布林優夫森(Erik Brynjolfsson) / 李芳齡 / 天下文化 / 2017-12-27 / TWD550

★★Amazon.com商業理財Top1 ★★ 全球暢銷書《第二次機器時代》作者最新力作 兩位MIT數位頂尖科學家歷時三年時間 走訪矽谷、華府、劍橋、紐約、倫敦、舊金山等科技政經重鎮 拜會許多領域精英進行交流,結合宏觀趨勢觀察, 指出人人都應關注的三重革命 科技正以空前速度改變每個產業及每個人的生活, 你該如何做,才能保持領先? 我們生活在一個奇特的......一起来看看 《機器,平台,群眾》 这本书的介绍吧!

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具