Some gotchas when using ASP.NET MVC with Entity Framework Code First.

A fairly short one today. I was playing around with ASP.NET MVC and Entity Framework Code First and discovered a few gotchas.

Which I intend to share here.

So this post is not about how EF code first or MVC works. For a really good tutorial on this (ASP.NET MVC and using EF Code first) I recommend:

 

"Intro to ASP.NET MVC 3 (C#)"

https://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/getting-started-with-mvc3-part1-cs

by Rick Anderson

 

This also shows the prerequisites for a setup like this.

 

But that is to verbose for the few gotchas that I will try to demonstrate here.

So, the premise is to create an MVC application that will store Persons in a database.

Let’s do it from scratch. Create a new ASP.NET MVC 3 Web Application called MVCData in C:\MVCData. Use the Empty template. This sets up the scaffolding for the MVC application.

 

What we want is to use a database to store Persons in it. So we start by creating a class called Person.
Each instance of the Person class will be a row in a table and each property will be a column in the table.

Right click the Models directory in the Solution Explorer and add a new class, Person.cs. The class should look like this:

 

using System.Data.Entity;

namespace MVCData.Models

{

    public class Person

    {

        public string Name { get; set; }

        public string City { get; set; }

        public int Age { get; set; }

    }

    public class PersonDbContext : DbContext

    {

        public DbSet<Person> Persons { get; set; }

    }

}

 

Notice that you need to bring in the System.Data.Entity namespace.

Next create the connection string for the database, here we use the SQL CE since this is used in most tutorials out there.

In the web.config of the application (not the one in the Views directory) add a connection string section to the configuration. Like so:

 

<configuration>

  <connectionStrings>

    <add name="PersonDbContext"

         connectionString="Data Source=|DataDirectory|Person.sdf"

         providerName="System.Data.SqlServerCe.4.0"/>

  </connectionStrings>

 

Next create a HomeController in order to create and use this database.

Right click the Controllers directory in the Solution Explorer and add a new controller called HomeController. Use the Empty Controllers template.

The class should look like this:

 

using MVCData.Models;

namespace MVCData.Controllers

{

    public class HomeController : Controller

    {

        private PersonDbContext db = new PersonDbContext();

        //

        // GET: /Home/

        public ActionResult Index()

        {

            var persons = from p in db.Persons select p;

            return View(persons.ToList());

        }

    }

}

 

Notice that you need to bring in the MVCData.Models namespace.

 

Now run by hitting F5.

 

# GOTCHA No. 1.

 

You should get an error along these lines:

 

One or more validation errors were detected during model generation:

                System.Data.Edm.EdmEntityType: : EntityType 'Person' has no key defined. Define the key for this EntityType.

                System.Data.Edm.EdmEntitySet: EntityType: EntitySet 'Persons' is based on type 'Person' that has no keys defined.

 

The reason is that by default, the Entity Framework interprets a property that's named ID or classnameID as the primary key.

 

So, to resolve this we could.

. Add a property called ID

or

. Add a property called PersonID

or

. Use the Key attribute on a property.

 

So, for the demo I’ve chosen to add a property called ID.

 

    public class Person

    {

        // Option 1, use a ID property

        public int ID { get; set; }

        // Option 2, use a classnameID property

        //public int PersonID { get; set; }

       

        // Option 3, use the Key attribute.

        //[System.ComponentModel.DataAnnotations.Key]

        public string Name { get; set; }

        public int Age { get; set; }

        public string City { get; set; }

  }

 

Ok, with this out of the way, run it again by hitting F5.

 

# GOTCHA No. 2.

 

You should get an error along these lines:

 

SqlCeException was unhandled by user code:

  The path is not valid. Check the directory for the database. [ Path = C:\MVCData\MVCData\MVCData\App_Data\Person.sdf ]

This one is a little bit clearer. Simply right click the project and select Add > Add ASP.NET Folder > App_Data.

And rerun the application to get to:

 

# GOTCHA No 3.

 

You should get an error along these lines:

 

Server Error in '/' Application.

--------------------------------------------------------------------------------

 

The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:

 

~/Views/Home/Index.aspx

~/Views/Home/Index.ascx

~/Views/Shared/Index.aspx

~/Views/Shared/Index.ascx

~/Views/Home/Index.cshtml

~/Views/Home/Index.vbhtml

~/Views/Shared/Index.cshtml

~/Views/Shared/Index.vbhtml

 

Description: An unhandled exception occurred during the execution of the current web request.

Please review the stack trace for more information about the error and where it originated in the code.

 

Exception Details: System.InvalidOperationException: The view 'Index' or its master was not found or no view engine supports the searched locations.

The following locations were searched:

...

 

This happens because we have entered our controller but we have forgotten to create the corresponding View. So when the Index method in the HomeController

is trying to pass the list of Persons to the View. No view is found in the routes and it fails.

 

This is also simple to resolve. Simply right click in the Index method and select Add View. Uncheck the “Use a layout or master page:” since this is not needed for this demo.

This creates a Home folder under the Views folder and inserts the Index.cshtml in it.

 

Now rerun the application once again by hitting F5 and you should see the Index page simply displaying “Index”.

 

Stop the debugging and try to locate the Database which should be called Persons.sdf.

 

# GOTCHA No 4.

 

You should not see the created database unless you select “Show All Files” in the Solution Explorer.

Once you select that option you should see App_Data -> Person.sdf.

 

So, now we have a setup where the database is created. There is however no data there since we have not inserted anything.

This is not really a ‘gotcha’ but I will show an example of how to insert some data when the database is created and to display it in the index view for the sake of clarity.

 

Right click the Models directory in the Solution Explorer and add a new class, PersonInitializer.cs. The class should look like this:

 

namespace MVCData.Models

{

    public class PersonInitializer : DropCreateDatabaseIfModelChanges<PersonDbContext>

    {

        protected override void Seed(PersonDbContext context)

        {

            var persons = new List<Person>

            {

                new Person{Name="John", Age=30, City="Stockholm"},

                new Person{Name="Eric", Age=40, City="Stockholm"},

                new Person{Name="Paul", Age=50, City="London"},

                new Person{Name="Mike", Age=60, City="Paris"}

            };

            persons.ForEach(p => context.Persons.Add(p));

     }

    }

}

 

Open the Global.asax in the solution and add this to the Application_Start method:

 

protected void Application_Start()

{

Database.SetInitializer<PersonDbContext>(new PersonInitializer());

 

And run it. This will create the database and insert the rows specified in the Seed method.

 

NOTE: If you change the Model, the above will DROP the database and effectively delete all your existing data. DO NOT use this on a production database, just for development!!!

 

Change the Index.cshtml to look as follows:

 

@model IEnumerable<MVCData.Models.Person>

@{

    ViewBag.Title = "Index";

}

<h2>Index</h2>

    <table>

        <tr>

            <th>

                Name

            </th>

            <th>

                Age

            </th>

  <th>

                City

            </th>

            <th></th>

        </tr>

   

    @foreach (var item in Model) {

        <tr>

            <td>

                @Html.DisplayFor(modelItem => item.Name)

            </td>

            <td>

    @Html.DisplayFor(modelItem => item.Age)

            </td>

            <td>

                @Html.DisplayFor(modelItem => item.City)

            </td>

        </tr>

    }

    </table>

 

Run it and you should now see the list of persons that you created in the Seed method.

 

Hope the above helps.

 

DbContext Class

https://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext(v=VS.103).aspx

DbSet(Of TEntity) Class

https://msdn.microsoft.com/en-us/library/gg696460(v=vs.103).aspx

Productivity Improvements for the Entity Framework

https://blogs.msdn.com/b/efdesign/archive/2010/06/21/productivity-improvements-for-the-entity-framework.aspx

System.Data.Entity Namespace

https://msdn.microsoft.com/en-us/library/gg696142(v=vs.103).aspx