.NET Core实体框架InvalidOperationException

5

我有一个简单的模型

[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?

你为什么要使用 FromSql 呢? - Crowcoder
是的,那可能会解决问题(正如我所说,我对此很陌生),但这应该有效,不是吗? - Nigel B
这个表中的列名确切地是“InterfaceTypeId”还是“InterfaceID”? - Crowcoder
也许有一些流畅的API配置可以将“InterfaceTypeId”映射到列名“InterfaceId”吗? - Gerald Chifanzwa
我不知道这是否有帮助,但为什么你要从[Interfaces].[Control].[InterfaceType]选择列,而不是从[Control].[InterfaceType]选择呢? - Viktors Telle
显示剩余7条评论
7个回答

6
它是从哪里获取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文档中的Single Navigation Property章节所述:

仅包含一个导航属性(没有反向导航和外键属性)就足以通过约定定义关系。

附带示例展示了只有public List<Post> Posts { get; set; }属性的Blog模型和Post模型。
所有EF Core运行时行为都基于模型元数据。数据库结构并不重要,更重要的是EF Core根据您的模型类、数据注释和流畅配置认为它是什么,并且是否与数据库架构匹配。检查的简单方法是生成迁移并检查它是否与数据库架构匹配。
因此,如果关系是有意的,则必须更新数据库以匹配您的模型。否则,您需要更新模型以匹配数据库-通过删除或忽略集合导航属性(或更正导致不一致的无效数据注释/流畅配置)。

2
我对这个问题的理解是,EF 在您的模型类中创建了一个影子属性,可能是通过部分发现在您的Interface模型中的关系而创建的。
另外,我感觉您的 ModelSnapshot 与数据库表的实际状态存在不匹配(可能是由于未完成的迁移)。请仔细检查<YourDbContext>ModelSnapshot.cs中的InterfaceType,并检查是否有您遗漏的属性。

2
我的猜测是,您的上下文中还注册了一个“Interface”表,其中保存着对InterfaceType的引用。Interface应该声明了一个InterfaceTypeId字段,但是如果您在使用HasOne与ForeignKey时,检查一下是否意外地分配了类似于以下内容的东西: .HasOne(x => x.InterfaceType).WithOne().HasForeignKey<InterfaceType>("InterfaceId"); 在Interface有InterfaceType的情况下,它将被映射为以下内容: .HasOne(x => x.InterfaceType).WithMany(); 这可能已经潜入了您的其他关联实体之一。通常这些都是拼写错误,自动完成选择了错误的类型而您没有注意到。如果任何一个类上存在这种映射,EF将期望在InterfaceType上找到一个InterfaceId列。搜索HasForeignKey<InterfaceType>并查看是否有任何异常。

这也是我的猜测,也许楼主可以发布模型构建器或查看smss中的“查看依赖项”。 - Laughing Lemonade

1

首先为什么不使用 List<InterfaceType> types = _context.InterfaceTypes.ToList();

其次,您是否对模型进行了任何更改并忘记将其持久化到数据库中?因为在您的类中列是正确的,但在数据库中可能不正确。当使用 Code-First Model 时,我经常会忘记这样做。

有关 FromSQL 的更多信息,请参见:https://learn.microsoft.com/en-us/ef/core/querying/raw-sql

有关迁移的更多详细信息,请参见:https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/

希望这可以帮助您。


使用您的建议,我得到了SqlException: Invalid object name错误,即使它是正确的。这里肯定有一些接线问题,但我就是想不出来。 - Nigel B
@RedEyedMonster,你尝试过gerryc.inc的建议并检查流畅的API是否进行了任何类型的映射吗?这可以在您的数据库上下文所在的同一类中看到。 - Steve
@RedEyedMonster,我看到您编辑了原始问题,添加了模式和数据库名称? - Steve
尝试让它尽可能易懂,我开始怀疑是否是数据库端的问题,但看不到明显的原因,例如权限等。如果其他查询也无法工作,那么这就更有意义了,但只有这个表格出现问题。 - Nigel B
@RedEyedMonster 好的,另一个提示,既然你正在使用Schema,请确保将你的对象指向正确的dbcontext中的schemas,并使用流畅的API进行操作,然后再尝试使用List<InterfaceType> types = _context.InterfaceTypes.ToList(); - Steve
显示剩余2条评论

0

可以尝试添加DatabaseGeneratedAttribute

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

遗憾的是,仍然得到相同的结果。 - Nigel B

0

为了检查是否有办法可以复现,我创建了一个示例.NET Core控制台应用程序来检查这个问题,在我的情况下,我能够从数据库中检索数据而没有任何异常。

我理解您有其他模型,其中相同的代码正在工作,如果您将有问题的代码移出原始解决方案,您可能会发现您错过了一些明显的东西。

我尽可能地按照您的代码进行了尝试以重现此问题,但我不得不更改其中的一些内容。

我不知道您使用了哪个.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

如果您提供最小化、完整性和可验证的示例(MCVE),将有助于他人调试并帮助您找到解决方案。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接