【问题标题】:Entity Framework throws invalid column name User_Id error实体框架抛出无效的列名 User_Id 错误
【发布时间】:2016-10-06 16:50:23
【问题描述】:

我正在尝试在我的项目中创建和关联实体。用户可以有许多角色、声明和登录。另一方面,一个声明或登录只能有一个用户,而一个角色也可以有多个用户。我有用 fluent API 定义的关系:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    modelBuilder.Entity<User>().HasMany(u => u.Claims).WithRequired(cl => cl.User).Map(m => m.MapKey("User"));
    modelBuilder.Entity<Claim>().HasRequired(cl => cl.User).WithMany(u => u.Claims).Map(m => m.MapKey("User"));

    modelBuilder.Entity<User>().HasMany(u => u.Logins).WithRequired(lg => lg.User).Map(m => m.MapKey("User"));
    modelBuilder.Entity<Login>().HasRequired(lg => lg.User).WithMany(u => u.Logins).Map(m => m.MapKey("User"));

    modelBuilder.Entity<User>().HasMany(u => u.Roles).WithMany(ro => ro.Users).Map(userRoles => 
    {
        userRoles.ToTable("Users_Roles");
        userRoles.MapLeftKey("User");
        userRoles.MapRightKey("Role");
    });     

    modelBuilder.Entity<Role>().HasMany(ro => ro.Users).WithMany(u => u.Roles).Map(userRoles => 
    {
        userRoles.ToTable("Users_Roles");
        userRoles.MapLeftKey("User");
        userRoles.MapRightKey("Role");
    }); 
}

如您所见,我的数据库表不遵循实体框架的约定,而不是使用“User_Id”作为外键,外键简单地命名为“用户”。但是,我不断收到此“无效的列名:User_Id”异常消息。

我尝试通过调用方法Map()和MapKey()来用上面的代码定义外键列名,但没有占上风。为什么会这样?我是不是写错了映射代码?有人可以帮忙吗?

PS:异常报错信息非常无用,不知道这个列名错误与哪个表相关联。有谁知道如何让异常消息显示表名,而不仅仅是列名?谢谢。

我还添加了用于声明、登录和角色实体的代码(删除了不相关的方法)。

public class Claim
{
    public string Id { get; protected set; }
    public string Type { get; protected set; }
    public string Value { get; protected set; }

    public virtual User User { get; protected set; }

    public Claim() { }

    public Claim(string id, User user, string type, string value)
    {
        Id = id;
        User = user;
        Type = type;
        Value = value;
    }
}

public class Login
{
    public string Id { get; protected set; }
    public string Provider { get; protected set; }
    public string Key { get; protected set; }
    public DateTime? DateLoggedin { get; protected set; }

    public virtual User User { get; protected set; }

    public Login() { }

    public Login(string id, string provider, string key, DateTime? dateLoggedIn = null)
    {
        Id = id;
        Provider = provider;
        Key = key;
        DateLoggedin = dateLoggedIn;
    }
}

public class Role
{
    public string Id { get; protected set; }
    public string Title { get; protected set; }
    public string Description { get; protected set; }
    public bool IsAdmin { get; protected set; }
    public bool IsBanned { get; protected set; }

    public IList<User> Users { get; protected set; }

    public Role() { }

    public Role(string id, string title, string description, bool isAdmin, bool isBanned)
    {
        Id = id;
        Title = title;
        Description = description;
        IsAdmin = isAdmin;
        IsBanned = isBanned;
    }
}

【问题讨论】:

  • 您的实体模型是否有一个用户属性作为键?至于错误消息,这是运行时错误,还是您正在使用迁移?如果迁移,请在更新时使用 -verbose 开关以查看有关错误的更多详细信息...例如 update-database -verbose。最后,在没有看到模型类的情况下,我质疑 ManyToMany 映射...您是说一个用户有很多用户...而一个角色有很多角色?您的用户实体上有 ICollection 用户吗?
  • @MatthewHilgenfeld:是的,这是一个错字,感谢您的指出,刚刚修复它。当我调试我的网站并尝试注册用户时,这是运行时错误。 User 作为 Claim 和 Login 实体的虚拟属性存在,而对于 Role 实体,它是虚拟属性 IList.
  • 我通常使用属性作为键,并使用例如映射HasRequired().WithMany().HasForeignKey(),但我没有看到您定义的映射有任何问题。是否有另一个未显示的实体具有约定试图为其定义键的用户属性?
  • 我明白了。我可以使用属性作为键,但是除非我想要不同的约定,否则我将不得不以某种方式定义列名。不,我没有另一个定义了“用户”属性的实体。我想如果我至少可以确定这个无效列名错误对应于哪个表,我可以更接近解决方案。现在它可以与任何表格一起使用,这更令人困惑。
  • @MatthewHilgenfeld:既然你提到了它,我意识到我的所有属性都是受保护的(不是公共的),并且我有所有实体的构造函数。这会是个问题吗?我编辑了原始帖子以包含除 User 之外的三个实体类的实体定义。

标签: c# sql-server entity-framework orm foreign-keys


【解决方案1】:

User_Id 列是按 EF 约定创建的,因为您将键映射到与导航属性同名的属性。

您可以删除 .Map() 部分并让 EF 处理密钥,或者在您的 Claim 和 Login 类上定义一个密钥属性,并将其映射到流利的 api 中。我个人会做后者。

例如:

public class Claim
{
public string Id { get; protected set; }
public string Type { get; protected set; }
public string Value { get; protected set; }

public virtual User User { get; protected set; }
public int UserId{get;set;}
public Claim() { }
}

在你的 ModelCreating 中

modelBuilder.Entity<User>().HasMany(a=>a.Claims).WithRequired(a=>a.User).HasForeignKey(a=>a.UserId);

如果您创建“EntityNameId”(UserId、ClaimId 等)的键字段,则 EF 约定将自动将其映射为您的外键,而无需流畅的映射。但是,如果您的名称是其他名称(UsernameId 或其他名称),则必须显式提供映射。 Some reading on that.

【讨论】:

  • 嗯,我明白了,所以在 EF 中你不能将列名/外键名定义为与导航属性名相同?我必须将其重命名为 UserId?
  • 正确,如果您自己定义键列(通过.Map),它不能与导航属性同名。我更喜欢最后一种方法,因为这样 Key 就成为模型类上的一个属性,您可以在应用程序中引用。而且我更喜欢明确的定义。
  • 我明白了。将列名重新定义为 UserId 而不是 User 后,错误就消失了。我现在得到一个不同的错误,它说“在类型'用户'上声明的导航属性'声明'已配置有冲突的映射信息。”。我现在正在尝试解决这个问题。
  • 不,这是我的错误,不用担心。
  • 对不起,即使我重命名了列,我仍然会遇到同样的错误。我现在完全迷路了......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
相关资源
最近更新 更多