ASP.NET vNext (MVC6) Ground Up #3 - Entity Framework 7

Continuing on from Part #2 of the Ground Up Series. Today we'll be looking at solutions for handling data with Entity Framework (EF) 7

The source code is available here.

Entity Framework V7 is a rewrite

The next version of EF will sport a version of number 7, but it's a complete rewrite. What does that mean for users, especially those of the previous versions?

  • When EF7 RTM's it will not be the suggested data platform for ASP.NET5, that will continue to be EF6.
  • Certain features won't make the cut for the initial RTM release such as lazy loading.
  • Post RTM and after adding in things like lazy loading, EF7 still won't support everything EF6 does. There's a lot of legacy cruft in the codebase (some reports indicate > 1.5 million lines of code dating back to the failed WinFS file system) and they are taking this opportunity to trim the fat in a serious way. There's a lot of obscure, rarely used features in EF and a lot of those will be getting cut.
  • "Code First" or "Code Only" is going to be the only supported mechanism, you can still generate these code files from an existing database, but there will be no massive XML generating UI system in Visual Studio.

In others news:

  • The surface API's you would typically use (DbContext, DbSet<T>) remain largely very familiar.
  • Supports the new .Net Core runtime, which means it will eventually be opened up to run on other platforms and devices.
  • Support for non-relational datastores such as document databases.
  • Much less bloated.
  • Much less magic!

Let's get cracking

As with the previous posts, we'll start by adding in our dependencies with KPM install

kpm install EntityFramework.SqlServer 7.0.0-beta2  

We also want to bring in the EntityFramework.Commands which will give us some additional tools to work with EF.

kpm install EntityFramework.Commands 7.0.0-beta2  

Now we can create a DbContext just like we would with previous versions of EF.

using Microsoft.Data.Entity;

public class GroundUpDbContext : DbContext  
{
    public DbSet<TodoItem> Todos {get; set;}

    protected override void OnConfiguring(DbContextOptions builder)
    {
        builder.UseSqlServer(@"Server=(localdb)\v11.0;Database=TodoItems;Trusted_Connection=True;");
    }
}

public class TodoItem  
{
    public int Id {get; set;}
    public string Description {get; set;}
}

There is one major change here. We need to override the OnConfiguring method and call the builder.UseSqlServer("connectionstring") method.

This UseSqlServer naming is significant. As I outlined in Part 1, the Use.. wording denotes adding the applicable services to the middleware pipeline. This wireup can also be done via convention or as part of our Startup.cs.

What was that about magic?

Prior versions of EF were designed to demo really well. You could write a quick class, scaffold up a MVC controller + view, fire up the project and then a whole bunch of magic would happen, your database would be created and you're off to the races. You could make changes to your database just by simply editing the class and EF would take care of dropping and recreating the neccesary parts.

This was great for getting up and running quickly but as you continued to build on your application you'd eventually want to start migrating the schema properly with EF Migrations. The issue you now have is that your initial database creation was never part of the migrations. So as soon as you hand your code off to someone else, that crucial step of actually creating the initial database schema was missing.

There are no more surprises with EF7.

(Re)Enter Commands

As we outlined in Part 1, commands are now the way we interact with our project. Add the following command to your project.json

"ef": "EntityFramework.Commands"

Our project.json now looks like:

{
  "dependencies": {
    "Microsoft.AspNet.Hosting": "1.0.0-beta2",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta2",
    "Microsoft.AspNet.Mvc": "6.0.0-beta2",
    "EntityFramework.SqlServer": "7.0.0-beta2",
    "EntityFramework.Commands": "7.0.0-beta2"
  },
  "commands": {
    "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000",
    "ef": "EntityFramework.Commands"
  }
}

Now run the following from the command line

k ef  

And the Magic Unicorn makes an appearance!

EF Magic Unicorn

Exposed here are commands to manage the contexts and database migrations.

k ef migration add "initial"  

This will scaffold out the appropriate files in /migrations/ directory of your project.

k ef migration apply  

Will now execute the commands against the database and bring everything up to date.

Plumbing back into the DI system

We want to Add... EntityFramework, SqlServer and our DbContext into the dependency injection system. So as part of our ConfigureServices method in Startup.cs, add the following:

services.AddEntityFramework()  
    .AddSqlServer()
    .AddDbContext<CompareLearningDbContext>();

Our Startup.cs now looks like:

using Microsoft.AspNet.Builder;  
using Microsoft.AspNet.Routing;  
using Microsoft.Framework.DependencyInjection;  
using Microsoft.Data.Entity;

public class Startup  
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<GroundUpDbContext>();
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

EF and our DbContext are now good to go. We'll add a constructor to our HomeController to accept an instance of our context:

using Microsoft.AspNet.Mvc;  
using System.Linq;

public class HomeController : Controller  
{
    private readonly GroundUpDbContext _db;
    public HomeController(GroundUpDbContext db)
    {
        _db = db;
    }

    public IActionResult Index()
    {
        var listOfTodos = _db.Todos.ToList();

        return View(listOfTodos);
    }
}

(aside: if you don't like the idea of depending on a concrete type as opposed to an interface, you can follow along with the github issue.)

And modify our views/home/index.cshtml:

@model IEnumerable<TodoItem>

Hello World from View  
<br />  
Count: @Model.Count()  

Executing k web again to boot the server up and accessing http://localhost:5000 will now display the count of 0.

Not terribly exciting but we're now interacting with the database. In the next post we'll evaluate some of the new scaffolding techniques to flesh this out with full CRUD support.

Thanks to @ahandersson for help with reviewing this post.

comments powered by Disqus