首页 > 解决方案 > “FromSql”操作的结果中不存在所需的列,并且 [NotMapped] 没有帮助

问题描述

我正在使用 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

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

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

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

解决方案


出现此错误消息有两个原因:

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

显而易见的是,对于 SQL 结果集中的每一列,对象图中必须有一个您要尝试水合的相应属性,在这种情况下,您的Recipient类没有EnvelopeId1.

因为您的查询是一个存储过程,所以在这样的问题中发布 SQL 结果是明智的,因为它会显示您尝试映射到对象图的 SQL 结构。

EnvelopeId1在您的 SQL 表中但不在您的对象图中的原因是因为实体框架已为您尚未为其定义属性的外部创建了影子属性

你没有列出架构,Envelope但我的钱说你有一个等价的ICollection<Recipient>财产。

创建一个NotMapped属性EnvelopeId只是让这个模式更加混乱,而不是我们应该正确地映射它,或者你应该从你的存储过程输出中省略它。

如果要FromSql在 EF(.Net 或 Core)中使用变体,则应始终在架构类中定义导航属性,因为数据库中的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>();
    ...
}

推荐阅读