Mapping and metadata information could not be found for the EntityType exception


I am trying out ASP.NET MVC Framework 2 with the Microsoft Entity Framework and when I try and save new records I get this error:

Mapping and metadata information could not be found for EntityType 'WebUI.Controllers.PersonViewModel'

My Entity Framework container stores records of type Person and my view is strongly typed with class PersonViewModel which derives from Person. Records would save properly until I tried to use the derived view model class. Can anyone explain why the metadata class doesnt work when I derive my view model? I want to be able to use a strongly typed model and also use data annotations (metadata) without resorting to mixing my storage logic (EF classes) and presentation logic (views).

// Rest of the Person class is autogenerated by the EF
public partial class Person
  public sealed class Metadata
    [DisplayName("First Name")]
    [Required(ErrorMessage = "Field [First Name] is required")]
    public object FirstName { get; set; }

    [DisplayName("Middle Name")]
    public object MiddleName { get; set; }

    [DisplayName("Last Name")]
    [Required(ErrorMessage = "Field [Last Name] is required")]
    public object LastName { get; set; }

// From the View (PersonCreate.aspx)
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
         Inherits="System.Web.Mvc.ViewPage<WebUI.Controllers.PersonViewModel>" %>

// From PersonController.cs
public class PersonViewModel : Person
   public List<SelectListItem> TitleList { get; set; }
} // end class PersonViewModel

Update: here is the stack trace:

[InvalidOperationException: Mapping and metadata information could not be found for EntityType 'WebUI.Controllers.PersonViewModel'.]  

System.Data.Objects.ObjectContext.GetTypeUsage(Type entityCLRType) +11531168  

System.Data.Objects.ObjectContext.VerifyRootForAdd(Boolean doAttach, String entitySetName, IEntityWrapper wrappedEntity, EntityEntry existingEntry, EntitySet& entitySet, Boolean& isNoOperation) +195  

System.Data.Objects.ObjectContext.AddObject(String entitySetName, Object entity) +243  

DomainModel.Entities.MyEntities.AddToPeople(Person person) in C:\Users\...\Documents\Visual Studio 2010\Projects\PersonWeb\DomainModel\Entities\MyEntities.Designer.cs:71  

DomainModel.Concrete.Repository.SavePerson(Person person) in C:\Users\...\Documents\Visual Studio 2010\Projects\PersonWeb\DomainModel\Concrete\Repository.cs:42  

WebUI.Controllers.PersonController.Create(FormCollection form, Int32 hidCancel) in C:\Users\...\Documents\Visual Studio 2010\Projects\PersonWeb\WebUI\Controllers\PersonController.cs:163
   lambda_method(Closure , ControllerBase , Object[] ) +165  

System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +258  

controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +39  

System.Web.Mvc.c__DisplayClassd.b__a() +125  

System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +640  

controllerContext, IList1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +312  

System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +709  

System.Web.Mvc.Controller.ExecuteCore() +162  

System.Web.Mvc.c__DisplayClass8.b__4() +58  

System.Web.Mvc.Async.c__DisplayClass1.b__0() +20  

System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +453  

System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +371  

I've just had a similar problem (a search for the exception lead me here), with MVC3, which for me turned out to be because I had moved my edmx file and it had got confused about where the namespace attribute of the EdmEntityTypeAttribute should be pointing.

I create a structure similar to the one you've described, whereby I had a model class that derived from an entity type and again, I got the same error. If I copy the EdmEntityTypeAttribute from the entity type to the derived class then the problem goes away (at least for writing, you get a different problem on reading). This leads me to believe that the framework is probably using reflection to interrogate the class passed into the add method, to determine what attributes are present, but only on the actual type (inheritence tree is ignored).

I thought about this and it actually makes a bit of sense if you think about data flowing back from the database. If you were to fetch back a list of 'Person' objects, the framework would need to decide what class to create to and populate from the table and it doesn't know about your derived Model class so it will need to make the base class. At best, this would mean that you're interacting with the storage using raw entity types for reading and model types for writing, which seems like it would get confusing.

The use of partial classes in the generated entity types allows you to extend them if you need to, or if you want to explicitly separate the entity and model types, then some kind of object mapping may be required.

Of course, I'm still getting to know the entity framework, so there may well be another way around the problem. I assume by this point, you've already found a solution that's working for you.