2012-09-07 21 views
1

我有一個示例項目,一個動態調查問卷系統,任何管理員都可以創建問卷,然後向其添加問題組並向每個問題組添加問題。實體框架POCO到MVC中的ViewModel 3

採取以下組波蘇斯的是彌補我的EF數據對象的實體:

public class Questionnaire 
{ 
    public virtual int Id { get; set; } 
    public virtual string QuestionnaireName { get; set; } 
    public virtual IList<QuestionGroup> QuestionGroups { get; set; } 
} 

public class QuestionGroup 
{ 
    public virtual int Id { get; set; } 
    public virtual string GroupName { get; set; } 
    public virtual int QuestionnaireId { get; set; } 
    public virtual IList<Question> Questions { get; set; } 
} 

public class Question 
{ 
    public virtual int Id { get; set; } 
    public virtual string QuestionText { get; set; } 
    public virtual int QuestionGroupId { get; set; } 
    public virtual QuestionGroup QuestionGroup { get; set; } 
} 

我訪問這些金融機構在通過WCF數據服務我的Web UI,我想知道什麼是最好的做法(或至少一個更清潔的方式)處理在我看來這些實體的輸入。以下是我所能克服的一些想法,但我很難喜歡它們中的任何一個,因爲它們只是感覺錯綜複雜。

解決方案1 ​​

一個屬性添加到我的Question實體,稱爲SubmittedValue,並有我的EF數據上下文Ignore(m => m.SubmittedValue)這一點。此屬性是我將用於在視圖級別保留Question的輸入值。

我不喜歡這件事,它讓我的POCO實體膨脹得幾乎不相關的屬性 - 我只在一個Web UI中使用SubmittedValue,而我的POCO實體會在其他地方多次使用。

解決方案2

創建具有相同的結構,我的波蘇斯視圖模型對象,讓我們稱他們爲QuestionnaireModelQuestionGroupModelQuestionModel - 這些都是在我的控制器初始化,屬性是從POCO複製到視圖模型。在QuestionModel我添加了我的SubmittedValue屬性,並使用自定義模型綁定器來查看綁定上下文並從視圖中獲取我的值 - 其中名稱看起來像[group.question.1](其中1是Id的Id問題)。這是在視圖中爲每個問題組和每個問題使用編輯模板呈現的。

我不喜歡這件事,是膨脹我的Web UI與這些額外的視圖模型對象,並不得不手動複製屬性值從我的POCO到視圖模型。我知道我可以使用像AutoMapper這樣的東西來爲我做這件事,但這只是自動化這項工作,我理想中完全不想這樣做。

溶液3

更改溶液2性能稍微,以代替致以波蘇斯並重寫與其它視圖模型對象virtual集合屬性。所以,我的觀點模型是這樣的:

public class QuestionnaireModel : Questionnaire 
{ 
    public new IList<QuestionGroupModel> QuestionGroups { get; set; } 
} 

public class QuestionGroupModel : QuestionGroup 
{ 
    public new IList<Question> Questions { get; set; } 
} 

public class QuestionModel : Question 
{ 
    public string SubmittedValue { get; set; } 
} 

我喜歡這個想法是最好的,但我沒有真正嘗試過這個呢。我在這裏得到兩全其美的好處1.我可以讓我的POCO遠離我的觀點,並且2.我將這個一次性使用屬性SubmittedValue排除在我的業務層之外。

你們有沒有更好的方法來處理這個問題?

回答

1

已經有解決方案3(這是我的首選解決方案)我已經成功地終於得到它玩耍了。對於在這個問題上磕磕絆絆的人,這就是我正在做的事。首先,我創建了擴展我的POCO實體的視圖模型。我使用new實現覆蓋集合屬性,以使我的集合成爲我的視圖模型類型。然後,我將表單持久性屬性添加到我的Question視圖模型中(這樣我就可以將其保留在業務層之外)。

public class QuestionnaireModel : Questionnaire 
{ 
    public new IList<QuestionGroupModel> QuestionGroups { get; set; } 
} 

public class QuestionGroupModel : QuestionGroup 
{ 
    public new IList<QuestionModel> Questions { get; set; } 
} 

public class QuestionModel : Question 
{ 
    public string SubmittedValue { get; set; } 
} 

使用AutoMapper創建我的波蘇斯和視圖模型之間的映射,像這樣(使用.AfterMap(),以確保我的堅持屬性不爲空,但空字符串代替):

Mapper.CreateMap<Questionnaire, QuestionnaireModel>(); 
Mapper.CreateMap<QuestionGroup, QuestionGroupModel>(); 
Mapper.CreateMap<Question, QuestionModel>().AfterMap((s, d) => d.SubmittedValue = ""); 

下一頁中,每個Question具有具有單個輸入元件的編輯模板:

@Html.Raw(string.Format("<input type=\"text\" name=\"group.question.{0}\" value=\"{1}\" />", Model.Id.ToString(), Model.SubmittedValue) 

最後,我拿起(和持續)值,這些S使用自定義的模型綁定,就像這樣:

int id = Int32.Parse(controllerContext.RouteData.Values["id"].ToString()); 

var questionnaire = _proxy.Questionnaires 
    .Expand("QuestionGroups") 
    .Expand("QuestionGroups/Questions") 
    .Where(q => q.Id == id) 
    .FirstOrDefault(); 

var model = Mapper.Map<Questionnaire, QuestionnaireModel>(questionnaire); 

foreach (var group in model.QuestionGroups) 
{ 
    foreach (var question in group.Questions) 
    { 
     string inputValueId = "group.question." + question.Id.ToString(); 
     string value = bindingContext.ValueProvider.GetValue(inputValueId).AttemptedValue; 

     question.SubmittedValue = value; 
    } 
} 

雖然我不是太快樂關於自定義模型粘合劑(我不認爲我正確設定我的編輯模板,以便訴諸自定義綁定)對我來說這是首選的解決方案。

+3

請謹慎編寫這樣的編輯器模板,因爲它是潛在的腳本注入威脅,因此不建議生成這樣的html控件。爲什麼不簡單地將'@ model'設置爲'QuestionModel',然後在編輯器模板中使用'Html.TextBoxFor(x => x)'helper?我能夠解決這樣的類似問題,並通過將後置動作的參數設置爲'List 模型,即使擺脫了定製模型聯編程序,但在控制器中具有邏輯。只是一些提示... – thmshd

2

IMO解決方案2是前進的方向,因爲您經常會發現EF POCO和ViewModels需要在解決不同問題時發生分歧。

例如像你說的會導致一個膨脹可能關心的是裝飾與表示層註解(UIHintsValidationAttributes等)

解決方案1您的ViewModels,你可能會倒閉引用System.Data.Annotations(大概OK),但你可以如果您需要,還可以參考System.Data.MVC [HiddenInput]

IMO解決方案3最終比新的ViewModel更加努力 - 例如儘管MetadataType允許您將屬性「轉移」到具有相似屬性的另一個類,但這是非常費勁的工作。

例如與解決方案3你可能會風與

namespace EFPocos 
{ 
    /// <summary> 
    /// Your EF POCO 
    /// </summary> 
    public class Question 
    { 
     public virtual int Id { get; set; } 
     public virtual string QuestionText { get; set; } 
     public virtual int QuestionGroupId { get; set; } 
    } 
} 

namespace UIViewModels 
{ 
    /// <summary> 
    /// Your ViewModel 'derivative', but sans Annotation decoration 
    /// </summary> 
    [MetadataType(typeof(QuestionUIMetaData))] 
    public class QuestionViewModel : EFPocos.Question, IValidatableObject 
    { 
     public string SubmittedValue { get; set; } 

     #region IValidatableObject Members 

     public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
     { 
      if (Id % 2 == 0) 
      { 
       yield return new ValidationResult("Some rule has fired"); 
      } 
     } 

     #endregion 
    } 

    /// <summary> 
    /// Annotations go here ... and we may as well just AutoMapped a simple ViewModel 
    /// </summary> 
    public class QuestionUIMetaData 
    { 
     [HiddenInput] 
     public int Id { get; set; } 
     [Required()] 
     public string QuestionText { get; set; } 
     [Required()] 
     [DisplayName("Select Group ...")] 
     public int QuestionGroupId { get; set; } 
     [DisplayName("Question is Here")] 
     [StringLength(50, ErrorMessage = "Too Long!!")] 
     public string SubmittedValue { get; set; } 
    } 
} 
+2

感謝您的回覆,我認爲我的解決方案2正在朝着正確的方向前進,但目前我正在使用automapper解決方案3並獲得一些有希望的結果。如果我得到我想要的位置,我會發布答案 - 否則我會回到解決方案2. –