尝试将新的EF4实体附加到ObjectContext,同时已附加其实体集合实体时出现问题

这有点难以解释,所以请耐心等待。

我有一个ASP.NET MVC 2项目,正在慢慢杀死我 ,我正在尝试获取表单数据并将其转换为实体来创建或更新,具体取决于具体情况。 最相关的部分(伪代码):

Entity Game Scalar properties EntityCollection Platforms 

基本工作流程是:

表单数据 – >绑定到DTO的模型 – >使用AutoMapper将DTO映射到EF4实体。

一切正常但有一个例外 – 我需要使用DTO中包含的原始整数索引数据创建或更新Game实体的Platforms EntityCollection。 所以,这是我一直在尝试的,这是行不通的:

 public AdminController(IArticleRepository articleRepository, IGameRepository gameRepository, INewsRepository newsRepository) { _articleRepository = articleRepository; _gameRepository = gameRepository; _newsRepository = newsRepository; Mapper.CreateMap() .BeforeMap((s, d) => { if (d.Platforms.Count > 0) { Platform[] existing = d.Platforms.ToArray(); foreach (var plat in existing) { d.Platforms.Remove(plat); } } foreach (var platId in s.PlatformIDs) { var newPlat = _gameRepository.GetPlatform(platId); d.Platforms.Add(newPlat); //  dest.BoxArtPath, opt => opt.Ignore()) .ForMember(dest => dest.IndexImagePath, opt => opt.Ignore()) .ForMember(dest => dest.Cons, opt => opt.MapFrom(src => String.Join("|", src.Cons))) .ForMember(dest => dest.Pros, opt => opt.MapFrom(src => String.Join("|", src.Pros))) .ForMember(dest => dest.LastModified, opt => opt.UseValue(DateTime.Now)) .ForMember(dest => dest.Platforms, opt => opt.Ignore()); } 

具体来说,我在上面突出显示的行中得到一个例外:

无法定义两个对象之间的关系,因为它们附加到不同的ObjectContext对象。

一些研究告诉我这是可以预料到的,因为我的新Game对象没有与之关联的ObjectContext,并且null上下文被认为是一个单独的上下文。 有关更多信息,请参阅Julie Lerman的简要说明 。

好吧,所以我是一个无畏的人,我想我只能用ObjectContext注册我的游戏,一切都会被修复。 所以,我尝试过:

 Game game = new Game(); _gameRepository.RegisterGame(game); 

RegisterGame简单地说:

 public void RegisterGame(Game game) { _siteDB.Games.AddObject(game); } 

不幸的是,这不起作用。 在同一点上抛出同样的exception。

所以,看起来我必须将每个Platform的EntityKey添加到我的EntityCollection中。 唯一的问题是,我不知道该怎么做。

那么,有什么想法吗?


编辑:一种进步。 我尝试只添加平台实体的EntityKeys,如下所示:

 Mapper.CreateMap() .BeforeMap((s, d) => { if (d.Platforms.Count > 0) { Platform[] existing = d.Platforms.ToArray(); foreach (var plat in existing) { d.Platforms.Remove(plat); } } foreach (var platId in s.PlatformIDs) { var newPlat = _gameRepository.GetPlatform(platId); d.Platforms.Add(new Platform { EntityKey = newPlat.EntityKey }); } }) 

它删除了“两个不同的ObjectContexts”exception。 问题是,如果我尝试将新游戏实体添加或附加到我的上下文,我将收到以下exception:

ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。

不幸的是,exception没有指定它是哪个对象,但我认为它是Platform对象之一,因为它们已经存在并且我只是想在两者之间建立新的关系。

所以,问题仍然存在:如何创建一个新实体并用现有实体填充其EntityCollection 属性? 我不可能是唯一一个想要创建新实体并在其与现有实体之间创建新的多对多关系的人,对吧?

试试这个:

 foreach (var platId in s.PlatformIDs) { Platfrom p = new Platform { Id = platId }; context.Attach(p) d.Platforms.Add(p); } 

您不必加载实体来建立关系。 你只需要一个具有正确Id(你已经拥有)的假人。 虚拟必须附加到上下文。

我通过使用Julie Lerman的原始解决方案来实现它 。 我没有必要使用我原来的DTO之前的解决方案来分离/重新连接我的平台,所以我认为我不需要在这里。 无论如何,看起来我需要对如何处理ObjectContext进行更多的研究。

通过简单地创建我自己的cludgy静态映射器,我最初解决了这个问题(以及我与EF4多对多图形结合使用AutoMapper时遇到的其他问题)。 这很难看,一点也不抽象,但它确实有效。 随着AutoMapper 2.0的发布,我决定看看我是否能够按照我想要的方式完成所有工作。

令人惊讶的是,我在AutoMapper代码中得到了与原来相同的“不同ObjectContexts”exception。 它在我自己的方法中没有exception,但在AutoMapper中没有。 奇怪的是,看到代码基本相同。

AutoMapper地图:

 Mapper.CreateMap() .BeforeMap((s, d) => { if (d.Platforms != null && d.Platforms.Count > 0) { var oldPlats = d.Platforms.ToArray(); foreach (var oldPlat in oldPlats) { d.Platforms.Remove(oldPlat); } } foreach (var platId in s.PlatformIDs) { var plat = _gameRepository.GetPlatform(platId); d.Platforms.Add(plat); } }) .ForMember(dest => dest.Platforms, opt => opt.Ignore()) .ForMember(dest => dest.BoxArtPath, opt => opt.Ignore()) .ForMember(dest => dest.IndexImagePath, opt => opt.Ignore()) .ForMember(dest => dest.Cons, opt => opt.MapFrom(src => string.Join("|", src.Cons))) .ForMember(dest => dest.Pros, opt => opt.MapFrom(src => string.Join("|", src.Pros))) .ForMember(dest => dest.LastModified, opt => opt.UseValue(DateTime.Now)); 

我自己的映射器:

 public static Game MapFromEditModelToGame(IGameRepository repo, AdminGameViewModel formData, Game newGame) { newGame.GameID = formData.GameID; newGame.GameTitle = formData.GameTitle; newGame.GenreID = formData.GenreID; newGame.LastModified = DateTime.Now; newGame.ReviewScore = (short)formData.ReviewScore; newGame.ReviewText = formData.ReviewText; newGame.Cons = String.Join("|", formData.Cons); newGame.Pros = String.Join("|", formData.Pros); newGame.Slug = formData.Slug; if (newGame.Platforms != null && newGame.Platforms.Count > 0) { var oldPlats = newGame.Platforms.ToArray(); foreach (var oldPlat in oldPlats) { newGame.Platforms.Remove(oldPlat); } } foreach (var platId in formData.PlatformIDs) { var plat = repo.GetPlatform(platId); newGame.Platforms.Add(plat); } return newGame; } 

我只能猜测游戏中存在一些奇怪的范围问题,但我认为这是一个有趣的(阅读:令人沮丧的)问题。 无法判断是由于EF4本身,还是我试图将其与AutoMapper一起使用。