【问题标题】:"EntityCollection already initialized" error with entity as model in Asp.Net MVC?在 Asp.Net MVC 中,实体作为模型的“EntityCollection 已初始化”错误?
【发布时间】:2011-07-03 16:37:26
【问题描述】:

我在创建复杂对象时遇到了很大的困难。我有一个带有顾问表的 EF 模型,该表与许多其他表具有一对多的关系。我想按原样使用 Consultant 对象作为模型,因为它非常简单和容易(正如在 NerdDinner 教程中所做的那样),就像我对其他没有一对多关系的对象所做的那样.问题是这些关系导致这个错误:当我尝试发布到 Create 方法时,“EntityCollection 已经初始化”。

有几个人建议我改用 ViewModel,我发布了一个关于此的问题 (ViewModels and one-to-many relationships with Entity Framework in MVC?),因为我不太了解它。问题是代码变得非常荒谬......这与我迄今为止在 MVC 中所欣赏的简单性相去甚远。

在那个问题中我忘了提到除了 Create 方法之外,Edit 方法使用相同的 CreateConsultant 方法(名称可能具有误导性,它实际上填充了 Consultant 对象)。因此,为了在编辑时不添加额外的“程序”,我需要进一步复杂化该方法。所以现在看起来像这样:

private Consultant CreateConsultant(ConsultantViewModel vm, Consultant consultant) //Parameter Consultant needed because an object may already exist from Edit method.
        {
            consultant.Description = vm.Description;
            consultant.FirstName = vm.FirstName;
            consultant.LastName = vm.LastName;
            consultant.UserName = User.Identity.Name;

            if (vm.Programs != null)
                for (int i = 0; i < vm.Programs.Count; i++)
                {
                    if (consultant.Programs.Count == i)
                        consultant.Programs.Add(vm.Programs[i]);
                    else
                        consultant.Programs.ToList()[i] = vm.Programs[i];
                }
            if (vm.Languages != null)
                for (int i = 0; i < vm.Languages.Count; i++)
                {
                    if (consultant.Languages.Count == i)
                        consultant.Languages.Add(vm.Languages[i]);
                    else
                        consultant.Languages.ToList()[i] = vm.Languages[i];
                }
            if (vm.Educations != null)
                for (int i = 0; i < vm.Educations.Count; i++)
                {
                    if (consultant.Educations.Count == i)
                        consultant.Educations.Add(vm.Educations[i]);
                    else
                        consultant.Educations.ToList()[i] = vm.Educations[i];
                }
            if (vm.WorkExperiences != null)
                for (int i = 0; i < vm.WorkExperiences.Count; i++)
                {
                    if (consultant.WorkExperiences.Count == i)
                        consultant.WorkExperiences.Add(vm.WorkExperiences[i]);
                    else
                        consultant.WorkExperiences.ToList()[i] = vm.WorkExperiences[i];
                }

            if (vm.CompetenceAreas != null)
                for (int i = 0; i < vm.CompetenceAreas.Count; i++)
                {
                    if (consultant.CompetenceAreas.Count == i)
                        consultant.CompetenceAreas.Add(vm.CompetenceAreas[i]);
                    else
                        consultant.CompetenceAreas.ToList()[i] = vm.CompetenceAreas[i];
                }

            string uploadDir = Server.MapPath(Request.ApplicationPath) + "FileArea\\ConsultantImages\\";
            foreach (string f in Request.Files.Keys)
            {
                var filePath = Path.Combine(uploadDir, Path.GetFileName(Request.Files[f].FileName));
                if (Request.Files[f].ContentLength > 0)
                {
                    Request.Files[f].SaveAs(filePath);
                    consultant.Image = filePath;
                }

            }
            return consultant;
        }

这很荒谬,可能是由于我的无能,但我需要知道如何正确地做到这一点。仅仅回答“使用 ViewModel”显然是不够的,因为这就是让我一开始就陷入困境的原因。我想要简单实体对象作为模型的简单性,但没有“EntityCollection 已被初始化”错误。我该如何解决这个问题?

当然,如果我只是以错误的方式执行 ViewModel 策略,那么也欢迎提出建议,但主要是我想知道如果我以简单的“NerdDinner”简单对象方式执行此错误,是什么原因造成的。请记住,有问题的视图仅限于网站的授权用户。我很想以“正确”的方式来做,但如果使用 ViewModels 意味着代码很难维护,我会放弃它......

请帮忙!

更新:

原来这段代码甚至不起作用。我只是在调用 Edit 以更新值后进行了检查,但它并没有更新它们。所以只有创建部分有效。

这是不起作用的部分:

consultant.Programs.ToList()[i] = vm.Programs[i];

我有一种预感,我无法使用 ToList 并更新 EntityCollection 中的项目。但这让它变得更加困难。所以现在我不知道如何直接处理实体,我更喜欢(见上文)。而且我不知道如何让这些 ViewModel 工作,更不用说把它弄干净了......

有什么想法吗?这里肯定有什么问题,我希望有人能发现我是如何遗漏了一些简单的东西,导致所有这些代码都颠倒了!

【问题讨论】:

  • 错误具体发生在哪里?
  • 如果我没有上面的代码(这是尝试使用 ViewModel 代替),而是从视图接收一个顾问对象返回到 Post Create 方法,就会发生这种情况。它永远不会到达方法(这意味着我不能设置断点来跟踪它,因为它永远不会到达那里)。相反,代码在 EF 模型设计器代码中的任何一个集合(关系的多方面)处停止,例如“程序”,并且会说这个 EntityCollection 已经被初始化了”。

标签: asp.net entity-framework asp.net-mvc-3 entity viewmodel


【解决方案1】:

好的,所以根据我的经验,EF 的东西在在线上使用时并不能很好地播放,句号(意思是,无论你是在 MVC 还是 WCF 中,它都不是传递数据的好格式,或者其他) .对我来说,这是一个 EF 问题,而不是 MVC 问题,因为您在直接使用 EF 模型时遇到的问题必须与如何在 EF 对象上下文中跟踪它们有关。有人告诉我,有解决方案涉及重新附加传递的实体,但我发现这比它的价值更麻烦;和其他人一样,这就是为什么大多数人在这种情况下对您的输入参数说“使用视图模型”。

我同意您上面的代码有点令人不快,有几种方法可以解决它。首先,查看AutoMapper,这很有帮助,因为您可以跳过明显的定义(例如,当两个模型都有一个简单的属性时,例如相同类型和名称的“名称”。

其次,无论如何,您正在使用循环执行的操作有点偏离。您不应该假设您从客户端发布的列表与 EF 正在跟踪的列表(更确切地说,是指您的程序等)的顺序相同。相反,请考虑可能发生的情况并解释这些情况。我将以程序为例。

1) 顾问有一个计划,但有关该计划的详细信息已更改。 2) 顾问没有视图模型中的程序 2a)该程序已经存在于数据库中的某个地方,只是不属于该顾问 2b) 程序是新的。 3) 顾问有一个不在视图模型中的程序(您目前不考虑的)。

我不知道您希望在每种情况下发生什么(即视图模型是否完整且规范,或者实体模型是否在更新期间?),但假设您正在循环查看您的视图模型像你上面做的程序。对于场景 1(这似乎是您的主要问题),您需要执行以下操作(使用 AutoMapper):

var updated = vm.Programs[i];    
var original = consultant.Programs.SingleOrdefault(p=>p.ID == uppdated.ID);
Mapper.Map(updated,original);
yourEfContext.SaveChanges(); // at some point after this, doesn't have to be inside the loop

编辑: 另一个可能对您有用的想法是,当您从数据库中取出您的顾问时(不确定您使用什么机制),请确保您在要更新的集合上调用 Include。否则,您所做的每一次迭代都将是数据库的另一次往返,如果您只使用 Include 一次性急切加载它们,显然可以避免这种情况。

【讨论】:

  • 好吧,这个 AutoMapper 似乎无处不在,所以我想我必须调查一下。但是您能否澄清一下:我是否必须先以相反的方式进行映射? (Consultant.Programs -> vm.Programs?)如果是这样,我是否在 Get 方法中执行此操作?其次,AutoMapper 是否将 vm.Program 对象中所有属性的值传递给此映射中的 advisor.Program 对象?如果您不介意,我将非常感谢一个扩展示例,说明如何创建映射器、最初映射,然后再映射回来!谢谢!
  • 当你拉动它时,自动映射器会附带大量示例。但基本上你想创建(一次,可能在你从 App_Start 调用的类中)你的地图。最基本的形式是:Mapper.CreateMap(),但是如果你想自定义事物的映射方式(或者如果事情这样做,因为你的类的形状,你会做各种各样的重载)可能并不总是匹配)。
  • 好的,谢谢。我真正想知道的是(我尝试阅读一些关于 AutoMapper 的内容,但它有点复杂):我可以将整个 Consultant 对象映射到一个 ConsultantViewModel 对象,包括子集合吗?如果是这样,怎么办?我只能找到更简单的示例(简单属性)。
  • stackoverflow.com/questions/1789925/… 与映射其他任何内容几乎相同,如该帖子所示。
  • 好的,所以基本上只是映射父对象,然后直接映射子集合,一个接一个?谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-15
  • 2010-11-24
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多