我正在建立一个系统,提出问题并收到答案。每个问题都可以有一个自己类型的aswer。我们暂时将其限制为String
和DateTime
。在Domain中,问题以下列方式表示:
public class Question
{
public int Id
{
get;
set;
}
public string Caption
{
get;
set;
}
public AnswerType
{
get;
set;
}
}
,其中AnswerType
是
enum AnswerType
{
String,
DateTime
}
请注意,实际上我有更多的答案类型。
我提出了创建MVC模型的想法,从问题派生并向其添加Answer属性。所以它必须是这样的:
public class QuestionWithAnswer<TAnswer> : Question
{
public TAnswer Answer
{
get;
set;
}
}
这里开始出现问题。我希望有一个通用的视图来绘制任何问题,所以它需要是这样的:
@model QuestionWithAnswer<dynamic>
<span>@Model.Caption</span>
@Html.EditorFor(m => m.Answer)
对于String
我希望在这里有简单的输入,对于DateTime
我将定义自己的视图。我可以从控制器传递具体模型。但问题是在渲染阶段,当然,它无法确定答案的类型,特别是如果它最初是null
(String
的默认值),那么EditorFor
不会为{ {1}}以及String
中所有属性的输入。
我确实理解问题的本质,但有没有优雅的解决方法?或者我必须实现我自己的逻辑,根据控件类型选择编辑器视图名称(大丑DateTime
)?
答案 0 :(得分:5)
我个人不喜欢这个:
enum AnswerType
{
String,
DateTime
}
我更喜欢使用.NET类型系统。让我建议你一个替代设计。一如既往,我们首先定义视图模型:
public abstract class AnswerViewModel
{
public string Type
{
get { return GetType().FullName; }
}
}
public class StringAnswer : AnswerViewModel
{
[Required]
public string Value { get; set; }
}
public class DateAnswer : AnswerViewModel
{
[Required]
public DateTime? Value { get; set; }
}
public class QuestionViewModel
{
public int Id { get; set; }
public string Caption { get; set; }
public AnswerViewModel Answer { get; set; }
}
然后是控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new QuestionViewModel
{
Id = 1,
Caption = "What is your favorite color?",
Answer = new StringAnswer()
},
new QuestionViewModel
{
Id = 1,
Caption = "What is your birth date?",
Answer = new DateAnswer()
},
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<QuestionViewModel> questions)
{
// process the answers. Thanks to our custom model binder
// (see below) here you will get the model properly populated
...
}
}
然后是主Index.cshtml
视图:
@model QuestionViewModel[]
@using (Html.BeginForm())
{
<ul>
@for (int i = 0; i < Model.Length; i++)
{
@Html.HiddenFor(x => x[i].Answer.Type)
@Html.HiddenFor(x => x[i].Id)
<li>
@Html.DisplayFor(x => x[i].Caption)
@Html.EditorFor(x => x[i].Answer)
</li>
}
</ul>
<input type="submit" value="OK" />
}
现在我们可以为我们的答案提供编辑器模板:
~/Views/Home/EditorTemplates/StringAnswer.cshtml
:
@model StringAnswer
<div>It's a string answer</div>
@Html.EditorFor(x => x.Value)
@Html.ValidationMessageFor(x => x.Value)
~/Views/Home/EditorTemplates/DateAnswer.cshtml
:
@model DateAnswer
<div>It's a date answer</div>
@Html.EditorFor(x => x.Value)
@Html.ValidationMessageFor(x => x.Value)
最后一块是我们答案的自定义模型绑定器:
public class AnswerModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
var type = Type.GetType(typeValue.AttemptedValue, true);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
将在Application_Start
注册:
ModelBinders.Binders.Add(typeof(AnswerViewModel), new AnswerModelBinder());
答案 1 :(得分:1)
您仍然可以使用Html.EditorFor(..),但指定第二个参数,它是编辑器模板的名称。您在Question对象上有一个属性,即AnswerType,因此您可以执行类似......
的操作@Html.EditorFor(m => m.Answer, @Model.AnswerType)
在EditorTemplates文件夹中,只为每个AnswerTypes定义一个视图。即“字符串”,“日期时间”等
编辑:对于String,Answer对象为null,我会在其中放置一个占位符对象,因此“String”编辑器模板中的模型不为空。