【问题标题】:Entity Framework only saving some of my fields实体框架只保存我的一些字段
【发布时间】:2022-02-03 23:35:05
【问题描述】:

我正在尝试编写一个小型 Web api,这是我第一次使用实体框架。

我的问题是只保存了我的部分数据。

我有这个 GET 方法,它只是应该将模型保存到数据库中:

[HttpGet]
public async Task<IActionResult> PopulateDB()
{
    var playerOneQuest = new StatusModel
    {
        PlayerId = "1",
        points= 250,
        completedSteps = new List<Status>()
        {
            new Status
            {
                Id = 11,
                Index = 1
            } 
        }
    };
    
    _context.Quest.Add(playerOneQuest);
    await _context.SaveChangesAsync();

    return Ok("Added data"); // Breakpoint here
}

当我在最后一行添加断点时,_context 看起来对所有数据都很好,但只要方法执行完毕,completedSteps 就会为空。其他数据 - PlayerIdpoints - 保存得很好。

我曾尝试在调用SaveChangesAsync() 之前添加此_context.Entry(playerOneQuest).State = EntityState.Added;,但在方法执行后completedSteps 仍以null 结束。

_context 定义为:

public class QuestContext : DbContext
{
    public QuestContext(DbContextOptions<QuestContext> options)
        : base(options){  }

    public virtual DbSet<StatusModel> Quest { get; set; } = null!;
}

使用的2个模型定义为:

public class StatusModel
{
    [Key]
    public string? PlayerId {get;set;}
    public long Points{get;set;}
    public List<Status> completedSteps {get;set;} = null!;
}

public class Status
{
    public long Id {get;set;}
    public int Index {get;set;}
}

为了更好地衡量,这就是将数据库上下文添加到我的网络应用程序的方式:

builder.Services.AddDbContext<QuestContext>(opt =>
opt.UseInMemoryDatabase("Quests"));

【问题讨论】:

  • 您是否尝试添加公共虚拟 DbSet Status { get;放; } 到 QuestContext?
  • @sam 是的,我确实尝试过。它没有效果,所以我再次删除它
  • 实际上需要将状态添加到上下文中。根据 EF 导航规则,如果您将子实体定义/添加到上下文,则父实体将由 EF 本身解析。所以,我会添加 Status 并且在我们正在讨论的这种情况下,拥有 StatusModel 不是强制性的。您能否编辑您的问题,仅向我们展示如何在您的 StatusModel 和 Status 实体/对象中定义导航的部分?
  • 您的 Status 和 StatusModel 实体看起来如何?您是否设置了导航属性、外键等? PK 是如何定义的 - 你真的可以设置 ID 还是它们是由数据库生成的?
  • @Sam 我之前确实读过一些类似的东西,但我不知道如果上下文是用“Status”定义的,我会在数据库中添加“StatusModel”

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


【解决方案1】:

问题在于您没有指定要加载其他数据。

https://docs.microsoft.com/en-us/ef/core/querying/related-data/

您可以通过代理启用延迟加载:https://docs.microsoft.com/en-us/ef/core/querying/related-data/lazy 或通过在查询中使用 .Include() 来明确。

var completeList = await _questContext.Quest.Include(i =&gt; i.completedSteps).ToListAsync();

据我所知,您的实体已按照约定正确设置。这意味着您不需要为实体配置任何其他内容 - 当您只想拥有简单的一对多关系时。

在注入QuestContext 的控制器上的示例:

[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    private readonly QuestContext _questContext;

    public MyController(QuestContext questContext)
    {
        _questContext = questContext;
    }

    [HttpGet]
    public async Task<NoContentResult> Get()
    {
        var playerOneQuest = new StatusModel
        {
            PlayerId = "1",
            Points = 250,
            completedSteps = new List<Status>()
            {
                new Status
                {
                    Id = 11,
                    Index = 1
                }
            }
        };

        _questContext.Quest.Add(playerOneQuest);
        await _questContext.SaveChangesAsync();
        return NoContent();
    }

    [HttpGet("check")]
    public async Task<List<StatusModel>> Check()
    {
        var completeList = await _questContext.Quest.Include(i => i.completedSteps).ToListAsync();
        return completeList;
    }
}

【讨论】:

  • 你的建议看起来很棒,很好,很简单。我仅有的两个问题是:1. MS 的示例使用“使用(var context = new BloggingContext())”,并且我的上下文被注入了 DbContextOptions 参数,所以我不知道该怎么做...... 2. 调用我的_context.Quest 通常没有“.Include()”。所以我真的希望你是对的,但我不知道该怎么做。
  • 我已经用一个控制器更新了答案,其中QuestContext 被注入到构造函数中。
  • 谢谢。从字面上看,这就是我的代码中已有的内容。 _questContext.Quest 只是没有 .Include() 。我不知道要改变什么来解决这个问题
  • 什么意思?它不在 Quest 对象上??
  • 你在用这个吗? using Microsoft.EntityFrameworkCore;
【解决方案2】:

在模型中:

 public class StatusModel
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id {get;set;}
        public long Points{get;set;}
        public List<Status> completedSteps {get;set;} = new List<Status>();
    }
    
    public class Status
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id {get;set;}
        public int Index {get;set;}
        public long StatusModelId {get;set;}
        public StatusModel {get;set;}
    }

OnModelCreating 中的存储上下文中:

modelBuilder.Entity<Status>().HasOne(c => c.StatusModel).WithMany(c => c.completedSteps)
                    .HasForeignKey(c => c.StatusModelId).OnDelete(DeleteBehavior.Cascade);

您必须设置对象之间的关系。您可以在 Context 中使用 fluent API 或直接在模型中使用属性来执行此操作。同样在您的上下文中,您需要两个 DbSet,用于 status 和 statusModel。

没有测试就写了答案,但应该足够接近。这是关系的链接:Relations in EF

我错过了你的 PlayerId - 我猜这是对 Player 的 FK?比你还必须建立这种关系。如果您打算将其作为 PK,它可能不应该为空。

【讨论】:

  • 根据我在其他地方以及您的链接中阅读的内容,不需要所有这些额外的步骤。我已尝试遵循您的所有建议,唯一改变的是“new List();”这导致“completedSteps”每次都是一个空列表,而不是简单的 null
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多