4

質問をして回答を受け取るシステムを構築しています。各質問には、独自のタイプの回答者を含めることができます。String今のところとに限定しましょうDateTime。ドメインでは、質問は次のように表されます。

public class Question
{
    public int Id
    {
        get;
        set;
    }

    public string Caption
    {
        get;
        set;
    }

    public AnswerType
    {
        get;
        set;
    }
}

、どこAnswerTypeですか

enum AnswerType
{
    String,
    DateTime
}

実際にはもっと多くの回答タイプがあることに注意してください。

Question から派生させて Answer プロパティを追加する MVC モデルを作成するというアイデアを思いつきました。したがって、次のようにする必要があります。

public class QuestionWithAnswer<TAnswer> : Question
{
    public TAnswer Answer
    {
        get;
        set;
    }
}

そして、ここから問題を開始します。質問を描画するための一般的なビューが必要なので、次のようなものにする必要があります。

@model QuestionWithAnswer<dynamic>

<span>@Model.Caption</span>
@Html.EditorFor(m => m.Answer)

ここStringでは簡単な入力を行いたいのでDateTime、独自のビューを定義します。コントローラーから具体的なモデルを渡すことができます。しかし、問題は、レンダリング段階では、当然のことながら、特に Answer のタイプを特定できないことです。特に初期の場合null( のデフォルトString)、EditorForに対して何も描画せずString、 のすべてのプロパティに入力しますDateTime

問題の性質は理解していますが、エレガントな回避策はありますか? または、コントロールの種類に基づいてエディター ビュー名を選択するための独自のロジックを実装する必要があります (大きな醜いswitch)?

4

2 に答える 2

6

個人的に私はこれが好きではありません:

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());
于 2011-07-08T11:33:22.557 に答える
1

Html.EditorFor(..) は引き続き使用できますが、エディター テンプレートの名前である 2 番目のパラメーターを指定します。AnswerType である Question オブジェクトにプロパティがあるため、次のようなことができます...

@Html.EditorFor(m => m.Answer, @Model.AnswerType)

EditorTemplates フォルダー内の は、各 AnswerType のビューを定義するだけです。つまり、「String」、「DateTime」などです。

編集:Answerオブジェクトが文字列に対してnullである限り、「文字列」エディターテンプレートのモデルがnullにならないように、プレースホルダーオブジェクトをそこに配置します。

于 2011-07-08T11:25:47.640 に答える