问题描述
|
这解释起来有些复杂,因此请耐心等待。
我有一个ASP.NET MVC 2项目,该项目正在慢慢杀死我,我正在其中尝试获取表单数据并将其转换为要创建或更新的实体(取决于具体情况)。最相关的部分(伪代码):
Entity Game
Scalar properties
EntityCollection<Platform> 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<AdminGameEditModel,Game>()
.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); // <-- where it chokes
}
})
.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,src.Pros)))
.ForMember(dest => dest.LastModified,opt => opt.UseValue(DateTime.Now))
.ForMember(dest => dest.Platforms,opt => opt.Ignore());
}
具体来说,我在上面突出显示的行上遇到了一个例外:
无法定义两个对象之间的关系,因为它们已附加到不同的ObjectContext对象。
一些研究告诉我,这是可以预期的,因为我的新Game对象没有与之关联的ObjectContext,并且空上下文被视为单独的上下文。有关更多信息,请参见Julie Lerman的简要说明。
好的,我是一个勇敢的人,所以我认为自己可以使用ObjectContext注册我的游戏,并且所有内容都将得到修复。因此,我尝试:
Game game = new Game();
_gameRepository.RegisterGame(game);
RegisterGame简单来说就是:
public void RegisterGame(Game game)
{
_siteDB.Games.AddObject(game);
}
不幸的是,那没有用。在同一点抛出相同的异常。
因此,看起来我不得不将每个Platform的EntityKey添加到我的EntityCollection中。唯一的问题是,我不确定该怎么做。
那么,有什么想法吗?
编辑:某种进度。我尝试仅添加Platform实体的EntityKeys,如下所示:
Mapper.CreateMap<AdminGameEditModel,Game>()
.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”异常。问题是,如果我尝试将新的Game实体添加或附加到上下文中,则会遇到以下异常:
具有相同键的对象已存在于ObjectStateManager中。 ObjectStateManager无法使用相同的键跟踪多个对象。
不幸的是,该异常未指定它是哪个对象,但我敢打赌它是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做更多的研究。
, 我最初是通过简单地创建自己的笨拙的静态映射器来解决此问题(以及与EF4多对多图和AutoMapper结合使用时遇到的其他问题)。这很丑陋,一点也不抽象,但确实有效。随着AutoMapper 2.0的发布,我决定看看是否最终可以使所有功能按自己的方式工作。
令人惊讶的是,我在AutoMapper代码中得到了与最初相同的“不同的ObjectContexts”异常。在我自己的方法中,它没有例外,但在AutoMapper中没有例外。奇怪的是,看到的代码本质上是相同的。
AutoMapper映射:
Mapper.CreateMap<AdminGameViewModel,Game>()
.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,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一起使用。