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

发布于 2024-07-26 06:52:42 字数 671 浏览 12 评论 0原文

假设我有一个像这样的接口:

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 表单活页夹使用它呢?

谢谢!

Let's say I have an interface like:

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

And in ASP.NET MVC I have a form that posts to a controller action like this:

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

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

Where SimpleThing is a concrete class that just barely implements IThing.

However, I would like all my methods to deal with the interface. I have a data assembly that uses NHiberate and its own IThing implementation (let's call it RealThing). I can't pass the SimpleThing to it because it will complain about an "unknown entity".

Does anyone have any ideas about a cleaner way to do this? I was thinking about something along the lines of using a factory class. But how would I get the MVC form binder to use it?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

贱贱哒 2024-08-02 06:52:42

您可以使用自定义模型绑定器。 然而msdn上的文章完全没用。 因此最好使用搜索并找到更好的东西。 有大量可用的文章。

You can use custom model binders. However article on msdn is totally useless. So better to employ search and find something better. There are planaty of articles available.

帅的被狗咬 2024-08-02 06:52:42

我想出了两种方法。

第一个是向我的 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>());

它与如下所示的控制器操作一起工作得很好:

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

我喜欢第二种方法,但我的大部分依赖项注入的东西在Controller类中。 我不喜欢在 Global.asax.cs 中添加所有这些 ModelBinder 映射。

I came up with two approaches to this.

The first was to add code to my NHibernate Repository class to translate the simple POCO type used by the MVC controller (SimpleThing) to the type of entity that NHibernate wanted (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;
        }            
    }

So, any method in the repository can call GetKnownEntity(T item) and it will copy the properties of the item you pass in to the type that NHibernate wants. Obviously this felt a bit clunky, so I looked in to custom model binders.


In the second approach, I created a custom model binder like this:

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

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

        return new T();                       
    }

}

Then I registered that in Global.asax.cs with:

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

And it works fine with a Controller Action that looks like this:

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

I like the second approach, but most of my dependency injection stuff is in the Controller class. I don't like to have to add all these ModelBinder mappings in Global.asax.cs.

寻找一个思念的角度 2024-08-02 06:52:42

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

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

There were some good suggestions here and I actually came up with a solution that works. However, I ended up with something altogether. I just created models specific for the form data I was posting and used the default model binder.

Much simpler and it allows me to capture data that isn't part of my domain model (ie. like a "comments" field).

递刀给你 2024-08-02 06:52:42

这并不是直接回答你的问题。

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

This is not dirrect unswer to your question.

We use slightly different approach to deal with same problem you have. Our Controllers accepts DTOs that match persistent entity field by field. Then we user AutoMapper to create persisten entities that will go to the database. This eliminates unnessesery interfaces and locks down public facing API (means that renaming persistance object's field will not break our client code).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文