本文介绍了如何彻底脱钩MVC中的视图和模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的第一个例子,我有一个模型,像这样:

In my first example I have a Model like so:

public class GuestResponse
{
    [Required(ErrorMessage = "Please enter your name")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Please enter your email")]
    [RegularExpression(".+\\@.+\\..+", ErrorMessage = "Please enter a valid email address")]
    public string Email { get; set; }

    public string Phone { get; set; }

    [Required(ErrorMessage = "Please specify whether you'll attend")]
    public bool? WillAttend { get; set; }
}

控制器:

public class HomeController : Controller
{
    public ViewResult Index()
    {
        ViewData["greeting"] = (DateTime.Now.Hour < 12 ? "Good morning" : "Good afternoon");
        return View();
    }

    [HttpGet]
    public ViewResult RsvpForm()
    {
        return this.View();
    }

    [HttpPost]
    public ViewResult RsvpForm(GuestResponse guestResp)
    {
        if (ModelState.IsValid)
        {
            return this.View("Thanks", guestResp);
        }
        else
        {
            return this.View();
        }
    }
}

和视图:

@model GuestResponse
<body>
<div>
    <h1>
        Thank you,
        <%: Model.Name  %>.</h1>
    <p>
        <% if (Model.WillAttend == true)
           {  %>
        It's great that you're coming. The drinks are already in
        the fridge!
        <% }
           else
           {  %>
        Sorry to hear you can't make it, but thanks for letting
        us know.
        <% } %>
    </p>
</div>

什么觉得奇怪,我是该视图是紧密结合的模式:它使用code像 Model.WillAttend 等...所以会发生什么如果在未来的时间模型的变化?我应该改变这种特殊的查看里面的所有片段。

What seems strange to me, is that the View is tightly coupled with the Model: it uses code like Model.WillAttend etc... So what happen if in a future time the Model changes? I should have to change all the snippets inside this particular View.

假如我的看法会显示一个注册页面,我将显示的姓名,职务,地址1,地址等输入,所有这些领域将被绑定到模型,但模型可能是不存在的时间。所以,我可以创建一个界面和模式将实现该接口和视图只是导入接口,而不是模型类?因此,创建UI因此智能感知会显示姓名,职务,地址1,地址等,当我们键入模型。

Suppose my view will show a registration page where I will show input for name, title, address1, address2 etc and all these fields will be bound to the model, but the model may not exist that time. So can I create a interface and model will implement that interface and the view will just import that interface instead of the model class? So create UI as a result IntelliSense will show name, title, address1, address2 etc when we type Model.?

我应该遵循什么办法让两个人分别可以开发视图和模型?所以,当视图将被创建模型,然后可能不存在,而车型将随后创建。它怎么会可能呢?通过接口?

What approach should I follow so two people separately can develop view and model? So when view will be created then model may not exist rather model will be created later. How it will be possible? Through an interface?

推荐答案

这一个一秒钟深深的思考,这是不可能脱钩你的查看视图模型。你不能启动,但不以某种方式预测或其他什么信息段将页面并在那里上显示创建一个网页 - 因为这是precisely HTML code是什么写作。如果你没有决定的这两件事情至少有一个,没有任何HTML code写的。所以,如果你有一个显示信息,从你的控制器来一个网页,你需要定义一个视图。

Separating your View From your View Model

Thinking deeply about this one for a second, it's not possible to decouple your View from your View Model. You can't start creating a web page without anticipating in some way or another what pieces of information are going to be displayed on the page and where - because that's precisely what writing HTML code IS. If you don't decide at least one of those two things, there isn't any HTML code to write. So if you have a page that displays information coming from your controller, you need to define a view.

唯一视图模型您传递到您的视图应该重新present只有数据字段将被显示为一个单一的视图(或部分视图) 。这不是解耦-能,因为你永远不需要它的多种实现 - 它是免费的逻辑,因此没有其他的实现它。这是你的应用程序的其它部分需​​要去耦,以使它们可重复使用和维护。

The View Model that you pass to your view should represent only the data fields that are to be displayed for a single view (or partial view) only. It's not "decouple-able", because you will never require multiple implementations of it - it is free of logic and hence there is no other implementation of it. It's the other pieces of your application that require decoupling in order to make them reusable and maintainable.

即使你使用了动态 ViewBag 和使用反射来确定包含在其中动态显示整个网页的属性,最终你不得不决定在哪里该信息将被显示,并且顺序。如果你正在写你的任何HTML code比你的看法及相关助手,或在您的视图执行除显示逻辑等任何东西,那么你很可能打破MVC的基本原则之一内的其他任何地方。

Even if you used the dynamic ViewBag and used reflection to determine the properties that are contained within it to display your entire page dynamically, eventually you'd have to decide where that information is going to be displayed and in what order. If your are writing any of your HTML code anywhere other than within your view and related helpers, or executing anything other than display logic in your view, then you're probably breaking one of the fundamental principles of MVC.

所有不虽然输了,请继续阅读...

All is not lost though, keep reading...

在条款两人分别独立开发的视图和模型
(因为你很清楚的提出的问题),这是完全没问题有没有定义的模型的视图。单从视图中删除 @model 完全,或者把它注释掉准备稍后注释。

In terms two people separately developing your view and model independently(as you quite clearly asked in the question), it's completely fine to have a view with no model defined. Just remove the @model completely from the view, or comment it out ready to be uncommented later.

//@model RegistrationViewModel
<p>Welcome to the Registration Page</p>

如果没有 @model 中定义的,你不必通过从你的控制器模型视图:

Without a @model defined, you don't have to pass through a model from your controller to your view:

public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        // Return the view, without a view model
        return View();
    }
}

您还可以使用的HTML辅助对MVC的非强类型的版本。因此,与定义的视图 @model ,你可能会写这样的:

You can also use non-strongly typed versions of the the HTML helpers for MVC. So with a view @model defined, you might have written this:

@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName)

相反,使用的版本,而不在名称末尾,这些接受一个字符串作为名称,而不是直接引用模型的:

Instead, use the versions without the For at the end of the name, these accept a string as a name instead of referring directly to your model:

@Html.Label("UserName")
@Html.TextBox("UserName")

您可以更新这些后来与助手的强类型版本以后,当你有一个视图模型完成的页面。这会让你的code稍微更稳健以后。

You can update these later with the strongly typed versions of the helpers later on when you have a View Model finished for the page. This will make your code a bit more robust later on.

在评论后面,我会尝试给您code显示我怎么倾向于布局在MVC我的code和我才能用的东西分离出不同的对象.. 。这将真正使您的code。通过多人更容易维护。当然,这是一个有点时间的投资,但你应用程序的增长出这是非常值得在我看来。

On the back of the comments, I'll attempt to show you with code how I tend to lay out my code in MVC and the different objects that I use in order to separate things out... which will truly make your code more maintainable by multiple people. Sure, it is a bit of an investment in time, but it's well worth it in my opinion as you application grows out.

您应该有不同的目的,一些跨层的不同类别和一些居住在一个特定的层,但没有从这些层外部访问。

You should have different classes for different purposes, some cross layers and some reside in a specific layer and aren't accessed from outside those layers.

我通常有以下几种类型的模型与我的MVC项目:


  • 域模型 - 这重新present该行的数据库模型,我倾向于操纵这些只是我的服务层,因为我使用实体框架,所以我不具有的数据访问层这样

  • 的DTO - 数据传输对象,传递服务层之间的具体数据 UI层

  • 视图模式 - 这只是您的视图和控制器内引用,你,你把它们传递给你的看法之前,你的DTO映射到这些模型

  • Domain Models - Models that represent the the rows in the database, I tend to manipulate these ONLY in my service layer because I use Entity Framework so I don't have a 'data access layer' as such.
  • DTOs - Data Transfer objects, for passing specific data between the Service Layer and UI Layer
  • View Models - Models that are just referenced within your view and controllers, you map your DTOs to these before you pass them to your view.

下面是我如何使用它们(你问code,所以这里的,我只是灌输在一起与你相似的例子,只是一个简单的注册):

Here's how I make use of them (You asked for code, so here's an example that I just drummed together that is similar to yours, but just for a simple registration):

下面是简单地重新$ P $域模型psents一个用户和它的列,因为它们是在数据库中。我的的DbContext 使用域模型和我处理领域模型在我的服务层

Here's a domain model that simply represents a User and it's columns as they are in the database. My DbContext uses domain models and I manipulate domain models in my Service Layer.

public User
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

数据传输对象(DTO的)

下面是我映射在我的 UI层在我的控制器和传递给我的服务层的一些数据传输对象,反之亦然。看看他们是多么干净,就应该只包含传递数据来回层之间所需的字段,的每个人都应该有特定的目的,希望通过特定的方法在你的服务层接收或返回

Data Transfer Objects (DTOs)

Here are some data transfer objects that I map in my UI Layer in my controllers and pass to my Service Layer and vice versa. Look how clean they are, they should contain only the fields required for passing data back and forth between layers, each one should have a specific purpose, like to be received or returned by a specific method in your service layer.

public class RegisterUserDto()
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

public class RegisterUserResultDto()
{
    public int? NewUserId { get; set; }
}

视图模型

下面是其中仅住在我的 UI层视图模型。这是具体到一个单一视图和你的服务层内从未接触过!您可以使用此为映射被调回到控制器的价值观,但你不必 - 你可以有一个全新的模式专门用于此目的。

View Models

Here's a view model which lives in my UI layer only. It is specific to a single view and is never touched within your service layer! You can use this for mapping the values that are posted back to your controller, but you don't have to - you could have a whole new model specifically for this purpose.

public class RegistrationViewModel()
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

服务层

这里的code为服务层。我有一个使用在域模型以重新present的数据的DbContext的一个实例。我映射注册响应到 DTO 我的 RegisterUser()方法的响应创建的。

Service Layer

Here's the code for the service layer. I have an instance of the DbContext which uses the Domain Models to represent the data. I map the response of the registration into a DTO that I created specifically for the response of the RegisterUser() method.

public interface IRegistrationService
{
    RegisterUserResultDto RegisterUser(RegisterUserDto registerUserDto);
}

public class RegistrationService : IRegistrationService
{
    public IDbContext DbContext;

    public RegistrationService(IDbContext dbContext)
    {
        // Assign instance of the DbContext
        this.DbContext = dbContext;
    }        

    // This method receives a DTO with all of the data required for the method, which is supposed to register the user
    public RegisterUserResultDto RegisterUser(RegisterUserDto registerUserDto)
    {
        // Map the DTO object ready for the data access layer (domain)
        var user = new User()
                   {
                       UserName = registerUserDto.UserName,
                       Password = registerUserDto.Password,
                       Email = registerUserDto.Email,
                       Phone = registerUserDto.Phone
                   };

        // Register the user, pass the domain object to your DbContext
        // You could pass this up to your Data Access LAYER if you wanted to, to further separate your concerns, but I tend to use a DbContext
        this.DbContext.EntitySet<User>.Add(user);
        this.DbContext.SaveChanges();

       // Now return the response DTO back
       var registerUserResultDto = RegisterUserResultDto()
       {
            // User ID generated when Entity Framework saved the `User` object to the database
            NewUserId = user.Id
       };

       return registerUserResultDto;
    }
}

控制器

在控制器中,我们映射DTO发送到服务层,作为回报,我们收到DTO回来。

Controller

In the controller we map a DTO to send up to the service layer and in return we receive a DTO back.

public class HomeController : Controller
{
    private IRegistrationService RegistrationService;

    public HomeController(IRegistrationService registrationService)
    {
        // Assign instance of my service
        this.RegistrationService = registrationService;
    }

    [HttpGet]
    public ActionResult Index()
    {
        // Create blank view model to pass to the view
        return View(new RegistrationViewModel());
    }

    [HttpPost]
    public ActionResult Index(RegistrationViewModel requestModel)
    {
        // Map the view model to the DTO, ready to be passed to service layer
        var registerUserDto = new RegisterUserDto()
        {
            UserName = requestModel.UserName,
            Password = requestModel.Password,
            Email = requestModel.Email,
            Phone = requestModel.Phone
        }

        // Process the information posted to the view
        var registerUserResultDto = this.RegistrationService.RegisterUser(registerUserDto);

        // Check for registration result
        if (registerUserResultDto.Id.HasValue)
        {
            // Send to another page?
            return RedirectToAction("Welcome", "Dashboard");
        }

        // Return view model back, or map to another view model if required?
        return View(requestModel);
    }
}

查看

@model RegistrationViewModel
@{
    ViewBag.Layout = ~"Views/Home/Registration.cshtml"
}

<h1>Registration Page</h1>
<p>Please fill in the fields to register and click submit</p>

@using (Html.BeginForm())
{

    @Html.LabelFor(x => x.UserName)
    @Html.TextBoxFor(x => x.UserName)

    @Html.LabelFor(x => x.Password)
    @Html.PasswordFor(x => x.Password)

    @Html.LabelFor(x => x.Email)
    @Html.TextBoxFor(x => x.Email)

    @Html.LabelFor(x => x.Phone)
    @Html.TextBoxFor(x => x.Phone)

    <input type="submit" value="submit" />
}

复制的code

你是什么,你在评论中说得很对,有目标code重复位(或很多),但如果你仔细想想,你需要,如果你想这样做要真正将它们分开了:

查看模型!=域模型

在很多情况下,你在一个视图中显示的信息并不只从单一的域模型包含的信息和一些信息不应该让它到您的 UI层,因为它不应该被显示给用户的应用程序 - 比如用户密码的哈希值

In many cases the information you display on a view doesn't contain information from only a single domain model and some of the information should never make it down to your UI Layer because it should never be displayed to the application user - such as the hash of a user's password.

在原来的例子中,你有模型 GuestResponse 与属性验证,装饰等领域。如果你做了 GuestResponse 对象一倍,作为一个域模型视图模型,你有你的污染域模型与属性,这些属性可能仅适用于你的 UI层,甚至单页!

In your original example you have the model GuestResponse with validation attributes decorating the fields. If you made your GuestResponse object double up as a Domain Model and View Model, you have polluted your domain models with attributes that may only be relevant to your UI Layer or even a single page!

如果您还没有量身定制的DTO为您服务层方法,那么当你添加一个新的领域,以什么类是,该方法返回时,你会要更新所有的返回特定类包括资料片过其他方法。机会是,你增加一个新的领域仅在您要更新从它返回一个单一的方法或相关计算,你会打一个点?有一个1:为DTO和服务方式1的关系使更新是一件轻而易举的,你不必担心使用相同的DTO类中的其他方法

If you don't have tailored DTOs for your service layer methods, then when you add a new field to whatever class it is that the method returns, you'll have to update all of the other methods that return that particular class to include that piece of information too. Chances are you'll hit a point where you add a new field is only relevant or calculated in the one single method you're updating to returning it from? Having a 1:1 relationship for DTO and service methods makes updating it a breeze, you don't have to worry about other methods that use the same DTO class.

另外,如果你想想看,有一个单一的目的类(DTO),特别是写入回从业务层上的方法返回的具体信息,你可以一眼返回类和理解的究竟它是什么,它​​会返回。而如果你只是在符合该法案像这在数据库表中的一个行重新presents全部都在同一您的域模型对象塞子,您不知道哪些信息是相关的特定方法和你可能带回的,你并不需要的信息片段。

Also, if you think about it, having a single purpose class (DTO) especially written to return specific pieces of information back from a method on your service layer, you can glance at the returning class and understand exactly what it is that it is going to return. Whereas if you just bung in an object that 'fits the bill' like one your domain models which represents EVERYTHING in a row of one of your database tables, you don't know which pieces of information are relevant for that particular method and you're probably bringing back pieces of information that you don't need.

如果您使用的是域模型为你的视图模型 的,如果你'重不小心,你可以给自己开的攻击。如果有人谁使用你的应用程序在猜测你的类的附加字段的名称,即使你没有在视图它提供了一个表单元素任何人都可以张贴价值并将其保存到数据库中。有一个视图模型,只有已领域为您量身定制的特定视图意味着你可以限制哪些将在服务器端没有任何特殊的jiggery pokery处理。哦,你能看到什么会从你的观点,而不检查该视图本身被退回。任何视图模型共享确实使事情变得混乱,当你试图找出是什么,是不应该被显示,或从一个视图回发。

If you use a Domain Model as your View Model, if you're not careful you can leave yourself open to overposting attacks. If someone who uses your application guesses the name of an additional field on your class, even if you don't provide a form element for it in the view anyone can post that value and have it save to the database. Having a View Model that only has fields tailored to your specific view means that you can restrict what will be processed on the server side without any special jiggery pokery. Oh, and you can see exactly what will be returned from your view without examining the view itself. Any view model sharing really makes things confusing when you're trying to work out what is and isn't supposed to be displayed or posted back from a view.

有其他原因的负载,我可以去整天在这个话题又好像。 :P

There are loads of other reasons, I could go on all day on this topic it would seem. :P.

我希望有助于澄清一些事情。当然这一切都是为辩论,我对此表示欢迎!

I hope that helps to clear up a few things. Naturally this is all up for debate and I welcome it!

这篇关于如何彻底脱钩MVC中的视图和模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-14 19:31