Visual Studio 2015 was recently released, and with it came a newer beta of ASP.NET 5 (formerly referred to as "ASP.NET vNext"). ASP.NET 5 is a complete rewrite of ASP.NET, focusing on being lightweight, composible, and cross-platform. It also includes an alpha version of Entity Framework 7. However, EF7 is not yet production-ready and does not support all features of EF6. One feature that is missing from EF6 is support for other database providers - Only SQL Server and SQLite are supported at this time.

I wanted to transition a site over to ASP.NET 5, but needed to continue using MySQL as a data source. This meant getting Entity Framework 6 running on ASP.NET 5, which is pretty much undocumented right now. All the documentation and tutorials for EF6 heavily relies on configuration in Web.config, which no longer exists in ASP.NET 5. In this post I'll discuss the steps I needed to take to get it running. An example project containing all the code in this post can be found at https://github.com/Daniel15/EFExample.

Since EF6 does not support .NET Core, we need to remove .NET Core support (delete "dnxcore50": { } from project.json). Once that's done, install the EntityFramework and MySql.Data.Entity packages, and add references to System.Data and System.Configuration. For this post, I'll be using this basic model and DbContext, and assume you've already created your database in MySQL:


public class MyContext : DbContext
{
	public virtual DbSet<Post> Posts { get; set; }
}

public class Post
{
	public int Id { get; set; }
	public string Title { get; set; }
	public string Content { get; set; }
}

Entity Framework 6 relies on the provider and connection string being configured in Web.config. Since Web.config is no longer used with ASP.NET 5, we need to use code-based configuration to configure it instead. To do so, create a new class that inherits from DbConfiguration:


public class MyDbConfiguration : DbConfiguration
{
	public MyDbConfiguration()
	{
		// Attempt to register ADO.NET provider
		try {
			var dataSet = (DataSet)ConfigurationManager.GetSection("system.data");
			dataSet.Tables[0].Rows.Add(
				"MySQL Data Provider",
				".Net Framework Data Provider for MySQL",
				"MySql.Data.MySqlClient",
				typeof(MySqlClientFactory).AssemblyQualifiedName
			);
		}
		catch (ConstraintException)
		{
			// MySQL provider is already installed, just ignore the exception
		}

		// Register Entity Framework provider
		SetProviderServices("MySql.Data.MySqlClient", new MySqlProviderServices());
		SetDefaultConnectionFactory(new MySqlConnectionFactory());
	}
}

The first part of the configuration is a hack to register the ADO.NET provider at runtime, by dynamically adding a new configuration entry to the system.data section. The second part registers the Entity Framework provider. We also need to modify the configuration file to include the connection string. You can use any configuration provider supported by ASP.NET 5, I'm using config.json here because it's the default provider.


{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=localhost; Database=test; Uid=vmdev; Pwd=password;"
    }
  }
}

Now that we have the configuration, we need to modify the context to use it:


[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContext : DbContext
{
	public MyContext(IConfiguration config)
		: base(config.Get("Data:DefaultConnection:ConnectionString"))
	{
	}
	// ...
}

An instance of IConfiguration will be automatically passed in by ASP.NET 5's dependency injection system. The final step is to register MyContext in the dependency injection container, which is done in your Startup.cs file:


public void ConfigureServices(IServiceCollection services)
{
	// ...
	services.AddScoped<MyContext>();
}

AddScoped specifies that one context should be created per request, and the context will automatically be disposed once the request ends. Now that all the configuration is complete, we can use MyContext like we normally would:


public class HomeController : Controller
{
    private readonly MyContext _context;

    public HomeController(MyContext context)
    {
	    _context = context;
    }

    public IActionResult Index()
    {
        return View(_context.Posts);
    }
}

Hope you find this useful!

Until next time,
— Daniel

Short URL for sharing: http://dan.cx/B5M. This entry was posted on 1st August 2015 and is filed under Programming, C#. You can leave a comment if you'd like to, or subscribe to the RSS feed to keep up-to-date with all my latest blog posts!

Comments

  1. Avatar for radams0x radams0x said:

    Hi Daniel,

    Awesome sample, got it working, very helpful!!!

    A couple of things I found that may help others; I am on a Mac, OS X Yosemite, using ASP.Net 5 beta6:

    The code starting in line 7 of MyDbConfiguration():

    dataSet.Tables[0].Rows.Add(

    didn't work for me - I still got an exception regarding the ADO.Net provider not being found. Based on advice I found from others, I then manually installed MySQL.Data.dll and MySQL.Data.Entify.EF6.dll, did the gacutil thing, and then manually edited the machine.config under mono to registered MySql:

    <system.data>
    <dbproviderfactories>
    <remove invariant="MySql.Data.MySqlClient"></remove>
    <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

    After that I got an error indicating a unique constraint on invariant, so I commented out the code code starting in line 7 of MyDbConfiguration(): dataSet.Tables[0].Rows.Add( ...

    Then everything worked ....

    Some more details for the unfamiliar: some samples auto initialize the db - this sample will throw an error if you don't have a Posts table in the db.

    The MVC view failed silently for most of the above errors - I found it helpful to add logging and exception handling, and also to enable an error handler view:

    in Startup.cs:

    public void Configure(IApplicationBuilder app)
    {

    app.UseErrorHandler("/Home/Error");

    ...

    ..

    /Home/Error.cshtml:

    @model Exception
    @{
    ViewBag.Title = "EXCEPTION!";
    }

    <h2 class="text-danger">An error occurred while processing your request.</h2>
    @if (Model != null)
    {
    @Html.ValueFor(model => model.Source)
    @Html.ValueFor(model => model.Message)
    @Html.ValueFor(model => model.StackTrace)
    }

    Thanks!!

    1. Avatar for Daniel Lo Nigro Daniel Lo Nigro said:

      I'm glad it helped!

      I noticed the same thing with Mono - Adding the ADO.NET provider at runtime doesn't work. I fixed it the same way you did - by GACing MySQL.Data.dll and editing machine.config. I didn't actually test on Mono until after I published the post so I didn't notice this until afterwards. When I get some free time I'll amend the post to include this information :)