实体框架 - 无效的列名称"*_ID"



我已经将其缩小到代码优先和数据库优先EF之间的一些问题,但我不确定如何解决它。我会尽量说清楚,但老实说,我自己在这里缺少一些理解。 这是实体框架 4.4

我继承了一个使用实体框架的项目,但许多实际文件被删除,没有真正的方法可以返回。 我重新添加了 EF(数据库优先)并复制了围绕该项目构建的 T4 设置。 它生成了所有数据库模型的代码版本和一个 DBContext 代码文件。

如果我的连接字符串看起来像"普通".NET 连接字符串,则我收到有关无效列的错误 名称"ProcessState_ID"不存在。 ProcessState_ID根本不在代码库中,也不在 EDMX 文件或任何文件中。 这似乎是查询中的一些自动 EF 转换。

当我使连接字符串与实体框架模型匹配时,它工作正常。

现在,在尝试将以前的代码与实体框架匹配时,我想保留"普通".NET 连接字符串。

所以我在这里有两个问题:1. 在代码中从普通连接字符串转到 EF 连接字符串的好方法是什么?2.这里有没有看到的另一种修复程序可以阻止无效的列名错误吗?

检查是否有

任何 ICollect。

我发现的是,当您有一个引用表的 ICollection 并且没有它可以找出的列时,它会创建一个供您尝试在表之间建立连接。 这特别发生在ICollection上,并驱使我"笨拙"地试图弄清楚。

对于那些

(像我一样)没有立即理解其他 2 个答案的人来说,这是一个较晚的条目。

所以。。。

  • EF 正在尝试使用父表键引用映射到预期名称
  • 由于实际的外键名称在数据库中被"更改或缩短",子表关系
  • 您收到上面提到的消息

(此修复程序可能因 EF 版本而异)

对我来说,修复是:
将"外键"属性添加到模型中

public partial class Tour
{
    public Guid Id { get; set; }
    public Guid CategoryId { get; set; }
    [Required]
    [StringLength(200)]
    public string Name { get; set; }
    [StringLength(500)]
    public string Description { get; set; }
    [StringLength(50)]
    public string ShortName { get; set; }
    [StringLength(500)]
    public string TourUrl { get; set; }
    [StringLength(500)]
    public string ThumbnailUrl { get; set; }
    public bool IsActive { get; set; }
    [Required]
    [StringLength(720)]
    public string UpdatedBy { get; set; }
    [ForeignKey("CategoryId")]
    public virtual TourCategory TourCategory { get; set; }
}

我终于想通了。

首先正在做EF6数据库,我想知道"范围未知列"错误 - 由于某种原因,它正在生成表名下划线列名,并试图找到一个不存在的列。

就我而言,我的一个表对另一个表中的同一主键有两个外键引用 - 如下所示:

Animals            Owners
=======            ======
AnimalID (PK)      Pet1ID    <- FK to AnimalID
                   Pet2ID    <- also FK to AnimalID

EF 生成了一些奇怪的列名,如 Owners_AnimalID1Owners_AnimalID2,然后继续破坏自己。

这里的诀窍是,这些令人困惑的外键需要使用 Fluent API 向 EF 注册!

在主数据库上下文中,重写 OnModelCreating 方法并更改实体配置。最好有一个单独的文件来扩展EntityConfiguration类,但您可以内联执行此操作。

无论如何,您都需要添加如下内容:

public class OwnerConfiguration : EntityTypeConfiguration<Owner>
{
    public OwnerConfiguration()
    {
        HasRequired(x => x.Animals)
            .WithMany(x => x.Owners)  // Or, just .WithMany()
            .HasForeignKey(x => x.Pet1ID);
    }
}

这样,EF 将(也许)开始按您的预期工作。繁荣。

此外,如果您将上述内容与可为空的列一起使用,则会遇到相同的错误 - 只需使用 .HasOptional() 而不是 .HasRequired()

<小时 />

这是让我克服驼峰的链接:

https://social.msdn.microsoft.com/Forums/en-US/862abdae-b63f-45f5-8a6c-0bdd6eeabfdb/getting-sqlexception-invalid-column-name-userid-from-ef4-codeonly?forum=adonetefx

然后,Fluent API 文档会提供帮助,尤其是外键示例:

http://msdn.microsoft.com/en-us/data/jj591620.aspx

您还可以将配置放在密钥的另一端,如下所述:

http://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx。

我现在遇到了一些新问题,但那是缺失的巨大概念差距。希望对您有所帮助!

假设:

  • Table
  • OtherTable
  • OtherTable_ID

现在选择以下方法之一:


一)

删除ICollection<Table>

如果在检索Table时遇到与OtherTable_ID相关的错误,请转到OtherTable模型并确保其中没有ICollection<Table>。如果未定义关系,框架将自动假定您必须具有 FK 到 OtherTable 并在生成的 SQL 中创建这些额外的属性。

这个答案的所有功劳都属于@LUKE。上面的答案是他在@drewid答案下的评论。我认为他的评论很干净,然后我重写了它作为答案。


二)

  • OtherTableId添加到Table

  • 在数据库中的Table中定义OtherTableId
对我来说,

这种行为的原因是使用 Fluent API 定义的映射存在问题。我有 2 个相关类型,其中类型 A 具有可选的类型 B 对象,而类型 B 具有许多 A 对象。

public class A 
{
    …
    public int? BId {get; set;}
    public B NavigationToBProperty {get; set;}
}
public class B
{
    …
    public List<A> ListOfAProperty {get; set;}
}

我用流畅的 api 定义了映射,如下所示:

A.HasOptional(p=> p.NavigationToBProperty).WithMany().HasForeignKey(key => key.BId);

但问题是,类型 B 具有导航属性List<A>,因此我SQLException Invalid column name A_Id

我将Visual Studio Debug附加到EF DatabaseContext.Database.Log以将生成的SQL输出到VS Output->Debug窗口

db.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);

生成的 SQL 与 B 表有 2 个关系 ->一个具有正确的 id,另一个与A_Id

问题的问题是,我没有将此B.List<A>导航属性添加到映射中。

所以这就是在我的情况下正确的映射必须的方式:

A.HasOptional(p=> p.NavigationToBProperty).WithMany(x => x.ListOfAProperty).HasForeignKey(key => key.BId);

就我而言,我错误地定义了由两个外键组成的主键,如下所示:

HasKey(x => x.FooId);
HasKey(x => x.BarId);
HasRequired(x => x.Foo)
    .WithMany(y => y.Foos);
HasRequired(x => x.Bar);

我得到的错误是"Bar_ID列名无效"。

正确指定复合主键解决了问题:

HasKey(x => new { x.FooId, x.BarId });
...

就我而言,此问题的原因是迁移的数据库上缺少外键约束。因此,现有的虚拟 ICollection 未成功加载。

对我来说,这是因为EF的复数问题。对于以"-Status"结尾的表,EF 认为单数是"-Statu"。将实体和数据库表名称更改为"-StatusTypes"修复了它。

这样,您就不需要在每次更新实体模型时重命名实体模型。

我也有这个问题,似乎有几个不同的原因。对我来说,在包含导航对象的父类中,id属性被错误地定义为int而不是long。数据库中的 id 字段被定义为 bigint,对应于 C# 中的 long。这不会导致编译时错误,但确实导致了与 OP 相同的运行时错误:

// Domain model parent object
public class WidgetConfig 
{
    public WidgetConfig(long id, int stateId, long? widgetId)
    {
        Id = id;
        StateId = stateId;
        WidgetId = widgetId;
    }
    private WidgetConfig()
    {
    }
    public long Id { get; set; }
    public int StateId { get; set; }
    // Ensure this type is correct
    public long? WidgetId { get; set; } 
    public virtual Widget Widget { get; set; }
}
// Domain model object
public class Widget
{
    public Widget(long id, string name, string description)
    {
        Id = id;
        Name = name;
        Description = description;
    }
    private Widget()
    {
    }
    public long Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}
// EF mapping
public class WidgetConfigMap : EntityTypeConfiguration<WidgetConfig>
{
    public WidgetConfigMap()
    {
        HasKey(x => x.Id);
        ToTable(nameof(WidgetConfig));
        Property(x => x.Id).HasColumnName(nameof(WidgetConfig.Id)).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
        Property(x => x.StateId).HasColumnName(nameof(WidgetConfig.StateId));
        Property(x => x.WidgetId).HasColumnName(nameof(WidgetConfig.WidgetId));
    }
}   
// Service
public class WidgetsService : ServiceBase, IWidgetsService
{
    private IWidgetsRepository _repository;
    public WidgetsService(IWidgetsRepository repository)
    {
        _repository = repository;
    }
    public List<WidgetConfig> ListWithDetails()
    {
        var list = _repository.ListWithDetails();
        return new WidgetConfigMapping().ConvertModelListToDtoList(list).ToList();
    }
}   
// Repository
public class WidgetsRepository: BaseRepository<WidgetConfig, long>, IWidgetsRepository
{
    public WidgetsRepository(Context context)
        : base(context, id => widget => widget.Id == id)
    {
    }
    public IEnumerable<WidgetConfig> ListWithDetails()
    {
        var widgets = Query
            .Include(x => x.State)
            .Include(x => x.Widget);
        return widgets;
    }
}

对我来说,问题是我在应用程序中映射了两次表-一次通过代码优先,一次通过数据库优先。

在我的情况下,删除任何一个都可以解决问题。

就我而言,我已经有一个数据库(首先是数据库)。感谢这里的所有评论,我找到了我的解决方案:

表必须具有关系,但列的名称需要不同,并添加 ForeignKey 属性。

[ForeignKey("prestadorId")]public virtual AwmPrestadoresServicios Colaboradores { get; set; }

也就是说,PRE_ID是PK,但另一个表中的FK是PRESTADOR_ID,然后它就可以工作。感谢这里的所有评论,我找到了我的解决方案。EF以神秘的方式工作。

我通过实施此解决方案解决了它

https://jeremiahflaga.github.io/2020/02/16/entity-framework-6-error-invalid-column-name-_id/

(互联网档案馆存档版本:https://web.archive.org/web/20210224204440/https://jeremiahflaga.github.io/2020/02/16/entity-framework-6-error-invalid-column-name-_id/)

简历>

modelBuilder.Entity<Friendship>()
         .ToTable("Friendship")
         .HasKey(x => new { x.Person1Id, x.Person2Id })
         .HasRequired(x => x.PersonOne)
            .WithMany()
            .HasForeignKey(x => x.Person1Id); // WRONG, “Invalid column name ‘Person_Id’.”
modelBuilder.Entity<Friendship>()
         .ToTable("Friendship")
         .HasKey(x => new { x.Person1Id, x.Person2Id })
         .HasRequired(x => x.PersonOne)
            .WithMany(x => x.Friendships) 
            .HasForeignKey(x => x.Person1Id); 
// The solution is to add .WithMany(x => x.Friendships) in your configuration

就我而言,我的种子方法数据仍在调用在上次迁移中删除的表列。如果您使用的是自动映射程序,请仔细检查您的映射。

如果同一表上的导航属性遇到此问题,则必须更改属性的名称。

例如:

Table : PERSON
Id
AncestorId (with a foreign key which references Id named Parent) 

您必须更改AncestorId才能PersonId

EF 似乎正在尝试创建一个密钥ParentId因为它找不到名为 Ancestor 的表......

编辑:这是数据库优先的修复程序!

如果多次

引用同一个表的外键,则可以使用 InverseProperty

像这样的东西——

[InverseProperty("MyID1")]
public virtual ICollection<MyTable> set1 { get; set; }
[InverseProperty("MyID2")]
public virtual ICollection<MyTable> set2 { get; set; }

对我来说(使用Visual Studio 2017和实体框架6.1.3下的数据库优先模型),重新启动Visual Studio和Rebuild后问题消失了。

我的问题与表上的自定义触发器有关

如果您与表没有任何一对多关系,我的问题最终是我有一个 MS SQL 触发器引用旧的/不存在的列。

我忘了将导航属性标记为虚拟

public Guid GroupId { get; set; }
public Group Group { get; set; }

->

public Guid GroupId { get; set; }
public virtual Group Group { get; set; }

TLDR:将虚拟属性设置为可为空。

我遇到了一个奇怪的问题,因为属性未设置为可为空,它试图寻找另一个表,从而创建了"*_ID1"。我只是将虚拟属性设置为可为空并且它可以工作。

[ForeignKey("Type")]
public virtual Type? Type{ get; set; }

最新更新