实体框架中的一对多关系与复合键

时间:2013-09-23 19:43:31

标签: entity-framework

我有以下类要用作Login对象的属性。我希望这个类没有任何导航属性(因为它只是用于快速检查),如果可能的话,但我愿意允许下面显示的那个。

public class LoginFeature
{
    [Key, Column(Order = 0)]
    public int RoleId { get; set; } //Role is another table in the db, but not looking for a nav. property or constraint here.

    [Key, Column(Order = 1)]
    public virtual Login Login { get; set; }

    public bool Deny { get; set; }
}

包含这些集合的类是(为了空间而被剥离)

public class Login
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; } //Database ID

    public virtual List<LoginFeature> LoginFeatures { get; set; }
}

我的DbContext定义为

public class MyContext : DbContext
{
    public DbSet<Login> Logins { get; set; }
    public DbSet<LoginFeature> LoginFeatures { get; set; }
}

但是以下测试会产生错误,说明

  

System.Data.Entity.Infrastructure.DbUpdateException:保存未公开其关系的外键属性的实体时发生错误。 EntityEntries属性将返回null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅InnerException。     ----&GT; System.Data.UpdateException:更新条目时发生错误。有关详细信息,请参阅内部异常     ----&GT; System.Data.SqlClient.SqlException:无法将值NULL插入“RoleId”列,表'TEST.dbo.LoginFeatures';列不允许空值。 INSERT失败。   声明已经终止。

    [Test]
    public void LoginCanHaveFeatures()
    {
        using (var ctx = new MyContext())
        {
            var login = ctx.Logins.FirstOrDefault(x => x.Id == 30);
            Assert.IsNotNull(login);

            for (int i = 10; i < 15; i++)
            {
                var feature = new LoginFeature();
                feature.Login = login;
                feature.RoleId = i;
                feature.Deny = true;
                login.LoginFeatures.Add(feature);
            }

            ctx.SaveChanges();
        }
    }

在EFProf中显示的生成的SQL是

insert [dbo].[LoginFeatures]
   ([Deny],
    [Login_Id])
values (1 /* @0 */,
    30 /* @1 */)

这似乎意味着LoginFeature上的数据注释属性不正确。我正在尝试做什么?

谢谢,

1 个答案:

答案 0 :(得分:1)

您无法将导航属性定义为键。仅支持原始属性作为键。所以,你应该像这样定义类:

public class LoginFeature
{
    [Key, Column(Order = 0)]
    public int RoleId { get; set; }

    [Key, Column(Order = 1)]
    public int LoginId { get; set; }

    public virtual Login Login { get; set; }

    public bool Deny { get; set; }
}

映射约定会将LoginId检测为Login的外键。可能会发生错误,因为EF确实忽略了您的第二个键属性(因为它在导航属性上),仅使用第一个键RoleId,默认情况下(对于单个键)假定键在数据库中自动生成(它显然不是,并且没有将其价值发送到数据库。