A Look at ASP.NET 5: Part 3 - EF7


database planNOTE: This post has been updated for changes in Beta 7 and later.

Every web project needs some sort of data framework and ASP.NET 5 is no exception. Like it’s forbearers, ASP.NET 5 uses Entity Framework, but this version of the Entity Framework is different. It’s being re-engineered from the ground up just like the ASP.NET 5 stack.

In this, the third part of my ASP.NET 5 series, I’ll be talking about Entity Framework 7. While you can use older versions of EF or other storage mechanisms (NoSQL, Postgres, etc.), I think it’s an interesting exercise to see what the EF team is thinking in this evolving version of the framework.

I’ll explain what I’m doing in a series of blog posts and link them all here as I write them. The plan is to write posts about:

Part 1: Getting Started

Part 2: Startup

Part 3: Using Entity Framework 7 (this post)

Part 4: ASP.NET MVC 6

Part 5: The API

Part 6: Web Tooling with VS2015 (coming soon)

The project will continue to evolve and you can always get the latest version by visiting it on GitHub:

https://github.com/shawnwildermuth/MyCountries

First thing to understand is that EF7 isn’t done. It’s a work in progress. So much of the warts and pain points I discuss here will hopefully be gone by the time you’re actually using the framework in ASP.NET 5 projects.

Configuring Entity Framework

To get started with Entity Framework 7, you need the reference. By default this is already there but for when you need to update to new versions, I thought I’d point it out where it is in the project.json file:

{
  /* Click to learn more about project.json  http://go.microsoft.com/fwlink/?LinkID=517074 */
  "webroot": "wwwroot",
  "version": "1.0.0-*",
  "dependencies": {
    "EntityFramework.SqlServer": "7.0.0-beta7",
    "EntityFramework.Commands": "7.0.0-beta7",
    "Microsoft.AspNet.Mvc": "6.0.0-beta7",

If you remember the last post, the startup.cs allowed for configuration to happen in two phases. First the configuration of the different elements, then the registering of services with the dependency injection layer. So let’s start there.

The service container is the dependency injection mechanism built into ASP.NET 5 and the ConfigureServices method is where that happens. Before we can add EF to the DI container, we need to get a connection string.

When I first came to ASP.NET 5, it looked as if the EF configuration would just load the connection string by convention, but it didn’t work. So I had to get it manually and add it (below) when I configured the context object. So, instead, I just grabbed it from the Configuration object that was created in the constructor:

    // This method gets called by the runtime.
    public void ConfigureServices(IServiceCollection services)
    {
      var connectionString = Configuration.Get("Data:DefaultConnection:ConnectionString");

The Configuration.Get method allows it to read a setting from the list of configuration

sources. If you remember from the last post, the Configuration object is a merge of the config.json file and any environment variables. In the case of the connection string, we’re looking for a string called “ConnectionString” inside an object graph. This should look obvious once you see the config.json file:

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=(localdb)\\MSSQLLocalDB;..."
    }
  },
  "EntityFramework": {
    "ApplicationDbContext": {
      "ConnectionStringKey": "Data:DefaultConnection:ConnectionString"
    }
  }
}

So now that we have the connection string, what is next?

Next, Entity Framework is added to the services collection by adding it like so:

// Add EF services to the services container.
services.AddEntityFramework(Configuration)
    .AddSqlServer()
    .AddDbContext<MyCountriesContext>(options =>
    {
      options.UseSqlServer(connectionString);
    });

The AddEntityFramework method adds EF to the dependency injection container so it can be served if needed later. Additionally, calling AddSqlServer specifies the data store you’ll be using. Finally, the AddDbContext adds a DbContext object to the EF service (we’ll get to what that looks like below). The options lambda allows us to specify the connection string. I suspect this extra step will go away at some point and just read from the configuration by convention, but at this point it’s necessary. At this point, if we need the context in another part of the system (e.g. Controllers) we can just let ASP.NET 5 serve it to us in the constructor like any other dependency injection framework.

Creating the DbContext

Assuming you’re somewhat familiar with Entity Framework, you should already know that the DbContext class has the responsibility of exposing the data in a data store via the Code-First semantics. Creating a DbContext derived class hasn’t changed substantially.

public class MyCountriesContext : IdentityDbContext<ApplicationUser>
{
  public MyCountriesContext()
  {
    Database.EnsureCreated();
  }

  public DbSet<Visit> Visits { get; set; }

  protected override void OnModelCreating(ModelBuilder builder)
  {
    builder.Entity<Visit>().Key(v => v.Id);

    base.OnModelCreating(builder);
  }
}

The Database.EnsureCreated is important because it causes the database to be created and migrations to be run to make sure the database is up to date as well.

But this isn’t quite what I did. Originally, the project template created a context for the ASP.NET Identity system that derived from IdentityContext. but I didn’t want to have two disconnected DbContext classes. Instead of creating a new context like this, I just combined the two so that one context would be responsible for Identity and my own data (e.g. the Visits DbSet). I did this by deriving my context from IdentityContext as shown here:

  public class MyCountriesContext : IdentityDbContext<ApplicationUser>
  {
    public MyCountriesContext()
    {
    }

    public DbSet<Visit> Visits { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
      builder.Entity<Visit>().Key(v => v.Id);

      base.OnModelCreating(builder);
    }

  }

Because I’m using this as my ASP.NET Identity context too, the configuration (in the ConfigureServices method of startup.cs) needs this context added too:

// Add Identity services to the services container.
services.AddIdentity<ApplicationUser, IdentityRole>(Configuration)
    .AddEntityFrameworkStores<MyCountriesContext>();

Because I created a repository class that does the real work of the site, I’ll need to configure that in the ConfigureService method too. In this case, it’s a simple adding of the interface and implementation class like any other dependency injection framework you’ve used before (i.e. Ninject, Unity, etc.):

// Add other services
services.AddScoped<IMyCountriesRepository, MyCountriesRepository>();

So that when I have a class that requires the IMyCountriesRepository, it passed in an instance of the MyCountriesRepository class as you’d expect. And since it is handling the dependency chains, it will fulfill any requirements that the repository class have (e.g. will pass in a created MyCountriesContext to the repository as that is a dependency of that class).

This implies something that is important:

If you’re not using dependency injection yet, get used to it…you’ll be doing it in ASP.NET 5!

Database Initialization in EF7 Today

In the creation of the DbContext, we saw that the code is calling Database.EnsureCreated() to cause the database to be created. What isn’t obvious here is that it also calls Migrations. Migrations are used to build the schema of your entities. But we need the migration to exist. Because I started this project back in an early beta, I’ve had to delete the entire set of migrations (the code in the Migrations folder) and rebuild the migrations a few times. Once we reach release, this won’t be as much of an issue.

To make the migrations, simply open up a console and type:

dnx ef migrations add {Name of Migration}

- or - 

dnx ef migrations add InitialDatabase

You’ll see these migrations show up in the project once you do that.

For seeding we have another problem. If you’ve done Entity Framework code before, you might be using the DbInitializer pattern to create the database and seed it. I am not sure if this part of EF7 isn’t done or whether they decided to just give you more control over the process, but in either case I needed a way to initialize the database creation and seeded data. Let’s see how I accomplished it.

The DbContext class I created looks different from the original one that the template project created. That’s because originally there was a hack in the code to allow you to create the database if necessary. I didn’t like how this  worked so I broke out it into it’s own code.

I created a class called SampleData that is used to create the database and seed it if necessary. I created it as a static class, but you can do it whatever way you want. I started with a static method called InitializeData:

public class SampleDataInitializer
{
private MyCountriesContext _ctx;
private UserManager<ApplicationUser> _userManager;

public SampleDataInitializer(MyCountriesContext ctx, UserManager<ApplicationUser> userManager)
{
  _ctx = ctx;
  _userManager = userManager;
}

Like any other class, I am simply going to use dependency injection to include the required objects for my sample data (I’m creating data in my context and creating sample users in ASP.NET Identity).

The real magic here is in a method called InitializeDataAsync:

public async Task InitializeDataAsync()
{
  await CreateUsersAsync();
  CreateVisits();
}

What is happening here is that I’m creating the users and visits. You’ll see later in the article why the CreateUsers needs to be async, but first creating the new visits is easy:

private void CreateVisits()
{
  if (!_ctx.Visits.Any())
  {
    _ctx.Visits.Add(new Visit()
    {
      Id = 0,
      UserName = "shawnwildermuth",
      Country = "France",
      City = "Paris",
      VisitDate = new DateTime(2014, 6, 4),
      Duration = 31,
      Notes = "Start of our round-the-world trip",
      ForWork = false,
      ForFun = true
    });

    // ...

    _ctx.SaveChanges();

  }
}

To start, I just see if any visits exist, if and if not, I create the visits. This code is just vanilla Entity Framework code (e.g. create objects, add them to the context, then save.

For the users in the system, I can’t just create the entities as there are rules in the Identity system. here I have to let UserManager do all the work:

private async Task CreateUsersAsync()
{
  var user = await _userManager.FindByEmailAsync("shawnwildermuth");
  if (user == null)
  {
    user = new ApplicationUser { UserName = "shawnwildermuth", Email = "shawn@foo.com" };
    await _userManager.CreateAsync(user, "P@ssw0rd!");

    await _userManager.AddClaimAsync(user, new Claim("ManageStore", "Allowed"));
  }

}

Because the calls to CreateAsync and AddClaimAsync requires me to use async, the method also has to be async.

So now that I have the SampleDataInitializer class, how do I call it. For me, I just called it in the startup.cs. (A bit brute force? Sure.) But the challenge was how to create an instance of the SampleDataInitializer using the dependency injection system to fulfill my dependencies?

The trick is to use the ConfigureServices dependency registration. You can simple add it via AddTransient so that  it’s created when you need it (and we’ll only need it once when the server is started):

// This method gets called by the runtime.
public void ConfigureServices(IServiceCollection services)
{
  // ...

  // Add other services
  services.AddTransient<SampleDataInitializer>();

Once that is registered, I can just use parameter injection to add it to the configure method (note the new SampleDataInitializer parameter):

public async void Configure(IApplicationBuilder app, 
                            IHostingEnvironment env, 
                            ILoggerFactory loggerfactory, 
                            SampleDataInitializer sampleData)
{
  // ...

Note that I changed the Configure to decorate it with async since our call to InitializeDataAsync requires async/await.  Now we can just use the initializer to initialize the data like so (somewhere in the Configure, I do it as the last step:
// Configure is called after ConfigureServices is called.
public async void Configure(IApplicationBuilder app, 
                            IHostingEnvironment env, 
                            ILoggerFactory loggerfactory, 
                            SampleDataInitializer sampleData)
{
  // ...

  // Add Sample Data
  await sampleData.InitializeDataAsync();

}

Now that database is being created and seeding, we can actually use the entities.

Using LINQ

The Entity Framework code inside of my project is no different than older EF projects:

public class MyCountriesRepository : IMyCountriesRepository
{
  private MyCountriesContext _context;
  public MyCountriesRepository(MyCountriesContext context)
  {
    _context = context;
  }

  public async Task<IEnumerable<Visit>> GetLatestVisits(int number)
  {
    return await _context.Visits
      .OrderByDescending(v => v.VisitDate)
      .Take(number)
      .ToListAsync();
  }

  // ...

So that once all this configuration and setup is done, you should be able to use it in the ways you’re used to. But there is one big caveat here:

Entity Framework 7 isn’t released yet so that lazy loading aren’t supported yet (as of the writing of this post) so you’ll need to use LINQ to load these manually. It’s a pain, but it works.

Next time, I’ll dive into the meat of ASP.NET MVC!




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.25009.03
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86