Проблемы при попытке присоединить новый объект EF4 к ObjectContext, пока его объекты коллекции объектов уже подключены - PullRequest
0 голосов
/ 01 июня 2011

Это довольно сложно объяснить, поэтому, пожалуйста, потерпите меня.

У меня есть проект ASP.NET MVC 2 , который медленно убивает меня , в котором я пытаюсь взять данные формы и преобразовать их в сущности для создания или обновления, в зависимости от контекста ситуации , Наиболее значимые части (псевдокод):

Entity Game
    Scalar properties
    EntityCollection<Platform> Platforms

И основной рабочий процесс:

Данные формы -> модель, связанная с DTO -> сопоставление DTO с сущностью EF4 с помощью AutoMapper.

Все это работает хорошо, за одним исключением - мне нужно создать или обновить EntityCollection Platforms объекта Game с необработанными целочисленными индексными данными, содержащимися в DTO. Итак, вот что я пытался, который не работает:

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.

Некоторые исследования говорят мне, что этого следовало ожидать, поскольку мой новый объект Game не имеет ObjectContext, с которым он связан, и нулевой контекст считается отдельным контекстом. См. это краткое объяснение от Джули Лерман для получения дополнительной информации.

Хорошо, так как я бесстрашный человек, я подумал, что просто смогу зарегистрировать свою игру с ObjectContext, и все будет исправлено. Итак, я попробовал:

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

Где RegisterGame просто:

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

К сожалению, это не сработало. В ту же точку выдается то же исключение.

Итак, похоже, мне придется добавить EntityKey каждой платформы в мою коллекцию EntityCollection. Единственная проблема, я не уверен, как это сделать.

Итак, есть идеи?


РЕДАКТИРОВАТЬ: прогресс в некотором роде. Я попытался добавить только EntityKeys для объектов Platform, например:

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 не может отслеживать несколько объектов с одним и тем же ключом.

К сожалению, исключение не указывает , какой объект это, но я держу пари, что это один из объектов Платформы, так как они уже существуют, и я просто пытаюсь создать новые отношения между два.

Итак, остается вопрос: как, черт возьми, создать новую сущность и заполнить ее свойство EntityCollection <> существующими сущностями? Я не могу быть единственным человеком, который когда-либо хотел создать новую сущность и создать новые отношения «многие ко многим» между ней и существующими сущностями, верно?

Ответы [ 3 ]

0 голосов
/ 07 июня 2011

Я заставил его работать, используя оригинальное решение Джули Лерман .Мне не нужно было отсоединять / повторно подключать свои платформы с помощью моего оригинального решения, предшествующего DTO, поэтому я подумал, что мне это не нужно.В любом случае, похоже, мне нужно больше исследовать, как обращаться с ObjectContext.

0 голосов
/ 19 октября 2011

Первоначально я решил эту проблему (и другие проблемы, которые у меня возникали с графиками «многие ко многим» в 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, 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.

0 голосов
/ 02 июня 2011

Просто попробуйте это:

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

Вам не нужно загружать объект, чтобы установить связь. Вам просто нужен манекен с правильным идентификатором (который у вас уже есть). Манекен должен быть прикреплен к контексту.

...