ASP.NET MVC:将表单POST绑定到参数时避免紧耦合

时间:2009-07-03 21:36:56

标签: asp.net-mvc modelbinders defaultmodelbinder

假设我有一个界面:

interface IThing {
   int Id { get; set; }
   string Title { get; set; }
}

在ASP.NET MVC中,我有一个表单发布到这样的控制器动作:

 [AcceptVerbs(HttpVerbs.Post)]
 public ActionResult NewThing([Bind(Exclude = "Id")] SimpleThing thing) {

    // code to validate and persist the thing can go here            
 }

其中 SimpleThing 是一个几乎没有实现 IThing 的具体类。

但是,我希望我的所有方法都能处理界面。我有一个使用NHiberate和它自己的 IThing 实现的数据程序集(我们称之为 RealThing )。我无法将SimpleThing传递给它,因为它会抱怨“未知实体”。

有没有人对更清洁的方法有任何想法?我正在考虑使用工厂类的东西。但是如何让MVC表单绑定器使用呢?

谢谢!

4 个答案:

答案 0 :(得分:4)

您可以使用自定义model binders。然而关于msdn的文章完全没用。因此,更好地利用搜索并找到更好的东西。有可用的文章。

答案 1 :(得分:3)

我提出了两种方法。

第一个是将代码添加到我的NHibernate Repository类,将MVC控制器使用的简单POCO类型( SimpleThing )转换为NHibernate想要的实体类型( RealThing ):

/// <summary>
/// A NHibernate generic repository.  Provides base of common 
/// methods to retrieve and update data.
/// </summary>
/// <typeparam name="T">The base type to expose 
/// repository methods for.</typeparam>
/// <typeparam name="K">The concrete type used by NHibernate</typeparam>
public class NHRepositoryBase<T, K> 
    : IRepository<T>
    where T : class
    where K : T, new()
{
    // repository methods ...

    /// <summary>
    /// Return T item as a type of K, converting it if necessary
    /// </summary>        
    protected static K GetKnownEntity(T item) {
        if (typeof(T) != typeof(K)) {
            K knownEntity = new K();

            foreach (var prop in typeof(T).GetProperties()) { 
                object value = prop.GetValue(item, null);
                prop.SetValue(knownEntity, value, null);
            }

            return knownEntity;
        } else {

            return (K)item;
        }            
    }

因此,存储库中的任何方法都可以调用GetKnownEntity(T item),它会将传入的项的属性复制到NHibernate想要的类型。显然这感觉有点笨重,所以我查看了自定义模型粘合剂。


在第二种方法中,我创建了一个这样的自定义模型绑定器:

public class FactoryModelBinder<T> 
    : DefaultModelBinder 
    where T : new() 
{

    protected override object CreateModel(ControllerContext controllerContext, 
                                          ModelBindingContext bindingContext, 
                                          Type modelType) {

        return new T();                       
    }

}

然后我在Global.asax.cs中注册了:

ModelBinders.Binders.Add(typeof(IThing), 
            new FactoryModelBinder<RealThing>());

使用如下所示的Controller Action可以正常工作:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NewThing([Bind(Exclude = "Id")] IThing thing) {
    // code to process the thing goes here
}

我喜欢第二种方法,但我的大多数依赖注入内容都在Controller类中。我不想在Global.asax.cs中添加所有这些ModelBinder映射。

答案 2 :(得分:1)

这里有一些很好的建议,我实际上想出了一个有效的解决方案。但是,我最终得到了一些东西。我刚刚创建了特定于我发布的表单数据的模型,并使用了默认的模型绑定器。

更简单,它允许我捕获不属于我的域模型的数据(即像“注释”字段)。

答案 3 :(得分:0)

这不是你想问的问题。

我们使用略有不同的方法来处理您遇到的同样问题。我们的控制器接受逐字段匹配持久实体的DTO。然后我们用AutoMapper来创建将转到数据库的持久实体。这消除了unnessesery接口并锁定了面向公众的API(意味着重命名persistance对象的字段不会破坏我们的客户端代码)。