0

I have the following class that I would like to use as property of a Login object. I'd prefer that this class not have any navigation properties (as it will just be used for quick checks) if that is at all possible, but I'm willing to allow the one shown below.

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; }
}

The class containing a collection of these is (stripped down for space)

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

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

My DbContext is defined as

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

But the following test generates an error saying that

System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. ----> System.Data.UpdateException : An error occurred while updating the entries. See the inner exception for details. ----> System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'RoleId', table 'TEST.dbo.LoginFeatures'; column does not allow nulls. INSERT fails. The statement has been terminated.

    [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();
        }
    }

The SQL being generated as show in EFProf is

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

Which seems to imply that the data annotation attributes on LoginFeature are incorrect. Is what I am trying to do possible?

Thanks,

Joe

4

1 回答 1

1

You cannot define a navigation property as a key. Only primitive properties are supported as keys. So, you should define the class like so:

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; }
}

Mapping conventions will detect LoginId as foreign key for Login. The error probably occurs because EF did ignore your second key attribute (as it was on a navigation property), uses only the first key RoleId and by default (for single keys) assumes that the key is autogenerated in the database (which it apparently isn't) and doesn't send its value to the database.

于 2013-09-23T20:28:55.527 回答