Entity Framework - error: the principal end of this association must be explicitly configured

asp.net-mvc asp.net-mvc-5.1 entity entity-framework entity-framework-6

Question

I have a project built on the top of the ASP.NET MVC 5 framework. I am using Entity Framework 6.2 to access data from my database.

I have the following two models

public class Entity<T> where T : struct
{
    [Key]
    public T Id { get; set; }
}

public class User : Entity<int>
{
    public string FirstName { get; set; }

    public int CategoryId { get; set; }
    // more properties removed for the sake of simplicity

    public virtual Category Category { get; set; }
}

public class Category : Entity<int>
{
    public string Name { get; set; }
}

Here is how I am accessing the user from the database-context.

User user = await DbContext.Users.FirstAsync(10);

However, I am getting the following unexpected exception.

Unable to determine the principal end of an association between the types 'Category' and 'User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

I tried adding [ForeignKey] attribute to the Category property but that did not work either:

[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }

I also tried adding InverseProperty attribute

[ForeignKey("CategoryId")]
[InverseProperty("Id")]
public virtual Category Category { get; set; }

but the inverse-property annotation throws the following error:

The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.

What could be causing this issue? How can I fix it?

1
1
3/26/2020 10:36:47 PM

Popular Answer

I couldn't repro (EF 6.4):

using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace Ef6Test
{
    public class Entity<T> where T : struct
    {
        [Key]
        public T Id { get; set; }
    }

    public class User : Entity<int>
    {
        public string FirstName { get; set; }

        public int CategoryId { get; set; }
        // more properties removed for the sake of simplicity

        public virtual Category Category { get; set; }
    }

    public class Category : Entity<int>
    {
        public string Name { get; set; }
    }

    public class Db : DbContext
    {
        public Db(string constr) : base(constr)
        { }

        public DbSet<User> Users { get; set; }
        public DbSet<Category> Categories { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }

    }
    class Program
    {
        static void Main(string[] args)
        {
            var constr = "server=localhost;database=ef6test;integrated security=true";
            using var db = new Db(constr);
            db.Database.Delete();
            db.Database.Log = msg => Console.WriteLine(msg); 
            db.Database.Initialize(true);

            var u = new User();
            u.Category = new Category();

            db.Users.Add(u);
            db.SaveChanges();

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}

outputs

Opened connection at 3/26/2020 5:57:48 PM -05:00

Started transaction at 3/26/2020 5:57:48 PM -05:00

CREATE TABLE [dbo].[Categories] (
    [Id] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([Id])
)


-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 15 ms with result: -1



CREATE TABLE [dbo].[Users] (
    [Id] [int] NOT NULL IDENTITY,
    [FirstName] [nvarchar](max),
    [CategoryId] [int] NOT NULL,
    CONSTRAINT [PK_dbo.Users] PRIMARY KEY ([Id])
)


-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 8 ms with result: -1



CREATE INDEX [IX_CategoryId] ON [dbo].[Users]([CategoryId])


-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 7 ms with result: -1



ALTER TABLE [dbo].[Users] ADD CONSTRAINT [FK_dbo.Users_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([Id]) ON DELETE CASCADE


-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 9 ms with result: -1



CREATE TABLE [dbo].[__MigrationHistory] (
    [MigrationId] [nvarchar](150) NOT NULL,
    [ContextKey] [nvarchar](300) NOT NULL,
    [Model] [varbinary](max) NOT NULL,
    [ProductVersion] [nvarchar](32) NOT NULL,
    CONSTRAINT [PK_dbo.__MigrationHistory] PRIMARY KEY ([MigrationId], [ContextKey])
)


-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 8 ms with result: -1



INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'202003262257478_InitialCreate', N'Ef6Test.Db',  0x1F...
-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 9 ms with result: 1



Committed transaction at 3/26/2020 5:57:48 PM -05:00

Opened connection at 3/26/2020 5:57:48 PM -05:00

Started transaction at 3/26/2020 5:57:48 PM -05:00

INSERT [dbo].[Categories]([Name])
VALUES (NULL)
SELECT [Id]
FROM [dbo].[Categories]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()


-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 10 ms with result: SqlDataReader



INSERT [dbo].[Users]([FirstName], [CategoryId])
VALUES (NULL, @0)
SELECT [Id]
FROM [dbo].[Users]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()


-- @0: '1' (Type = Int32)

-- Executing at 3/26/2020 5:57:48 PM -05:00

-- Completed in 20 ms with result: SqlDataReader



Committed transaction at 3/26/2020 5:57:48 PM -05:00

Closed connection at 3/26/2020 5:57:48 PM -05:00

Hit any key to exit
0
3/26/2020 10:58:38 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow