“EntityCollection已初始化”实体作为Asp.Net MVC模型的错误? [英] "EntityCollection already initialized" error with entity as model in Asp.Net MVC?

查看:130
本文介绍了“EntityCollection已初始化”实体作为Asp.Net MVC模型的错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm having great difficulties with creating a complex object. I have an EF model with a Consultant table that has one-to-many relationships to a number of other tables. I wanted to use the Consultant object as the model as is because it would be very simple and easy (and as it's done in the NerdDinner tutorial), as I've done with other objects that didn't have one-to-many relationships. The problem is that these relationships cause this error: "EntityCollection already initialized" when I try to post to the Create method.

有几个人建议我使用ViewModel ,并且我发布了一个有关此问题的问题( ViewModels和MVC中实体框架的一对多关系?),因为我不太明白。问题是代码得到了可笑的可笑...与MVC中迄今为止欣赏的简单性相差甚远。

Several people have advised me to use a ViewModel instead, and I posted a question about this (ViewModels and one-to-many relationships with Entity Framework in MVC?) because I don't really understand it. The problem is the code gets reeeeally ridiculous... It's a far cry from the simplicity I've so far appreciated in MVC.

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

In that question I forgot to mention that besides the Create method, the Edit method uses the same CreateConsultant method (the name may be misleading, it actually populates the Consultant object). And so in order not to have additional, say "Programs" added when editing, I needed to complicate that method further. So now it looks like this:

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已被初始化的错误。如何解决这个问题?

This is absurd, and it's probably due to my incompetence, but I need to know how to do this properly. Just the answer "use a ViewModel" obviously won't suffice, because that's what got me into this trouble to begin with. I want the simplicity of the simple entity object as model but without the "EntityCollection has already been initialized" error. How do I get around this?

当然,如果我只是以ViewModel策略做错了方式,那么这个建议也是受欢迎的,但主要是我想知道是什么导致这个错误,如果我这样做简单的NerdDinner简单对象的方式。请记住,所涉及的视图仅限于该网站的授权用户。我很乐意做到这一点正确的方式,但是如果使用ViewModels意味着有很难维护的代码,我会放弃...

Of course, if I'm just doing the ViewModel strategy the wrong way, suggestions on that are welcome too, but mainly I want to know what is causing this error if I do it the simple "NerdDinner" simple object way. Please keep in mind also that the View in question is restricted to authorized users of the site. I would love to do it the "correct" way, but if using ViewModels implies having code that is this hard to maintain, I'll forgo it...

请帮助!

更新:

显示此代码甚至不起作用。调用编辑后,我刚刚检查更新值,它不会更新它们。所以只有创建零件可以工作。

Turns out this code doesn't even work. I just checked after calling Edit to update values, and it doesn't update them. So only the Create part works.

这是不起作用的部分:

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

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

I sort of had a hunch I couldn't use ToList and update an item in the EntityCollection. But this makes it even harder. So now I don't know how to do it with the entity directly, which I would prefer (see above). And I don't know how to get this ViewModel stuff working, let alone get it clean...

任何想法?这里肯定有一些错误,我希望有人会发现我刚刚错过了一些简单的东西,将所有这些代码放在头上!

Any ideas? There must be something really wrong here, and I'm hoping someone will spot how I've just missed something simple that turns all of this code on its head!

推荐答案

好的,所以在我的经验中,EF东西在线上使用时不会很好地播放,完全停止(意思是说,不管你是否在MVC中传递数据都不是一个好的格式或WCF,或任何)。这是一个EF问题,对我来说,不是一个MVC问题,因为直接使用你的EF模型遇到的问题必须在EF对象中被跟踪。我被告知有解决方案涉及重新附加所通过的实体,但我发现它比它的价值更麻烦;和其他人一样,为什么大多数人在这种情况下对你的输入参数说use viewmodels。

Ok, so in my experience the EF stuff doesn't easily play nicely when used on the wire, full stop (meaning, it's not a good format for passing data in whether you're in MVC or WCF, or whatever). It's an EF issue, to me, not a MVC issue because the problem you're experiencing with the direct use of your EF models has to do w/ how they're tracked in the EF objectcontext. I've been told that there's solutions to that involving re-attaching the passed entity, but I've found it to be more trouble than it's worth; as have others which is why most folks say "use viewmodels" for your input parameters in this situation.

我同意你上面的代码是非常不愉快的,有几种方法要解决这个问题。首先,请查看 AutoMapper ,这有助于您轻松定义明显的(例如,当两个模型都有一个简单的属性,如相同类型和名称的名称。

I agree that your code above is kind unpleasant, there's a couple ways to fix it. First, check out AutoMapper, which helps a lot in that you can skip defining the obvious (e.g. when both models have a simple property like "Name" of hte same type & name.

其次,你正在做的这些循环有点不合,你不应该假设您从客户端发布的列表与EF正在跟踪的列表(列表,而不是参考您的程序等)是相同的顺序,而是考虑可能发生的情况并对其进行说明。将使用程序作为示例。

Second, what you're doing w/ the loops is a bit off anyway. You should not assume that your list being posted from the client is the same order, etc as the list that EF is tracking (lists, rather; referring to your Programs, etc). Instead, think of the scenarios that might occur and account for those. I'll use Programs as an example.

1)顾问有一个程序,但有关该程序的详细信息已更改。
2)顾问没有在viewmodel
中的程序2a)该程序已经存在于数据库中,只是不是这个顾问的一部分
2b)该程序是新的。
3)顾问有一个不在viewmodel中的程序(你目前没有解释的东西)。

1) a consultant has a program, but details about that program have changed. 2) a consultant does not have a program that is in the viewmodel 2a) the program already exists in the database somewhere, just not part of this consultant 2b) the program is new. 3) a consultant has a program that's not in the viewmodel (something you don't currently account for).

我不知道你想要什么在每种情况下都会发生(即,viewmodel是完整的和规范的,还是在更新过程中是实体模型),但是假设您正如上述那样循环访问您的viewmodel的程序。对于场景1(这似乎是您的主要问题),您将需要执行以下操作(使用AutoMapper):

I don't know what you want to have happen in each of those cases (i.e. is the viewmodel complete and canonical, or is the entity model, during an update?), but let's say you're looping over your viewmodel's Programs as you do above. For the scenario 1 (which seems to be your main problem), you will want to do something like (using 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来一次性加载它们。

one other thought that might be useful to you is that I when you fetch your consultant out of the database (not sure what mechanism you use for that), make sure you call Include on the collections that you're going to update as well. Otherwise, each of those iterations you do will be another round-trip to the database, which obviously you could avoid if you just used Include to eager-load them all in one shot.

这篇关于“EntityCollection已初始化”实体作为Asp.Net MVC模型的错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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