实体框架POCO到MVC 3中的ViewModel [英] Entity Framework POCO to ViewModel in MVC 3

查看:77
本文介绍了实体框架POCO到MVC 3中的ViewModel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an example project, a dynamic questionnaire system where any administrator can create a questionnaire, then add groups of questions to it and in-turn add questions to each question group.

采用构成我的EF数据上下文实体的以下POCO组:

Take the following group of POCOs that make up the entities for my EF data context:

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中访问这些实体,并想知道在我看来这些实体处理输入的最佳实践(或至少一种更简洁的方法).以下是我克服这些困难的一些想法,但是我很难喜欢其中任何一个,因为它们只是让人感到困惑而又令人费解.

I am accessing these entities in my Web UI via WCF Data Services and am wondering what a best practice (or at least a cleaner way) of handling input in my view for these entities. The following are some of the ideas I have of overcoming this, but I'm having a hard time liking any of them because they just feel a but convoluted.

解决方案1 ​​

向我的Question实体添加一个名为SubmittedValue的属性,并使我的EF数据上下文为Ignore(m => m.SubmittedValue).我将使用此属性在视图级别持久保存Question的输入值.

Add a property to my Question entity called SubmittedValue and have my EF data context Ignore(m => m.SubmittedValue) this. This property is what I will use to persist the input value for the Question at the view level.

我对此不满意的是,我的POCO实体膨胀了几乎不相关的属性-在一种情况下,我只会在Web UI上使用SubmittedValue,而我的POCO实体将在其他地方多次使用

What I don't like about this, is bloating my POCO entity with pretty much irrelevant properties - I'm only going to use SubmittedValue in one case at the Web UI whereas my POCO entities will be used elsewhere many times.

解决方案2

创建与POCO具有相同结构的视图模型对象,我们将其称为QuestionnaireModelQuestionGroupModelQuestionModel-这些对象在我的控制器中初始化,并且属性从POCO复制到视图模型.在QuestionModel上,添加我的SubmittedValue属性,并使用自定义模型活页夹保留该值,该活页夹将查看绑定上下文并从视图中获取我的值-该名称看起来像[group.question.1](其中1是问题的ID).在视图中使用每个问题组和每个问题的编辑器模板对此进行了显示.

Create view model objects that have the same structure as my POCOs, let's call them QuestionnaireModel, QuestionGroupModel and QuestionModel - these are initialised in my controller and properties are copied from the POCO to the view model. On QuestionModel I add my SubmittedValue property and persist this value using a custom model binder which looks at the binding context and gets my values from the view - where the name looks something like [group.question.1] (where 1 is the Id of the question). This is presented in the view using Editor Templates for each question group and for each question.

我对此不满意的是,使用这些额外的视图模型对象使我的Web UI膨胀,并且不得不手动将属性值从POCO复制到视图模型.我知道我可以使用类似AutoMapper的方法为我完成此操作,但这只是自动化该工作,而我理想情况下根本不希望这样做.

What I don't like about this, is bloating my web UI with these extra view model objects and having to manually copy property values from my POCO to the view model. I'm aware I could use something like AutoMapper to do this for me, but this is just automating that work, where I'd ideally like to not do it at all.

解决方案3

轻松更改解决方案2,以扩展我的POCO并使用其他视图模型对象覆盖virtual集合属性.因此,我的视图模型将如下所示:

Change solution 2 slighly, to instead extend my POCOs and override the virtual collection properties with the other view model objects. So, my view model would look like this:

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保留在我的业务层之外.

I do like this idea the best, but I haven't actually tried this yet. I get the best of both worlds here as 1. I can keep my POCOs out of my views and 2. I keep that one-time use property SubmittedValue out of my business layer.

你们中有没有更好的方法来解决这个问题?

Do any of you have a better way of handling this?

推荐答案

已经尝试了解决方案3(这是我的首选解决方案),我终于找到了解决方案.对于那些偶然发现此问题的人,这就是我正在做的事情.首先,创建扩展POCO实体的视图模型.我使用new实现重写了集合属性,以使集合成为视图模型类型.然后,将表单持久性属性添加到我的Question视图模型中(这样我就可以将其保留在我的业务层之外).

Having played around with solution 3 (which was my preferred solution) I've managed to finally get it. Here's what I'm doing, for anyone who stumbles on this question. First, I create my view models that extend my POCO entities. I override the collection properties with a new implementation to make my collections my view model types. I then add my form persistence property to my Question view model (so I can keep it out of my business layer).

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 ,我在POCO和视图模型之间创建映射,就像这样(使用.AfterMap()确保我的持久性属性不是null而是一个空字符串):

Using AutoMapper I create the mappings between my POCOs and view models like so (using .AfterMap() to make sure my persistence property isn't a null but an empty string instead):

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

接下来,每个Question都有一个具有单个输入元素的编辑器模板:

Next, each Question has an editor template that has a single input element:

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

最后,我使用自定义模型联编程序拾取(并保留)这些值,如下所示:

Finally, I pick up (and persist) these values using a custom model binder, like so:

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

尽管我对自定义模型联编程序不太满意(我认为我没有正确设置我的编辑器模板,所以诉诸自定义联编程序),这是我的首选解决方案.

Although I'm not too happy about the custom model binder (I don't think I'm setting my editor templates up correctly so resorting to a custom binder) for me this is the preferred solution.

这篇关于实体框架POCO到MVC 3中的ViewModel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆