Search code examples
c#asp.net-mvcentity-frameworkasp.net-mvc-5entity-framework-6

EntityType has no key defined error despite defining key


I'm following the tutorial listed here: http://dotnetawesome.blogspot.com/2014/07/nested-webgrid-with-expand-collapse-in-aspnet-mvc4.html using my VS2015 Enterprise IDE.

However when at step 8 I go to right click the List() ActionResult in OrderController and select Add View with the following: enter image description here

I get the following error:

enter image description here

However in the tutorial they don't use a key for OrderVM. Even when I do add [Key] and the proper using and after rebuilding I still get the same error and I'm at a complete loss as to why given I'm using getters and setters.

ViewModels: OrderVM

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using WebAppTest.Models;

namespace WebAppTest.ViewModels
{
    public class OrderVM
    {
        [Key]
        public OrderMaster order { get; set; }
        public List<OrderDetail> orderDetails { get; set; }
    }
}

Models (Generated from doing a CodeFirst from DB): MyModel

namespace WebAppTest.Models
{
    using System;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;

    public partial class MyModel : DbContext
    {
        public MyModel()
            : base("name=MyDatabaseEntities")
        {
        }

        public virtual DbSet<OrderDetail> OrderDetails { get; set; }
        public virtual DbSet<OrderMaster> OrderMasters { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<OrderDetail>()
                .Property(e => e.Product)
                .IsUnicode(false);

            modelBuilder.Entity<OrderDetail>()
                .Property(e => e.Rate)
                .HasPrecision(10, 2);

            modelBuilder.Entity<OrderDetail>()
                .Property(e => e.Amount)
                .HasPrecision(10, 2);

            modelBuilder.Entity<OrderMaster>()
                .Property(e => e.OrderAmount)
                .HasPrecision(10, 2);

            modelBuilder.Entity<OrderMaster>()
                .Property(e => e.CustomerName)
                .IsUnicode(false);

            modelBuilder.Entity<OrderMaster>()
                .Property(e => e.CustomerAddress)
                .IsUnicode(false);
        }
    }
}

OrderDetail

namespace WebAppTest.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class OrderDetail
    {
        [Key]
        public int OrderDetailsID { get; set; }

        public int OrderID { get; set; }

        [Required]
        [StringLength(100)]
        public string Product { get; set; }

        public int Quantity { get; set; }

        [Column(TypeName = "numeric")]
        public decimal Rate { get; set; }

        [Column(TypeName = "numeric")]
        public decimal Amount { get; set; }
    }
}

OrderMaster

namespace WebAppTest.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    [Table("OrderMaster")]
    public partial class OrderMaster
    {
        [Key]
        public int OrderID { get; set; }

        public DateTime OrderDate { get; set; }

        [Column(TypeName = "numeric")]
        public decimal OrderAmount { get; set; }

        [Required]
        [StringLength(100)]
        public string CustomerName { get; set; }

        [StringLength(200)]
        public string CustomerAddress { get; set; }
    }
}

Controllers

OrderController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebAppTest.Models;
using WebAppTest.ViewModels;

namespace WebAppTest.Controllers
{
    public class OrderController : Controller
    {
        // GET: Order
        //public ActionResult Index()
        //{
        //    return View();
        //}
        public ActionResult List()
        {
            List<OrderVM> allOrder = new List<OrderVM>();

            // here MyDatabaseEntities is our data context
            using (MyModel dc = new MyModel())
            {
                var o = dc.OrderMasters.OrderByDescending(a => a.OrderID);
                foreach (var i in o)
                {
                    var od = dc.OrderDetails.Where(a => a.OrderID.Equals(i.OrderID)).ToList();
                    allOrder.Add(new OrderVM { order = i, orderDetails = od });
                }
            }
            return View(allOrder);
        }
    }
}

Solution

  • Here is the logic of Scaffolder to check for key field:

    First finds all properties with this criteria:

    • Property should be primitive type or if not primitive, one of these: string, decimal, Guid, DateTime, DateTimeOffset, TimeSpan.

    Then Tries to find key based on name:

    • [Id] Property (case insensitive)
    • [ClassName][Id] Property (case insensitive)

    Tries to find key based on attributes:

    • Property having Key Attribute
    • Property having EdmScalarProperty Attribute with value of EntityKeyPropety=true
    • Property having Column Attribute with value of IsPrimaryKey=true

    So you should have a filed in your class that meets one of above criterias.
    Also you should remove [Key] from your order property because complex types can not be key.

    As an option you can add this property to your class:

    [Key]
    public int OrderID { get; set; }
    

    And as another option your OrderVM class can be like this:

    public partial class OrderVM
    {
        public OrderVM()
        {
            orderDetails = new List<OrderDetails>();
        }
    
        [Key]
        public int OrderID { get; set; }
    
        public DateTime OrderDate { get; set; }
    
        public decimal OrderAmount { get; set; }
    
        [Required]
        [StringLength(100)]
        public string CustomerName { get; set; }
    
        [StringLength(200)]
        public string CustomerAddress { get; set; }
    
        public List<OrderDetail> orderDetails { get; set; }
    }