C# mvc3中实体框架POCO到ViewModel

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

我有一个示例项目,一个动态问卷系统,任何管理员都可以创建一个问卷,然后向其中添加一组问题,然后再向每个问题组添加问题

以构成my EF数据上下文实体的以下POCO组为例:

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