.NET Core Entity Framework InvalidOperationException - PullRequest
0 голосов
/ 13 декабря 2018

У меня есть простая модель

[Table("InterfaceType")]
public class InterfaceType
{
    [Key]
    public int InterfaceTypeId { get; set; }
    public string Description { get; set; }
}

и в моем DbContext

public DbSet<InterfaceType> InterfaceTypes { get; set; }

и в моем контроллере

List<InterfaceType> types = _context.InterfaceTypes.FromSql(
            "SELECT * FROM [Interfaces].[Control].[InterfaceType]").ToList();

, который возвращает ошибку:

InvalidOperationException: обязательный столбец InterfaceID не присутствовал в результатах операции FromSql.

Я использую FromSql в других методах, аналогичных этому, безпроблема, хотя эти модели содержат InterfaceId.Почему эта операция ожидает InterfaceId, когда его нет в модели.Я также пробовал ниже с тем же результатом.

List<InterfaceType> types = _context.InterfaceTypes.FromSql(
            "SELECT InterfaceTypeId, Description FROM [Interfaces].[Control].[InterfaceType]").ToList();

Я также пытался:

interfacesOverview.SelectedInterface.InterfaceTypes = _context.InterfaceTypes.ToList();

После объявления через API:

 protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
       modelBuilder.Entity<InterfaceType>().ToTable("InterfaceType", "Control");
    }

с тем же результатом.

Для ясности вот таблица в MSSQL:

    CREATE TABLE [Control].[InterfaceType](
    [InterfaceTypeId] [tinyint] NOT NULL,
    [Description] [varchar](25) NULL,
 CONSTRAINT [PK_InterfaceType] PRIMARY KEY CLUSTERED 
(
    [InterfaceTypeId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ОБНОВЛЕНИЕ

Я смотрел на SQL, который генерирует EF:

    SELECT [i].[InterfaceTypeId], [i].[Description], [i].[InterfaceID] FROM [Control].[InterfaceType] AS [i]

Откуда он получает InterfaceID?

Ответы [ 7 ]

0 голосов
/ 19 декабря 2018

Я предполагаю, что у вас также есть таблица "Interface", зарегистрированная в контексте, которая содержит ссылку на InterfaceType.Для интерфейса было бы объявлено поле InterfaceTypeId, однако с EF, если вы используете HasOne с ForeignKey, убедитесь, что вы случайно не присвоили что-то вроде:

.HasOne(x => x.InterfaceType).WithOne().HasForeignKey<InterfaceType>("InterfaceId");

В случаеИнтерфейса, имеющего InterfaceType, он будет отображаться как:

.HasOne(x => x.InterfaceType).WithMany();

Это могло бы закрасться в одну из ваших других связанных сущностей.Часто это опечатки, когда автозаполнение выбрало неправильный тип, не замечая вас.Если это сопоставление существует в любом из ваших классов, EF ожидает найти столбец InterfaceId в InterfaceType.Выполните поиск по номеру HasForeignKey<InterfaceType> и посмотрите, получится ли что-нибудь необычное.

0 голосов
/ 19 декабря 2018

Откуда он получает InterfaceID?

Во-первых, должно быть ясно, что это не из показанной "простой" (но явно неполной) модели.

Сгенерированный EF SQL ясно указывает на то, что вы не переименовали столбец, сгенерированный свойством PK, также нет столбца Discriminator, поэтому он не может быть получен из наследования.И вероятность того, что вы явно определили свойство тени с именем InterfaceID и не заметили его, мала.

Все это вместе с тем, что имя InterfaceID соответствует одному из обычных имен EF Core дляИмя свойства / столбца FK для меня является четким указанием на обычный FK, введенный отношениями.Например, наличие второй модели, подобной этой:

public class Interface
{
    public int ID { get; set; }
    // or
    // public int InterfaceID { get; set; }
    public ICollection<InterfaceType> InterfaceTypes { get; set; }
}

Как объяснено в Отношения - одиночное свойство навигации В разделе документации EF Core:

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

, а в прилагаемом примере показана модель Blog / Post только с public List<Post> Posts { get; set; }свойство в Blog выделено.

Все поведения среды выполнения EF Core основаны на метаданных модели.Неважно, какова структура вашей базы данных, тем важнее то, что EF Core думает, что она основана на ваших классах модели, аннотациях данных и быстрой конфигурации, и соответствует ли она схеме базы данных.Самый простой способ проверить это - сгенерировать миграцию и проверить, соответствует ли она схеме базы данных.

Так что, если связь является преднамеренной, вам необходимо обновить базу данных, чтобы она соответствовала вашей модели.В противном случае вам нужно обновить модель, чтобы она соответствовала базе данных - удалив или игнорируя свойство навигации по коллекции (или исправив неверную аннотацию данных / измененную конфигурацию, вызывающую расхождение).

0 голосов
/ 18 декабря 2018

Для меня сработало следующее:

Введите некоторые данные:

insert into [Control].[InterfaceType] values (1, 'Desc1'), (2, 'Desc2');

C #:

class SOContext : DbContext
{
    public DbSet<InterfaceType> InterfaceTypes { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var conn_string = @"Server=(localdb)\mssqllocaldb;Database=Interfaces;Trusted_Connection=Yes;";
        optionsBuilder.UseSqlServer(conn_string);
    }
}

[Table("InterfaceType", Schema = "Control")]
public class InterfaceType
{
    [DatabaseGenerated(DatabaseGeneratedOption.None), Key]
    public byte InterfaceTypeId { get; set; }
    public string Description { get; set; }
    public override string ToString() =>
        $"Id: {InterfaceTypeId} | Description: {Description}";
}

// Output:
// Id: 1 | Description: Desc1
// Id: 2 | Description: Desc2
0 голосов
/ 18 декабря 2018

Просто чтобы проверить, есть ли способ воспроизвести, я создал образец .NET Core Console application, чтобы проверить это, и в моем случае я могу получить данные из БД без каких-либо исключений.

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

Я старался максимально внимательно следовать вашему коду, пытаясь воспроизвести эту проблему, где некоторые вещи мне пришлось изменить.

Я не знаю, какие версии .NET Core и EF Core вы использовали.В моем примере я использовал:

  1. .NET Core 2.2
  2. Microsoft.EntityFrameworkCore 2.2
  3. Microsoft.EntityFrameworkCore.Design 2.2
  4. Microsoft.EntityFrameworkCore.SqlServer 2.2
  5. Microsoft.EntityFrameworkCore.Tools 2.2

Я создал:

  1. Класс модели в соответствии с вашим примером
  2. КонтекстКласс с OnModelCreating для вашего образца

Выполнены следующие dotnet core команды в том же порядке, как указано ниже:

  1. dotnet restore
  2. dotnet build
  3. dotnet ef migrations add InitMgr
  4. dotnet ef database update
  5. Добавлено несколько тестовых записей в таблицу

Скопирован код поиска ваших записей, удалено "[Интерфейсы] "из запроса и отладки кода ниже.

        var _context = new InterfaceTypeContext ();
        List<InterfaceType> types = _context.InterfaceTypes.FromSql ("SELECT * FROM [Control].[InterfaceType]").ToList ();

Мне удалось получить данные из БД.

enter image description here

Также будет полезно, если вы поделитесь Минимальным, Завершенным и Проверяемым примером для кого-то для отладки и помощиВы найдете решение для этого.

0 голосов
/ 18 декабря 2018

Мое понимание этой проблемы заключается в том, что EF создал свойство тени внутри класса вашей модели, возможно, благодаря частично обнаруженным отношениям в вашей Interface модели.

Также я чувствую, что естьнесоответствие между вашим ModelSnapshot, используемым EFCore, и реальным состоянием таблиц в базе данных (возможно, в ожидании миграции).Дважды проверьте, как ваш InterfaceType в <YourDbContext>ModelSnapshot.cs, и проверьте, есть ли имущество, которое вы пропустили.

0 голосов
/ 18 декабря 2018

Возможно попробуйте добавить DatabaseGeneratedAttribute

[Table("InterfaceType")]
public class InterfaceType
{
   [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity),Key()]
   public int InterfaceTypeId { get; set; }
   ...
0 голосов
/ 13 декабря 2018

Во-первых, почему бы не использовать

List<InterfaceType> types = _context.InterfaceTypes.ToList();

Во-вторых, применили ли вы какие-либо изменения к модели и забыли сохранить это в базе данных, так как может быть, что столбец верен вваш класс, но не в вашей базе данных.Это часто что-то, что я забываю делать при использовании Code-FirstModel.

Вот некоторая дополнительная информация о FromSQL: - https://docs.microsoft.com/en-us/ef/core/querying/raw-sql

Подробнее о миграции здесь: - https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/

Надеюсь, это поможет.

...