2011-07-08 7 views
4

質問をして回答を受け取るシステムを構築しています。それぞれの質問には、それぞれ独自のタイプの質問があります。今のところStringDateTimeに制限しましょう。ドメインでは、質問は次のように表される:MVC3 Editor動的プロパティ(または回避策が必要)

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

    public string Caption 
    { 
     get; 
     set; 
    } 

    public AnswerType 
    { 
     get; 
     set; 
    } 
} 

AnswerType

enum AnswerType 
{ 
    String, 
    DateTime 
} 

ここで実際に私がはるかに回答タイプを持っていることに注意してください。

MVCモデルを作成し、質問から派生し、回答プロパティを追加するアイデアを思いつきました。だからこのようなものでなければなりません:

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

ここで問題を開始してください。 Stringについて

@model QuestionWithAnswer<dynamic> 

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

DateTimeのために、私は私自身のビューを定義するつもりです、私はここに簡単な入力を持つようにしたい:それはそのような何かにする必要があるので、私は、どんな質問を描画するための一般的な見解を持っていると思います。コントローラから具体的なモデルを渡すことができます。しかし、レンダリング段階では当然、最初はnull(デフォルトはString)であるため、特にStringの場合は何も描画せず、DateTimeの場合はすべてのプロパティの入力を描画しません。

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

答えて

1

Html.EditorFor(..)は引き続き使用できますが、エディタテンプレートの名前である2番目のパラメータを指定できます。あなたは何かなどを行うことができますので、AnswerTypeある質問オブジェクトのプロパティを持っている...あなたのEditorTemplatesフォルダ内の

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

ザ・ちょうどAnswerTypesのそれぞれのビューを定義します。 "String"、 "DateTime"など

EDIT:AnswerオブジェクトがStringの場合は、プレースホルダオブジェクトをそこに置くので、 "String"エディタテンプレートのモデルはnullではありません。私は.NET型システムを使用して好む

enum AnswerType 
{ 
    String, 
    DateTime 
} 

+0

ハ:Application_Startに登録されます

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; } } 

!どのように私はこの過負荷を見逃すことができますか?完璧に近づいている、ありがとう!答えを受け入れる: –

5

個人的に私はこれが好きではありません。代替設計をお勧めします。いつものように、我々は、ビューモデルを定義することによって開始します。その後、

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) 

と最後のピースは、私たちの答えのためのカスタムモデルバインダーである:

ModelBinders.Binders.Add(typeof(AnswerViewModel), new AnswerModelBinder()); 
+0

'enum AnswerType { String、 DateTime }'これはデータベースからのものです。しかし、私はそれをあなたの構造にマップすることができます。しかし、@tkerwoodの最初の答えは私の必要に応えているようです。私はそれを評価し、そこにいくつかの欠点があると、あなたはあなたに戻ってきます。ありがとう! –

+0

@Michael Sagalovich、私が示したことは、ビューモデルの使用です。これはあなたが常に使うべきものです。あなたのデータベースやドメインエンティティに何があるかはあまり重要ではありません。あなたが理解する必要があるのは、常にビューモデルで作業し、コントローラアクションがビューモデルを渡して受け取るようにすることです。既存のドメインクラスを再利用したい場合は、ドメインモデルとビューモデルのマッピングをコントローラーで行うことができます。しかし、あなたの意見にそれらのクラスを渡さないでください。 –

+0

ありがとう、偉大な教祖:)正直言って、私はあなたが言っていることに反対する意見をたくさん持ち、ビュー上でドメインモデルを使うことを賛否するが、ここではそれは問題ではない。 –