繁体   English   中英

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

[英]Problems trying to attach a new EF4 entity to ObjectContext while its entity collection entities are already attached

这解释起来有点复杂,所以请多多包涵。

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

Entity Game
    Scalar properties
    EntityCollection<Platform> Platforms

基本的工作流程是:

表单数据 -> model 绑定到 DTO -> 使用 AutoMapper 将 DTO 映射到 EF4 实体。

除了一个例外,这一切都很好——我需要使用 DTO 中包含的原始 integer 索引数据创建或更新游戏实体的平台 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, 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 对象。

一些研究告诉我这是意料之中的,因为我的新游戏 object 没有与之关联的 ObjectContext,并且 null 上下文被认为是一个单独的上下文。 有关更多信息,请参阅Julie Lerman 的简短解释

好的,作为一个勇敢的人,我想我可以简单地使用 ObjectContext 注册我的游戏,一切都会得到解决。 所以,我尝试了:

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

RegisterGame 很简单:

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

不幸的是,这并没有奏效。 在同一点抛出相同的异常。

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

那么,有什么想法吗?


编辑:某种进展。 我尝试仅添加平台实体的 EntityKey,如下所示:

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 中已存在具有相同密钥的 object。 ObjectStateManager 无法跟踪具有相同键的多个对象。

不幸的是,该异常没有指定它是哪个object,但我打赌它是平台对象之一,因为它们已经存在,我只是想在两者之间建立新的关系。

所以,问题仍然存在:到底是如何创建一个新实体并用现有实体填充其 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 static 映射器来解决了这个问题(以及我在使用 EF4 多对多图与 AutoMapper 时遇到的其他问题)。 它很丑陋,一点也不抽象,但它确实有效。 随着 AutoMapper 2.0 的发布,我决定看看我能否最终让一切都按照我想要的方式工作。

令人惊讶的是,我在 AutoMapper 代码中得到了与最初相同的“不同 ObjectContexts”异常。 它在我自己的方法中毫无例外地工作,但在 AutoMapper 中却没有。 奇怪的是,代码本质上是相同的。

AutoMapper map:

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, 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;
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM