C# mvc3中实体框架POCO到ViewModel
我有一个示例项目,一个动态问卷系统,任何管理员都可以创建一个问卷,然后向其中添加一组问题,然后再向每个问题组添加问题 以构成my EF数据上下文实体的以下POCO组为例:C# mvc3中实体框架POCO到ViewModel,c#,asp.net-mvc-3,viewmodel,wcf-data-services,C#,Asp.net Mvc 3,Viewmodel,Wcf Data Services,我有一个示例项目,一个动态问卷系统,任何管理员都可以创建一个问卷,然后向其中添加一组问题,然后再向每个问题组添加问题 以构成my EF数据上下文实体的以下POCO组为例: public class Questionnaire { public virtual int Id { get; set; } public virtual string QuestionnaireName { get; set; } public virtual IList<QuestionG
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; }
}
我最喜欢这个主意,但我还没有真正尝试过。我在这里得到了最好的两个世界作为1。我可以让我的POCO远离我的观点和2。我将一次性使用属性SubmittedValue
保留在我的业务层之外
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; }
}
你们中有谁有更好的方法来处理这个问题吗?IMO解决方案2是正确的前进方向,因为您经常会发现EF POCO和VIEWMODEL需要分歧,因为它们解决了不同的问题 e、 g.一个可能的问题是使用表示层注释装饰ViewModels(
UIHitts
,ValidationAttribute
等)
正如您所说的解决方案1将导致膨胀,您可能会引用System.Data.Annotations(可能是OK),但如果您需要[HiddenInput]
等,您也可以引用System.Data.MVC
IMO解决方案3比一个新的ViewModel更费劲-例如,虽然允许您将属性“转移”到另一个具有类似属性的类上,但这是一个非常费劲的工作
e、 g.使用解决方案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; }
}
}
名称空间EFPocos
{
///
///你的EF POCO
///
公开课问题
{
公共虚拟整数Id{get;set;}
公共虚拟字符串QuestionText{get;set;}
公共虚拟组ID{get;set;}
}
}
命名空间UIViewModels
{
///
///您的ViewModel“衍生”,但无注释装饰
///
[元数据类型(typeof(QuestionUIMetaData))]
公共类QuestionViewModel:EFPocos.Question,IValidatableObject
{
公共字符串SubmittedValue{get;set;}
#表对象成员所在的区域
公共IEnumerable验证(ValidationContext ValidationContext)
{
如果(Id%2==0)
{
返回新的ValidationResult(“某些规则已触发”);
}
}
#端区
}
///
///注释在这里…我们也可以自动映射一个简单的ViewModel
///
公共类问题元数据
{
[HiddenInput]
公共int Id{get;set;}
[必需()]
公共字符串QuestionText{get;set;}
[必需()]
[显示名称(“选择组…”)]
public int QuestionGroupId{get;set;}
[DisplayName(“问题在这里”)]
[StringLength(50,ErrorMessage=“太长!!”)
公共字符串SubmittedValue{get;set;}
}
}
在使用解决方案3(这是我的首选解决方案)之后,我终于找到了它。这是我正在做的,为任何一个在这个问题上绊倒的人。首先,我创建扩展POCO实体的视图模型。我使用new
实现覆盖集合属性,以使我的集合成为我的视图模型类型。然后,我将表单持久性属性添加到我的Question
视图模型中(这样我就可以将它排除在我的业务层之外)
接下来,每个问题
都有一个编辑器模板,其中包含一个输入元素:
@Html.Raw(string.Format("<input type=\"text\" name=\"group.question.{0}\" value=\"{1}\" />", Model.Id.ToString(), Model.SubmittedValue)
@Html.Raw(string.Format(“”,Model.Id.ToString(),Model.SubmittedValue)
最后,我使用自定义模型绑定器拾取(并保存)这些值,如下所示:
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;
}
}
intid=Int32.Parse(controllerContext.RouteData.Values[“id”].ToString());
var问卷=_proxy.investments
.展开(“问题组”)
.展开(“问题组/问题”)
.其中(q=>q.Id==Id)
.FirstOrDefault();
var模型=Mapper.Map(问卷);
foreach(模型中的var组。问题组)
{
foreach(组中的var问题。问题)
{
字符串inputValueId=“group.question.”+question.Id.ToString();
字符串值=bindingContext.ValueProvider.GetValue(inputValueId).AttemptedValue;
问题。提交的价值=价值;
}
}
尽管我对自定义模型活页夹不太满意(我认为我没有正确设置编辑器模板,所以求助于自定义活页夹)对我来说,这是首选的解决方案。感谢您的回复,我认为我使用解决方案2的方向是正确的,但我目前正在使用automapper使用解决方案3,并获得了一些有希望的结果。如果我达到了我想要的位置,我会发布答案,否则我会返回到解决方案2。请小心编写Editor像您展示的这样的模板,它是一个潜在的脚本注入威胁,不建议生成这样的html控件。为什么不简单地将
@model
设置为QuestionModel
,然后使用html.TextBoxFor(x=>x)
helper在编辑器模板中?我可以解决类似的问题,通过将post action的参数设置为List model
甚至可以摆脱自定义模型绑定器,但控制器中有逻辑。只是一些提示。。。
@Html.Raw(string.Format("<input type=\"text\" name=\"group.question.{0}\" value=\"{1}\" />", Model.Id.ToString(), Model.SubmittedValue)
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;
}
}