You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Entity Framework中无主键ATTEMPTED_ANSWER表的关联加载问题求助

解决无主键表的EF读写问题

嘿,作为ASP.NET MVC和EF的新手,碰到无主键现有表的问题确实头疼,我来一步步帮你搞定:

1. 修正实体模型,移除不存在的Id列并配置正确映射

你之前在ATTEMPTED_ANSWER模型里加了数据库不存在的id列并标记为[Key],这就是查询时报错Invalid column name 'id'的根源。现在我们调整模型,适配无主键的数据库表:

修正后的ATTEMPTED_ANSWER模型

public class ATTEMPTED_ANSWER {
    // 新增非映射的Id属性,仅用于内存中标识实体
    [NotMapped]
    public int Id { get; set; }

    // 配置复合主键(用现有字段的唯一组合作为EF识别的主键)
    [Key, Column(Order = 0)]
    [ForeignKey("Question")]
    public int question_id { get; set; }

    // 映射数据库的answered_option_id列,且设为可空类型
    [Key, Column(Order = 1, Name = "answered_option_id")]
    public int? attempted_option_id { get; set; }

    // 映射数据库的answered_text列
    [Key, Column(Order = 2, Name = "answered_text")]
    public string attempted_text { get; set; }

    public QUESTIONS Question { get; set; }
}

关键说明:

  • [NotMapped]:告诉EF这个Id属性不需要映射到数据库列,仅在内存中使用
  • 复合主键:用[Key]+Column(Order)标记现有字段组合作为EF的主键,让EF能正确跟踪实体进行读写
  • 列名映射:因为模型字段名和数据库列名不一致(比如attempted_option_id对应数据库的answered_option_id),必须用Name参数指定数据库列名,避免EF找不到列的错误

2. 可选:用Fluent API配置实体(替代DataAnnotations)

如果你更习惯用Fluent API,也可以在DbContext的OnModelCreating方法里这样配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ATTEMPTED_ANSWER>()
        // 设置复合主键
        .HasKey(a => new { a.question_id, a.attempted_option_id, a.attempted_text })
        // 映射数据库列名
        .Property(a => a.attempted_option_id)
        .HasColumnName("answered_option_id");
    modelBuilder.Entity<ATTEMPTED_ANSWER>()
        .Property(a => a.attempted_text)
        .HasColumnName("answered_text");
    // 配置关联关系
    modelBuilder.Entity<ATTEMPTED_ANSWER>()
        .HasOne(a => a.Question)
        .WithMany(q => q.Attempted_Answers)
        .HasForeignKey(a => a.question_id);
}

3. 查询时自动设置Id属性

现在查询关联数据不会再报错了,接下来实现加载后自动给ATTEMPTED_ANSWER的Id赋值:

var QuestionList = _context.Questions
    .Where(m => m.survey_id == id)
    .Include(m => m.Options)
    .Include(m => m.Attempted_Answers)
    .ToList();

// 给每个ATTEMPTED_ANSWER生成唯一的内存Id
int idCounter = 1;
foreach (var question in QuestionList)
{
    foreach (var answer in question.Attempted_Answers)
    {
        answer.Id = idCounter++;
    }
}

这样每次加载数据后,所有关联的ATTEMPTED_ANSWER实体都会有一个自增的Id值,满足你的需求。

4. 读写操作注意事项

  • 因为用了复合主键,更新或删除ATTEMPTED_ANSWER时,EF会根据复合主键的字段组合定位数据库中的记录
  • 如果attempted_option_id或attempted_text为空,要确保这些值的组合在数据库中是唯一的,否则EF可能会误操作重复记录

内容的提问来源于stack exchange,提问作者Rajinder Nagi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:59:32