Home > Blockchain >  Primary Key not defined, even though it already is
Primary Key not defined, even though it already is

Time:12-20

I been developing a C# ASP.NET Core Application and I am currently doing databases using SQL Server Object Explorer. I want to display certain hardcoded data as a testing, however when I try to run the page, I got this error

System.InvalidOperationException: 'The entity type 'recent_health_issue' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'

My primary key is already defined and the display page works normally without that line of error code, so I am not sure here.

My database:

enter image description here

Model class:

public class recent_health_issue
{
    [Required, MinLength(3, ErrorMessage = "Enter at least 3 characters"),
     MaxLength(5)]
    public string recent_id { get; set; }

    [Required, MaxLength(25)]
    public string recent_name { get; set; }

    [Required, EmailAddress]
    public string recent_email { get; set; }

    [Required]
    public string recent_description { get; set; }
}

My DbContext:

public class RecentHealthIssueDBContext : DbContext
{
    private readonly IConfiguration _config;

    public RecentHealthIssueDBContext(IConfiguration configuration)
    {
        _config = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string connectionString = _config.GetConnectionString("MyConn");
        optionsBuilder.UseSqlServer(connectionString);
    }

    public DbSet<recent_health_issue> HealthIssues { get; set; }
}

My controller (where the error happens):

public class RecentHealthController
{
    private RecentHealthIssueDBContext _context;

    public RecentHealthController(RecentHealthIssueDBContext context)
    {
        _context = context;
    }

    public List<recent_health_issue> getAllRecentHealthIssues()
    {
        List<recent_health_issue> AllRecentHealth = new List<recent_health_issue>();

        // error occured here
        AllRecentHealth = _context.HealthIssues.ToList(); 

        return AllRecentHealth;
    }

    public recent_health_issue getRecentHealthIssueById(String id)
    {
        recent_health_issue RecentHealthIssue = new recent_health_issue();
        return RecentHealthIssue;
    }
}

CodePudding user response:

The error message says you need to define a primary key for the table:

[Key]
[Required, MinLength(3, ErrorMessage = "Enter at least 3 characters"),
 MaxLength(5)]
public string recent_id { get; set; }

Also try to rename the DbSet property in your DbContext to match the DB table name, which is RecentHealthIssues instead of HealthIssues:

public DbSet<recent_health_issue> RecentHealthIssues { get; set; }

CodePudding user response:

To clarify, from your question, you're doing database first development.

This means that your model and your database need to match exactly, so if there is any discrepancy then in most cases won't work.

Entity Framework expects Pascal Case for properties, and if it finds a single occurrence of property either named or containing "Id" then it will assume that is the key.

If you don't follow pascal case (which is the C# standard), or don't use a property containing the letters "Id", then you must use the [Key] data annotation.

As you're doing database first, you've chosen snake case for your SQL table, which means that you need to align the names with the C# without breaking coding conventions and allowing EF to do it's thing.

Also, looking at your code and what you're doing, as you're using snake_case to define the names in the database, then you should be using data annotations for tables/column names, not changing your property names to align, as defined here: https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/data-annotations#table-and-column

For your information, EF expects Pascal casing in C#, so it can do it's automation (which is why you should be using it)

public long Id {get;set;} //ef will recognise this as a Key

public long HelloCheekyThisIsMyId {get;set;} //ef will recognise this as a Key

[Key] //ef will be told this is a key
public long product_identity { get; set; }

So your model should look like this:

[Table("recent_health_issue")]
public class RecentHealthIssue
    {
        [Key] //this shouldn't be needed but it's good for readability
        [Required, MinLength(3, ErrorMessage = "Enter at least 3 characters"), MaxLength(5)]
        [Column("recent_id")]
        public string RecentId { get; set; }

        [Required, MaxLength(25)]
        [Column("recent_name")]
        public string RecentName { get; set; }

        [Required, EmailAddress]
        [Column("recent_email")]
        public string RecentEmail { get; set; }

        [Required]
        [Column("recent_description")]
        public string RecentDescription { get; set; }
}

as defined in the documentation: "Don’t confuse Column’s TypeName attribute with the DataType DataAnnotation. DataType is an annotation used for the UI and is ignored by Code First."

Your context like this:

public class RecentHealthIssueDBContext : DbContext
{
    private readonly IConfiguration _config;
    public RecentHealthIssueDBContext(IConfiguration configuration)
    {
        _config = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string connectionString = _config.GetConnectionString("MyConn");
        optionsBuilder.UseSqlServer(connectionString);
    }

    public DbSet<RecentHealthIssue> HealthIssues { get; set; }
}

and your controller should look like this:

public class RecentHealthController
{
    private RecentHealthIssueDBContext _context;

    public RecentHealthController(RecentHealthIssueDBContext context)
    {
        _context = context;
    }

    //This should be PascalCase (GetAllRecentHealthIssues)
    public List<recent_health_issue> getAllRecentHealthIssues()
    {
        List<RecentHealthIssue> allRecentHealth = new List<RecentHealthIssue>();
        allRecentHealth = _context.HealthIssues.ToList(); //error occured here
        return allRecentHealth;
    }

    //This should be PascalCase (GetRecentHealthIssuesById)
    public recent_health_issue getRecentHealthIssueById(String id)
    {
        //return recent health issues using an Id
    }
}

Also I would reccomend some defensive programming, it will save you headaches in the future. (Null checks, try/catch blocks etc.)

This is the C# coding convention: https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions

You can find out more about the available annotations here: https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/data-annotations#key

  • Related