Каков пример счетчика для этого кода, генерирующего измененное предупреждение о закрытии, которое может вызвать проблему? - PullRequest
0 голосов
/ 24 марта 2011

Есть и другие вопросы по этому поводу, но это мой первый раз, и я не могу полностью понять.

ВОПРОС 1: Предполагая, что этот пример действительно безопасен, то есть предупреждение может быть проигнорировано: как нужно изменить этот код, чтобы проблема , о которой предупреждает это предупреждение, была бы реальной?

ВОПРОС 2: Как исправление, примененное к предупреждению, устраняет предупреждение? Моя интуиция подсказывает мне, что это тот же результат.

Вот код:

    public static void SynchCreativesForCampaign(int pid, ILogger logger)
    {
        var db = new SynchDbDataContext(true);

        foreach (var creativeItem in CreativeList.Create(pid).CreativeItems)
        {
            logger.Log(@"creative id " + creativeItem.CreativeId);

            var creativeDetail = CreativeDetail.Create(creativeItem.CreativeId);

            //var item = creativeItem; <-- this gets added by the "fix" for the warning
            var creativeEntity = (from c in db.CreativeEntities
                                  where c.dtid == creativeItem.CreativeId
                                  select c).FirstOrDefault();

            if (creativeEntity == null)
            {
                creativeEntity = new CreativeEntity {dtid = item.CreativeId};
                db.CreativeEntities.InsertOnSubmit(creativeEntity);
            }
        }

        db.SubmitChanges();
    }

Вот предупреждение:

enter image description here

1 Ответ

0 голосов
/ 24 марта 2011

Вот как это может укусить вас.Пример немного надуман, но вы поймете идею.

public static void SynchCreativesForCampaign(int pid, ILogger logger)
{
    var db = new SynchDbDataContext(true);
    CreativeEntity creativeEntity = null; // NEW: pull this out of the loop

    foreach (var creativeItem in CreativeList.Create(pid).CreativeItems)
    {
        logger.Log(@"creative id " + creativeItem.CreativeId);

        var creativeDetail = CreativeDetail.Create(creativeItem.CreativeId);

        if (creativeEntity != null) {
            continue; // NEW: let the loop go on, but make sure we only
                      // wrote to creativeEntity *once*
        }

        // IMPORTANT: notice I don't immediately call FirstOrDefault!
        creativeEntity = from c in db.CreativeEntities
                         where c.dtid == creativeItem.CreativeId
                         select c;

    }

    if (creativeEntity != null)
    {
        // Only NOW evaluate the query
        var result = creativeEntity.FirstOrDefault();

        // OK, stop: result holds the creative entity with the dtid
        // referring to which CreativeItem.CreativeId?
    }
}

Вы можете ответить «с помощью dtid, ссылающегося на первый элемент в коллекции CreativeItems, который мы перебрали», но реальность такова, чтоон будет ссылаться на последний один (по этой причине я позволил циклу continue - он ничего не сделал , кроме изменения значения creativeItem, которое было необходимо дляпусть всплывет ошибка).

Код работает, выполняя некоторый компилятор voodoo, который по сути продлевает время жизни переменной creativeItem до точки, где вы оцениваете запрос .Таким образом, хотя это выглядит так, что после foreach end creativeItem больше не существует, на самом деле он ждет где-то в памяти и, конечно, его значение остается неизменным по сравнению с тем, что было во время последней итерации.

Теперьрассмотрим, что произойдет, если вы скопируете значение creativeItem в другую переменную с областью действия только внутри цикла foreach.Теперь продлен срок жизни переменной, но есть важное отличие: эта переменная, находящаяся в области внутри foreach, не будет повторно использоваться для нескольких итераций.В каждой итерации будет использоваться переменная new (с тем же именем, конечно), и это будет то значение, которое вы увидите при оценке запроса.

...