【问题标题】:The required column was not present in the results of a 'FromSql' operation and [NotMapped] has not helped“FromSql”操作的结果中不存在所需的列,并且 [NotMapped] 没有帮助
【发布时间】:2019-11-04 21:35:40
【问题描述】:

我正在使用 EF Core 3.0 将数据从 SQL Server 存储过程提取到名为 Recipient 的对象模型中。 Recipient 模型定义了两个属性,EnvelopeIdName,它们不是从存储过程返回的。当我使用FromSqlInterpolated 调用存储过程时,我收到错误消息

“FromSql”操作的结果中不存在所需的列“EnvelopeId1”

根据我在 EF Core 文档中阅读的内容,我应该能够向这些属性添加 [NotMapped] 属性,并且 EF Core 应该能够在读取或写入数据库时​​忽略它们,对吧?不幸的是,这对我不起作用,我收到了上述错误。

如果我在存储过程中添加两列并在我的对象模型中删除[NotMapped] 属性,一切正常。这证明我的其他列/属性名称匹配正确,并且任何地方都没有错字。

我看到了使用DbQuery 而不是DbSet 的建议,但它已被弃用,所以我不想使用它。我已经看到使用两种不同对象模型的建议,一种具有与存储过程结果集完全匹配的属性,但这只会导致大量额外的模型。我在这里做错了什么?

我的对象模型:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MyNamespace.Models
{
    public class Recipient
    {
        [Key]
        public long RecipientId { get; set; }
        [NotMapped]
        public Guid? EnvelopeId { get; set; }
        public string Type { get; set; }
        public string UserId { get; set; }
        public string Email { get; set; }
        [NotMapped]
        public string Name { get; set; }
        public string RoleName { get; set; }
        public int RoutingOrder { get; set; }
        public string Status { get; set; }
        public DateTime StatusDate { get; set; }

        public Recipient() { }
    }
}

我的数据库上下文:

using Microsoft.EntityFrameworkCore;
using MyNamespace.Models;

namespace MyNamespace.Data
{
    public class MyDbContext : DbContext
    {
        public virtual DbSet<Recipient> Recipient { get; set; }
        public virtual DbSet<Envelope> Envelope { get; set; }
        public virtual DbSet<Template> Template { get; set; }

        public MyDbContext (DbContextOptions<MyDbContext> options) : base(options)   
        { }
    }
}

我的失败代码:

FormattableString sql = $"EXEC dbo.MyStoredProcedure @Param1={param1}, @Param2={param2}";
var result = await MyDbContext.Recipient.FromSqlInterpolated(sql).ToListAsync();

编辑 #1 ...

Envelope 对象模型:

public class Envelope
{
    [Key]
    public Guid EnvelopeId { get; set; }
    public string Status { get; set; }
    public DateTime StatusDate { get; set; }
    public DateTime StatusPollDate { get; set; }
    [NotMapped]
    public List<Recipient> Recipients { get; set; }

    public Envelope() {
        Recipients = new List<Recipient>();
        StatusPollDate = DateTime.Parse("1753-01-01");
    }
}

存储过程结果集架构:

[RecipientId] bigint,
[Type] varchar(20),
[UserId] varchar(50),
[Email] varchar(100),
[RoleName] varchar(100),
[RoutingOrder] int,
[Status] varchar(13),
[StatusDate] datetime

在阅读了一些答案和 cmets 后,我意识到 EF 正在创建一个影子属性,因为它识别出 RecipientEnvelope 对象的子对象。老实说,我真的应该在结果集中包含EnvelopeId(并删除[NotMapped])以安抚EF Core。我没有在存储过程的结果集中返回EnvelopeId 的唯一原因是因为我一开始就将它作为输入参数传入,并认为传入某些内容然后取回它是在浪费网络资源在每个结果集记录中。

现在我不知道EnvelopeId1 来自哪里,但这就是错误消息中提到的内容。正如您在我的对象模型和存储过程结果集架构中看到的那样,我只引用了EnvelopeId。我最好的猜测是,当 EF Core 决定创建一个影子属性时,它不能使用 EnvelopeId,因为我已经在我的对象模型中拥有它,所以它创建了一个名为 EnvelopeId1 的对象,然后期望它出现在存储过程的结果集。

【问题讨论】:

  • EnvelopeId1EnvelopeId ?
  • EnvelopeId 听起来像一个 pirmary 键,所以你会遇到一些问题使它成为可选的。除此之外,您还得到了 tymtam 指出的命名不匹配。
  • @tymtam,这不是错字。我的代码或存储过程结果集中的任何地方都没有定义EnvelopeId1,但错误消息显示的是EnvelopeId1
  • @Christopher,是的 EnvelopeId 在概念上是不同对象 (Envelope) 的主键,但我定义 Recipient 对象模型的方式,我只将其称为Guid? 所以不需要。但是你的评论让我觉得这可能与Guid?的数据类型有关。我用long 的数据类型尝试了类似的操作,[NotMapped] 属性完全按预期工作。
  • 您需要发布 Envelope 的架构以及 SP 输出示例。仅仅因为您认为不需要属性,并不意味着它不是必需的。 :)

标签: c# sql-server .net-core-3.0 ef-core-3.0


【解决方案1】:

这个错误信息有两个原因:

“FromSql”操作的结果中不存在所需的列“EnvelopeId1”

很明显,对于 SQL 结果集中的每一列,对象图中必须有一个对应的属性,在这种情况下,您的 Recipient 类没有 EnvelopeId1

由于您的查询是一个存储过程,因此最好将 SQL 结果发布到这样的问题中,因为它会显示您尝试映射到对象图的 SQL 结构。

EnvelopeId1 在您的 SQL 表中但不在您的对象图中的原因是因为实体框架为 foreign that you have not defined a property 创建了一个Shadow Property

您还没有列出 Envelope 的架构,但我的钱说您有一个等效的 ICollection&lt;Recipient&gt; 属性。

EnvelopeId 创建一个NotMapped 属性只会使这个架构更加混乱,我们应该正确映射它,或者你应该从存储过程输出中省略它。

如果您要在 EF(.Net 或 Core)中使用 FromSql 变体,则应始终将架构类中的导航属性定义为 ShadowAuto数据库中的外键列会引起麻烦,除非您明确省略数据库中存在但架构中不存在的列

这意味着没有SELECT *,您必须明确定义所有列。

解决方案 1:修改存储过程
您可以简单地修改您的存储过程以不从您的收件人表中返回EvelopeId1

解决方案 2:为数据类中所有关系的两端完全定义属性
在这里,我使用EnvelopeId1 作为名称,因此不需要迁移,但是我建议您将其更改为EnvelopeId,我个人使用数据库中以数字结尾的任何字段的存在作为指示某些我的人际关系没有得到充分定义。

public class Recipient
{
    [Key]
    public long RecipientId { get; set; }
    public Guid? EnvelopeId1 { get; set; }
    [ForeignKey(nameof(EnvelopeId1))]
    public virtual Envelope Envelope { get; set; }
    public string Type { get; set; }
    public string UserId { get; set; }
    public string Email { get; set; }
    [NotMapped]
    public string Name { get; set; }
    public string RoleName { get; set; }
    public int RoutingOrder { get; set; }
    public string Status { get; set; }
    public DateTime StatusDate { get; set; }

    public Recipient() { }
}

这是基于您的 Envelope 类的定义类似于此部分定义的假设:

public class Envelope
{
    [Key]
    public Guid EnvelopeId { get; set; }
    public virtual ICollection<Recipient> Recipients { get; set; } = new Hashset<Recipient>();
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-20
    • 2020-07-02
    • 2019-09-26
    • 1970-01-01
    • 1970-01-01
    • 2011-09-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多