在asp.net mvc的多步骤的注册过程中的问题(分裂的ViewModels,单模型) [英] multi-step registration process issues in asp.net mvc (splitted viewmodels, single model)

查看:87
本文介绍了在asp.net mvc的多步骤的注册过程中的问题(分裂的ViewModels,单模型)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个多步骤的注册程序,由单个对象的领域层,已验证规则的属性中定义的支持。

I have a multi-step registration process, backed by a single object in domain layer, which have validation rules defined on properties.

我应该如何验证域对象域时在许多意见分裂,
我已经发布时,部分保存对象在第一个看法呢?

How Should I validate the domain object when the domain is split across many views, and I have to save the object partially in the first view when posted?

我想过使用会话,但是这是不可能的原因过程是漫长和数据量很高,所以我不想使用会话。

I thought about using Sessions but That's not possible cause the process is lengthy and amount of data is high, So I don't want to use session.

我想过保存所有数据的关系内存数据库(具有相同的架构为主要分贝),然后刷新该数据传送到主数据库,但是问题出现了,因为我应该服务之间的航线(在视图中的要求)谁与主db和内存中的分贝工作

I thought about saving all the data in an relational in-memory db (with the same schema as main db)and then flushing that data to main db but issues arisen cause I should route between services (requested in the views) who work with the main db and in-memory db.

我在找一个优雅干净的解决方案(更多precisely最佳实践)。

I 'm looking for an elegant and clean solution (more precisely a best practice).

更新和澄清:

@Darin谢谢您的回复周到,
这是我做了什么至今。
不过顺便说一句,我得到了它有它的许多附件的要求,我设计了一个 Step2View 例如该用户可以在它异步上传文件,
但这些附件应参照关系应已在 Step1View 之前保存的另一个表保存在一个表中。

@Darin Thank you for your thoughtful reply, That was exactly what I've done till now. But incidentally I've got a request which have many attachments in it, I design a Step2View e.g. which user can upload documents in it asynchronously , but those attachments should be saved in a table with referential relation to another table that should have been saved before in Step1View.

因此​​,我要保存第一步(部分)域对象,但我不能,
导致这部分映射到第一步的视图模型备份核心Domain对象不能保存,而不是来自转换 Step2ViewModel

Thus I should save the domain object in Step1 (partially), But I can't, cause the backed Core Domain object which is mapped partially to a Step1's ViewModel can't be saved without props that come from converted Step2ViewModel.

推荐答案

首先,你不应该使用在您的任何意见的领域对象。你应该使用视图模型。每个视图模式将只包含由给定的视图作为验证属性特定于该给定的视图需要,以及属性。所以,如果你有3个步骤向导,这意味着你将有3视图模型,每一个步:

First you shouldn't be using any domain objects in your views. You should be using view models. Each view model will contain only the properties that are required by the given view as well as the validation attributes specific to this given view. So if you have 3 steps wizard this means that you will have 3 view models, one for each step:

public class Step1ViewModel
{
    [Required]
    public string SomeProperty { get; set; }

    ...
}

public class Step2ViewModel
{
    [Required]
    public string SomeOtherProperty { get; set; }

    ...
}

和等。所有这些视图模型可以由一个主向导视图模型进行备份:

and so on. All those view models could be backed by a main wizard view model:

public class WizardViewModel
{
    public Step1ViewModel Step1 { get; set; }
    public Step2ViewModel Step2 { get; set; }
    ...
}

然后你可以有控制器动作渲染向导过程的每个步骤,并通过主 WizardViewModel 到视图。当你在控制器内采取行动的第一步,你可以初始化第一步属性。那么视图中,你会生成表单,允许用户填写有关步骤1的属性当表单提交的控制器操作将应用验证规则的步骤1只:

then you could have controller actions rendering each step of the wizard process and passing the main WizardViewModel to the view. When you are on the first step inside the controller action you could initialize the Step1 property. Then inside the view you would generate the form allowing the user to fill the properties about step 1. When the form is submitted the controller action will apply the validation rules for step 1 only:

[HttpPost]
public ActionResult Step1(Step1ViewModel step1)
{
    var model = new WizardViewModel 
    {
        Step1 = step1
    };

    if (!ModelState.IsValid)
    {
        return View(model);
    }
    return View("Step2", model);
}

现在的第2步视图中,你可以使用Html.Serialize从MVC期货订单(如果你希望一个排序的ViewState)的序列化步骤1中一个隐藏字段的形式里面帮手

Now inside the step 2 view you could use the Html.Serialize helper from MVC futures in order to serialize step 1 into a hidden field inside the form (sort of a ViewState if you wish):

@using (Html.BeginForm("Step2", "Wizard"))
{
    @Html.Serialize("Step1", Model.Step1)
    @Html.EditorFor(x => x.Step2)
    ...
}

和第二步的POST操作里面:

and inside the POST action of step2:

[HttpPost]
public ActionResult Step2(Step2ViewModel step2, [Deserialize] Step1ViewModel step1)
{
    var model = new WizardViewModel 
    {
        Step1 = step1,
        Step2 = step2
    }

    if (!ModelState.IsValid)
    {
        return View(model);
    }
    return View("Step3", model);
}

依此类推,直到你到最后一步,你将有充满 WizardViewModel 所有数据。然后,你将视图模型映射到您的域模型,并把它传递给处理服务层。服务层可能进行任何验证规则本身等等...

And so on until you get to the last step where you will have the WizardViewModel filled with all the data. Then you will map the view model to your domain model and pass it to the service layer for processing. The service layer might perform any validation rules itself and so on ...

还有另一种选择:使用JavaScript和把所有在同一页上。有许多<一个href=\"http://www.google.com/#hl=en&sugexp=ldymls&xhr=t&q=jquery+wizard+plugin&cp=14&pf=p&sclient=psy&safe=off&source=hp&aq=0&aqi=&aql=&oq=jquery+wizard+&pbx=1&bav=on.2,or.r_gc.r_pw.&fp=47736a996907bd1&biw=1440&bih=795\">jquery插件在那里,提供向导功能( Stepy 是一个很好的一个)。它基本上呈现和客户端在这种情况下,您不再需要担心的步骤之间持续存在的状态上隐藏的div的问题。

There is also another alternative: using javascript and putting all on the same page. There are many jquery plugins out there that provide wizard functionality (Stepy is a nice one). It's basically a matter of showing and hiding divs on the client in which case you no longer need to worry about persisting state between the steps.

但是不管你选择什么样的解决方案,始终使用视图模型,并在这些视图模型进行验证。只要你坚持数据注解验证你的域模型的属性,你将很难很难为域模型并不适用于意见。

But no matter what solution you choose always use view models and perform the validation on those view models. As long you are sticking data annotation validation attributes on your domain models you will struggle very hard as domain models are not adapted to views.

更新:

确定,由于众多的意见,我得出结论,我的答案是不明确的。而且我必须一致。所以,让我尝试进一步阐述我的例子。

OK, due to the numerous comments I draw the conclusion that my answer was not clear. And I must agree. So let me try to further elaborate my example.

我们可以定义所有的步骤视图模型应该实现一个接口(它只是一个标记接口):

We could define an interface which all step view models should implement (it's just a marker interface):

public interface IStepViewModel
{
}

那么我们将作为相关验证属性定义为向导的3个步骤,其中每一步当然会只包含它需要以及属性:

then we would define 3 steps for the wizard where each step would of course contain only the properties that it requires as well as the relevant validation attributes:

[Serializable]
public class Step1ViewModel: IStepViewModel
{
    [Required]
    public string Foo { get; set; }
}

[Serializable]
public class Step2ViewModel : IStepViewModel
{
    public string Bar { get; set; }
}

[Serializable]
public class Step3ViewModel : IStepViewModel
{
    [Required]
    public string Baz { get; set; }
}

下我们定义它由步骤列表和当前步骤索引的主向导视图模型

next we define the main wizard view model which consists of a list of steps and a current step index:

[Serializable]
public class WizardViewModel
{
    public int CurrentStepIndex { get; set; }
    public IList<IStepViewModel> Steps { get; set; }

    public void Initialize()
    {
        Steps = typeof(IStepViewModel)
            .Assembly
            .GetTypes()
            .Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t))
            .Select(t => (IStepViewModel)Activator.CreateInstance(t))
            .ToList();
    }
}

然后,我们进入到控制器:

Then we move on to the controller:

public class WizardController : Controller
{
    public ActionResult Index()
    {
        var wizard = new WizardViewModel();
        wizard.Initialize();
        return View(wizard);
    }

    [HttpPost]
    public ActionResult Index(
        [Deserialize] WizardViewModel wizard, 
        IStepViewModel step
    )
    {
        wizard.Steps[wizard.CurrentStepIndex] = step;
        if (ModelState.IsValid)
        {
            if (!string.IsNullOrEmpty(Request["next"]))
            {
                wizard.CurrentStepIndex++;
            }
            else if (!string.IsNullOrEmpty(Request["prev"]))
            {
                wizard.CurrentStepIndex--;
            }
            else
            {
                // TODO: we have finished: all the step partial
                // view models have passed validation => map them
                // back to the domain model and do some processing with
                // the results

                return Content("thanks for filling this form", "text/plain");
            }
        }
        else if (!string.IsNullOrEmpty(Request["prev"]))
        {
            // Even if validation failed we allow the user to
            // navigate to previous steps
            wizard.CurrentStepIndex--;
        }
        return View(wizard);
    }
}

这个控制器的言论夫妇:

Couple of remarks about this controller:


  • 指数POST操作使用 [反序列化] 从Microsoft期货库属性所以一定要确保你已经安装了 MvcContrib 的NuGet。这就是为什么视图模型应该与 [Serializable接口] 属性来修饰的原因

  • 指数POST操作采用作为参数的 IStepViewModel 接口,所以这个是有道理的,我们需要一个自定义模型粘合剂。

  • The Index POST action uses the [Deserialize] attributes from the Microsoft Futures library so make sure you have installed the MvcContrib NuGet. That's the reason why view models should be decorated with the [Serializable] attribute
  • The Index POST action takes as argument an IStepViewModel interface so for this to make sense we need a custom model binder.

下面是相关的模型绑定:

Here's the associated model binder:

public class StepViewModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var stepTypeValue = bindingContext.ValueProvider.GetValue("StepType");
        var stepType = Type.GetType((string)stepTypeValue.ConvertTo(typeof(string)), true);
        var step = Activator.CreateInstance(stepType);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => step, stepType);
        return step;
    }
}

这粘结剂使用一个特殊的隐藏字段名为步骤类型将包含具体类型每一步,而我们将在每个发送请求。

This binder uses a special hidden field called StepType which will contain the concrete type of each step and which we will send on each request.

这个模型绑定将在的Application_Start 注册:

This model binder will be registered in Application_Start:

ModelBinders.Binders.Add(typeof(IStepViewModel), new StepViewModelBinder());

拼图的最后缺少的是一点意见。这里的主〜/查看/精灵/ Index.cshtml 查看:

@using Microsoft.Web.Mvc
@model WizardViewModel

@{
    var currentStep = Model.Steps[Model.CurrentStepIndex];
}

<h3>Step @(Model.CurrentStepIndex + 1) out of @Model.Steps.Count</h3>

@using (Html.BeginForm())
{
    @Html.Serialize("wizard", Model)

    @Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
    @Html.EditorFor(x => currentStep, null, "")

    if (Model.CurrentStepIndex > 0)
    {
        <input type="submit" value="Previous" name="prev" />
    }

    if (Model.CurrentStepIndex < Model.Steps.Count - 1)
    {
        <input type="submit" value="Next" name="next" />
    }
    else
    {
        <input type="submit" value="Finish" name="finish" />
    }
}

而这一切,你需要做这个工作。当然,如果你想,你可以个性化的外观和通过定义自定义编辑模板感到一些或所有步骤的向导。例如,让我们做它为第2步因此我们定义了一个〜/查看/精灵/ EditorTemplates / Step2ViewModel.cshtml 部分:

@model Step2ViewModel

Special Step 2
@Html.TextBoxFor(x => x.Bar)

这里的结构是怎样的样子:

Here's how the structure looks like:

当然有改进的余地。该指数POST操作看起来像s..t。有没有在太多code。进一步简化将涉及到所有的移动基础架构的东西,如指数,目前指数的管理,目前步入向导的复制,......到另一个模型粘合剂。让我们终于结束了:

Of course there is room for improvement. The Index POST action looks like s..t. There's too much code in it. A further simplification would involve into moving all the infrastructure stuff like index, current index management, copying of the current step into the wizard, ... into another model binder. So that finally we end up with:

[HttpPost]
public ActionResult Index(WizardViewModel wizard)
{
    if (ModelState.IsValid)
    {
        // TODO: we have finished: all the step partial
        // view models have passed validation => map them
        // back to the domain model and do some processing with
        // the results
        return Content("thanks for filling this form", "text/plain");
    }
    return View(wizard);
}

这是更POST动作应该怎么样子。我要离开这个改进下一次: - )

which is more how POST actions should look like. I am leaving this improvement for the next time :-)

这篇关于在asp.net mvc的多步骤的注册过程中的问题(分裂的ViewModels,单模型)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆