.NET核心实体框架InvalidOperationException

时间:2018-12-13 13:00:22

标签: c# entity-framework .net-core entity-framework-core

我有一个简单的模型

[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:'FromSql'操作的结果中没有所需的列'InterfaceID'。

我正在类似的其他方法中使用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]

更新

我查看了EF生成的SQL:

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

它从哪里获取InterfaceID?

7 个答案:

答案 0 :(得分:5)

  

它从哪里获取InterfaceID?

首先,应该清楚,它不是来自所示的“简单”(但显然是不完整的)模型。

由EF生成的SQL清楚地表明您没有重命名PK属性生成的列,也没有Discriminator列,因此它不能来自继承。而且,您明确定义了称为InterfaceID的阴影属性而没有注意到它的机会很小。

所有这些以及名称InterfaceID与FK属性/列名的EF核心常规名称之一匹配的事实,清楚地表明了由关系引入的常规FK。例如,具有第二个这样的模型:

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

Relationships - Single Navigation Property EF Core文档主题中所述:

  

仅包含一个导航属性(没有反向导航,也没有外键属性)就足以具有由约定定义的关系。

及其随附的示例显示了Blog / Post模型,其中public List<Post> Posts { get; set; }中只有Blog属性被突出显示。

所有EF Core运行时行为均基于模型元数据。数据库的结构无关紧要,更重要的是EF Core认为它基于模型类,数据注释和流畅的配置,以及是否与数据库架构匹配。一种更简单的检查方法是生成迁移并检查其是否与数据库模式匹配。

因此,如果关系是有意的,则必须更新数据库以匹配模型。否则,您需要更新模型以匹配数据库-通过删除或忽略集合导航属性(或更正导致差异的无效数据注释/流利配置)。

答案 1 :(得分:2)

我对这个问题的理解是,EF创建了一个Shadow Property 在模型类中,可能是由于在Interface模型中部分发现的关系所致。

我还感到EFCore使用的ModelSnapshot与数据库中表的实际状态(可能是由于挂起的迁移)不匹配。仔细检查您在InterfaceType中的<YourDbContext>ModelSnapshot.cs的状况,并检查您是否缺少某个属性。

答案 2 :(得分:1)

首先为什么不使用

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/

我希望这会有所帮助。

答案 3 :(得分:1)

我的猜测是,您还有一个在上下文中注册的“接口”表,该表包含对InterfaceType的引用。接口将声明一个InterfaceTypeId字段,但是对于EF,如果将HasOne与ForeignKey一起使用,请检查是否没有意外分配以下内容:

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

对于具有InterfaceType的接口,它将被映射为:

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

这可能已经潜入您的其他关联实体之一。通常,这些是拼写错误,在这种情况下,自动完成功能在没有引起您注意的情况下选择了错误的类型。如果您的任何类上都存在该映射,则EF将期望在InterfaceType上找到一个InterfaceId列。在HasForeignKey<InterfaceType>上进行搜索,看看是否有什么异常之处。

答案 4 :(得分:0)

也许尝试添加 DatabaseGeneratedAttribute

[Table("InterfaceType")]
public class InterfaceType
{
   [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity),Key()]
   public int InterfaceTypeId { get; set; }
   ...

答案 5 :(得分:0)

仅检查是否有可以重现的方法,创建了一个示例.NET Core Console application进行检查,就我而言,我能够毫无例外地从数据库中检索数据。

  

我了解您还有其他可以使用相同代码的模型,   如果您将有问题的代码移出原始解决方案之外,则可能是   能够弄清楚您是否缺少明显的东西。

我试图尽可能地遵循您的代码,以尝试重现此问题,在此我必须进行一些更改。

我不知道您使用了哪个.NET CoreEF 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. 在表中添加了一些测试记录

复制您的记录检索代码,从查询中删除“ [Interfaces]”并调试下面的代码。

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

我能够从数据库中检索数据。

enter image description here

  

如果您共享Minimal, Complete, and Verifiable example供他人调试并帮助您找到解决方案,这也将有所帮助   这个。

答案 6 :(得分:0)

以下对我有用:

插入一些数据:

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