实体框架POCO到MVC 3中的ViewModel [英] Entity Framework POCO to ViewModel in MVC 3
问题描述
我有一个示例项目,一个动态问卷系统,任何管理员都可以创建一个问卷,然后向其中添加问题组,然后向每个问题组添加问题.
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具有相同结构的视图模型对象,我们将其称为QuestionnaireModel
,QuestionGroupModel
和QuestionModel
-这些对象在我的控制器中初始化,并且属性从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屋!