Squeak.ru - шаблоны программирования

Где возвращает неверную запись

ОБНОВЛЕНИЕ

У меня только что возникла мысль, которая вполне может иметь отношение к этому вопросу. В этом проекте я использую первый подход с кодом. Первоначально мой класс ZoneMapping был определен, как вы можете видеть ниже, однако в базе данных было только одно поле PrimaryKey. Я считаю, потому что EF не совсем правильно интерпретировал данные.

На этом этапе я внес изменения в выходные данные SQL-скрипта migration, чтобы добавить дополнительный первичный ключ, который я применил к базе данных. Я только что обновил миграцию вместо:

       CreateTable(
            "dbo.NetC_EF_ZoneMapping",
            c => new
                {
                    PostcodeKey = c.String(nullable: false, maxLength: 128),
                    Zone_ID = c.Int(),
                })
            .PrimaryKey(t => t.PostcodeKey)
            .ForeignKey("dbo.NetC_EF_Zone", t => t.Zone_ID)
            .Index(t => t.Zone_ID);

Я только что попытался добавить дополнительный PrimaryKey вручную при миграции после того, как PostcodeKey был определен.

 .PrimaryKey(t => t.Zone_ID)

К сожалению, я все еще получаю сообщение об ошибке — я предполагаю, что эта миграция не используется для построения «модели» EF в коде, но мне интересно, думает ли она, что может быть только одна запись с любым заданным PostcodeKey, который может объясните ситуацию?


Я публикую новый вопрос на основе Linq, за исключением того, что он не работает должным образом - повторяющиеся элементы, потому что я чувствую, что было обнаружено достаточно, чтобы вопрос был недействительным, а Except вообще не проблема.

У меня проблема в том, что у меня есть предложение Linq Where, которое, кажется, возвращает неправильные данные. Данные в моей базе данных выглядят так:

введите здесь описание изображения

Класс, представляющий эти данные, имеет составной ключ:

/// <summary>
/// Represents a mapping between a postcode and a zone
/// </summary>
[Table("NetC_EF_ZoneMapping")]
public class ZoneMapping
{
    /// <summary>
    /// Gets or sets the postcode identifier
    /// </summary>
    [Key]
    public String PostcodeKey { get; set; }

    /// <summary>
    /// Gets or sets the Zone identifier
    /// </summary>
    [Key]
    public Zone Zone { get; set; }
}

Итак, я выполняю следующий код, который приводит к разным идентификаторам:

var result = this.context.ZoneMappings.Include("Zone").Where(z => z.Zone.ID == 257 && z.PostcodeKey == "2214");
var result2 = new FreightContext().ZoneMappings.Include("Zone").Where(z => z.Zone.ID == 257 && z.PostcodeKey == "2214");
if (result.First().Zone.ID != result2.First().Zone.ID)
     throw new InvalidOperationException();

введите здесь описание изображения

SQL (или ToString() для этих двух элементов идентичны). Таким образом, единственная разница в том, что один является новым контекстом, а другой был передан и использован для каких-то других вещей. Код, который создает контекст, возвращающий неверный результат:

 // Copy the contents of the posted file to a memory stream
 using (StreamReader sr = new StreamReader(fileUpload.PostedFile.InputStream))
 using (FreightContext context = new FreightContext())
 {
      // Attempt to run the import
      ZoneMappingCSVImporter importer = new ZoneMappingCSVImporter(sr, context, System.Globalization.CultureInfo.CurrentUICulture);
      var items = importer.GetItems().ToList();
      importer.SaveItems(items);
      this.successBox.Value = "Import completed and added " + items.Count() + " zones mappings.";
  }

Затем это регистрирует ClassMap в библиотеке, которую я использую, где:

 public ZoneMappingCSVImporter(TextReader textReader, FreightContext context, CultureInfo culture)
 : base(textReader, context, culture)
 {
     this.reader.Configuration.RegisterClassMap(new ZoneMappingMap(this.context));
 }

Я делаю поиск, используя контекст:

 /// <summary>
        /// Initializes a new instance of the <see cref="ZoneMap"/> class.
        /// </summary>
        public ZoneMappingMap(FreightContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            Map(m => m.PostcodeKey);
            Map(m => m.Zone).ConvertUsing(row =>
            {
                // Grab the name of the zone then go find this in the database
                String name = row.GetField<String>("Zone");
                return context.Zones.Where(z => String.Compare(z.Name, name, true) == 0).FirstOrDefault();
            });
        }

Я не вижу здесь ничего странного, я проверил SQL, сгенерированный Entity Framework, проверил, что база данных та же самая - я не могу понять, почему будет возвращена неправильная запись. Кто-нибудь может пролить свет на это?

08.05.2014

  • Когда вы говорите «проверено»... вы имеете в виду, что просмотрели их обоих? Или вы на самом деле запускали запросы к базе данных вручную с помощью Management Studio или аналогичного инструмента? Если да... что возвращают запросы? Оба возвращают ожидаемую зону с идентификатором 257? 08.05.2014
  • @SimonWhitehead: я сравнил их друг с другом в WinMerge, чтобы проверить их идентичность, и запустил один из них через Management Studio. 08.05.2014
  • ... и они возвращают ожидаемую строку с идентификатором зоны 257? (глупые вопросы, я знаю.. но я должен спросить..) 08.05.2014
  • @SimonWhitehead: Да, то же самое, что и result2.First(). Я тоже не против глупых вопросов :) 08.05.2014
  • Пробовали ли вы с другими предложениями where увидеть, есть ли какой-либо шаблон? например, всегда ли он добавляет один, всегда возвращает следующий элемент в порядке по умолчанию, всегда возвращает ту же неправильную запись и т. д. 08.05.2014
  • @Chris: Я не пробовал - чтобы добраться до этого момента, я не смог воспроизвести его в своем модульном тесте (поэтому я пытаюсь использовать другой контекст в своем коде). Я посмотрю, смогу ли я найти другой пример с двумя идентичными PostcodeKeys и попробовать его. 08.05.2014
  • Я не гуру EF (на самом деле NHibernate ..), но это сопоставление Zone в карте классов кажется мне странным. Столбец Zone_ID.. но нигде в вашем коде не упоминается (только Zone делает..). Есть ли в этом часть, в которой отсутствует карта столбца Zone_ID == внешнего ключа? 08.05.2014
  • Учитывая мой комментарий выше.. возможно, .GetField<string>("Zone") должно быть GetField<string>("Zone_ID")? .. (опять же, может быть глупый комментарий .. так как я не очень хорошо разбираюсь в картах классов EF!) 08.05.2014
  • @SimonWhitehead: Извините, Zone_ID - это поле внешнего ключа, да. Нет - материал GetField действительно не связан (и связан с сопоставлением столбцов CSV). Он здесь, чтобы проиллюстрировать, как был использован нарушенный контекст. 08.05.2014
  • @Chris: Ну, я попытался смоделировать, создав аналогичную запись в базе данных, но «Где», похоже, в этот момент работает нормально:/ 08.05.2014
  • Если вы сравниваете первый результат этих двух запросов, вы действительно должны упорядочить наборы по этому идентификатору. 08.05.2014
  • Кроме того, что еще вы сделали в существующем контексте перед запуском этого оператора. если бы у вас были некоторые ожидающие изменения, они бы отображались в запросе, поскольку они существуют в локальном графе объектов. 08.05.2014
  • @LukeMcGregor: Привет, Люк, в этом случае оба набора все равно возвращают только один идентификатор. Контекст, в котором возникли проблемы, к этому моменту не имеет ожидающих изменений, он использовался исключительно для поиска зон (последний блок кода в моем вопросе). 08.05.2014
  • @Chris: Ну, мне удалось повторить это с другим набором данных и извлечь его в модульный тест, хотя на данный момент я не мудрее: / Также кажется, если я перемещаю условия «Где ()» вокруг немного иногда я не получаю ошибку. 09.05.2014

Ответы:


1

Объяснение этой проблемы, скорее всего, следующее:

  • Ваш способ определения составного первичного ключа в вашей сущности неверен. В вашей текущей модели EF рассматривает только ZoneMapping.PostcodeKey как первичный ключ. Подробнее об этом и о том, как это исправить, ниже.

  • Вы получаете неправильный result в случае, когда соответствующий контекст уже содержит объект с PostcodeKey == "2214" и Zone_ID == 256 до выполнения запроса. (Я думаю, у вас никогда не было неправильного result2.) Когда EF загружает объекты из базы данных, он всегда проверяет запрос, если объект с таким же ключом уже существует в контексте. Если да, запрошенный объект отбрасывается, а присоединенный объект добавляется в коллекцию результатов. В вашем случае вы запрашиваете PostcodeKey == "2214" и Zone_ID == 257. После запроса EF выбирает значения первичного ключа из строки результата. Но поскольку EF «думает», что первичный ключ — это только PostcodeKey == "2214", он ищет присоединенный объект с этим значением ключа, находит объект с PostcodeKey == "2214", но Zone_ID == 256 и возвращает его вам как результат. У вас никогда не возникнет этой проблемы с result2, потому что соответствующий контекст является новым и пустым, поэтому EF вернет только что загруженный результат, а не какой-либо более старый присоединенный объект.

Если вы хотите определить составной ключ, вы должны иметь скалярные свойства для обеих частей ключа. Вы не можете использовать свойство навигации, такое как ZoneMapping.Zone, в качестве ключевой части. Это означает, что у вас должно быть свойство для вашего столбца Zone_ID в классе модели (в дополнение к свойству навигации Zone):

public class ZoneMapping
{
    public String PostcodeKey { get; set; }

    public int Zone_ID { get; set; }
    public Zone Zone { get; set; }
}

Затем, чтобы определить составной ключ с аннотациями данных, вы должны использовать атрибут Column(Order = n), как уже показано в ответе @Jonas Jämtberg. Вы также должны применить атрибут ForeignKey к Zone_ID, потому что подчеркивание делает имя свойства «нетрадиционным», так что EF не обнаружит его как FK по соглашению:

public class ZoneMapping
{
    [Key, Column(Order = 0)]
    public String PostcodeKey { get; set; }

    [Key, ForeignKey("Zone"), Column(Order = 1)]
    public int Zone_ID { get; set; }
    public Zone Zone { get; set; }
}

Класс миграции, созданный EF с этим сопоставлением, должен иметь определение первичного ключа, которое выглядит следующим образом:

.PrimaryKey(t => new { t.PostcodeKey, t.Zone_ID })

...не похоже на .PrimaryKey(t => t.PostcodeKey).PrimaryKey(t => t.Zone_ID), что вы безуспешно пытались решить проблему.

22.05.2014
  • Это отличное объяснение, и я очень хочу его протестировать — однако, OrderAttribute, похоже, не определен, когда я пытаюсь его добавить. 23.05.2014
  • Я понял, что мне нужен столбец (порядок = 1) для EF5, и вы абсолютно правы в своем ответе. 23.05.2014
  • @Ian: Извините, моя ошибка, такого OrderAttribute на самом деле никогда не существовало. Column(Order = n) всегда был правильным атрибутом. Я исправил это. 23.05.2014
  • Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...