6

我试图让 UpdateModel 填充在编译时仅设置为接口的模型。例如,我有:

// View Model
public class AccountViewModel {
  public string Email { get; set; }
  public IProfile Profile { get; set; }
}

// Interface
public interface IProfile {
  // Empty
}

// Actual profile instance used
public class StandardProfile : IProfile {
  public string FavoriteFood { get; set; }
  public string FavoriteMusic { get; set; }
}

// Controller action
public ActionResult AddAccount(AccountViewModel viewModel) {
  // viewModel is populated already
  UpdateModel(viewModel.Profile, "Profile"); // This isn't working.
}

// Form
<form ... >
  <input name='Email' />
  <input name='Profile.FavoriteFood' />
  <input name='Profile.FavoriteMusic' />
  <button type='submit'></button>
</form>

另请注意,我有一个继承自 DefaultModelBinder 的自定义模型绑定器,该绑定器使用覆盖的 CreateModel 方法中的 StandardProfile 实例填充 IProfile。

问题是FavoriteFood 和FavoriteMusic 永远不会被填充。有任何想法吗?理想情况下,这一切都将在模型绑定器中完成,但我不确定如果不编写完全自定义的实现是否可行。

谢谢,布赖恩

4

2 回答 2

2

我必须检查 ASP.NET MVC 代码(DefaultModelBinder),但我猜它反映的是 IProfile 类型,而不是实例 StandardProfile。

所以它会寻找它可以尝试绑定的任何 IProfile 成员,但它是一个空接口,所以它认为自己已完成。

您可以尝试更新 BindingContext 并将 ModelType 更改为 StandardProfile 然后调用

bindingContext.ModelType = typeof(StandardProfile);
IProfile profile = base.BindModel(controllerContext, bindingContext);

不管怎样,有一个空的接口很奇怪~


编辑:只想添加上面的代码只是伪代码,您需要检查 DefaultModelBinder 以确切了解您要编写的内容。


编辑#2:

你可以做:

public class ProfileModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
    {
        bindingContext.ModelType = typeof(StandardProfile);
        return base.BindModel(controllerContext, bindingContext);
    }
}

无需为 AccountView 制作模型绑定器,它可以正常工作。


编辑#3

测试了一下,上面的 binder 可以,只需要添加:

ModelBinders.Binders[typeof(IProfile)] = new ProfileModelBinder();

您的操作如下所示:

public ActionResult AddAccount(AccountViewModel viewModel) {
    // viewModel is fully populated, including profile, don't call UpdateModel
}

您可以在设置模型绑定器时使用 IOC(例如注入类型构造函数)。

于 2009-09-09T17:02:14.120 回答
0

此处讨论了不检查接口背后的实际类型:http ://forums.asp.net/t/1348233.aspx

也就是说,我找到了一种解决问题的方法。因为我已经有一个用于这种类型的自定义模型绑定器,所以我可以向它添加一些代码来为我执行绑定。这是我的模型活页夹现在的样子:

public class AccountViewModelModelBinder : DefaultModelBinder
{
    private readonly IProfileViewModel profileViewModel;
    private bool profileBound = false;

    public AccountViewModelModelBinder(IProfileViewModel profileViewModel)
    {
        this.profileViewModel = profileViewModel;
    }

    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // Bind the profile
        if (profileBound)
            return;

        profileBound = true;

        bindingContext.ModelType = profileViewModel.GetType();
        bindingContext.Model = profileViewModel;
        bindingContext.ModelName = "Profile";

        BindModel(controllerContext, bindingContext);
    }

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
    {
        var model = new AccountViewModel();
        model.Profile = profileViewModel;

        return model;
    }
}

基本上,当模型绑定器“完成”绑定主 AccountViewModel 时,我会更改绑定上下文(如 eyston 建议的那样)并再次调用 BindModel。然后绑定我的个人资料。请注意,我在 profileViewModel 上调用了 GetType(由构造函数中的 IOC 容器提供)。另请注意,我包含一个标志来指示配置文件模型是否已被绑定。否则会无限循环调用 OnModelUpdated。

我并不是说这很漂亮,但它确实可以满足我的需求。我仍然很想听听其他建议。

于 2009-09-09T19:27:33.747 回答