使用 ASP MVC 和 MySQL 作为 Azure 中的模型存储





5.00/5 (3投票s)
我想使用 MySQL 作为数据库来存储 Code First 模型,并配合 ASP MVC 使用。
引言
微软允许拥有 Dreamspark 账户的学生免费获得 Azure 网站托管。然而,使用 MS SQL Server 是收费的,但 MySQL 在 Azure 中是免费的(针对小型数据库)。因此,为了所有希望编写 ASP MVC 程序并使用 Azure 的学生,我研究了如何实现这一点。搜索网络,有很多文章包含了部分解决方案,但我找不到一个可行的示例。这个“拼接怪”程序汇集了多篇文章的代码片段,创建了一个简单的示例。我假设读者熟悉使用 MS SQL Server 构建 MVC 应用程序,并且只想了解如何切换到 MySQL。
背景
主要的程序代码大部分来自 这里 和 这里,以及解决“迁移历史表,__migrationhistory
”问题的代码来自 这里。我强烈建议您仔细阅读那些文章,如果我的项目中存在您不理解的代码。我没有重复那些文章中所有有用的内容。
Using the Code
这是 Contoso University MVC 示例的一个小型版本,该示例出现在许多文章中,这里只有一个学生数据,以减少混乱并专注于 MySQL 连接挑战。当您粘贴我的代码时,各种类引用会未定义,但是新的 VS2015 工作得很好,只需右键单击并选择“快速修复”,让 VS 添加正确的 Using
语句。请非常小心 Student
和 Students
。Student
是一个类和一个类文件。Students
是数据库表。多一个或少一个“s
”都会导致项目中断。
要重新创建它,请使用 VS2015,创建一个没有身份验证的新 MVC 项目。我建议使用我相同的项目名称(ContosoMySQLAzure
),这样粘贴我的代码更有可能成功。将 Home Index.cshtml 视图中的代码替换为:
@{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>Contoso University</h1>
</div>
<div class="row">
<div class="col-md-4">
<h2>Welcome to Contoso University</h2>
<p>
Contoso University is a sample application that
demonstrates how to use Entity Framework 6 in an
ASP.NET MVC 5 web application. This one uses MySQL.
</p>
</div>
</div>
从“工具”菜单中,选择“NuGet 包管理器”,然后选择“程序包管理器控制台”。插入以下命令:
>Install-Package MySql.Data.Entity
这将加载与 MySQL 配合使用的 Entity Framework 代码。
现在,通过在 Model 文件夹中添加一个新的 Student.cs 类文件来创建您的学生数据模型:
using System;
using System.Collections.Generic;
namespace ContosoMySQLAzure.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
}
现在创建数据库上下文(处理和跟踪各种 Entity Framework 操作的类),通过向项目中添加一个名为 DAL 的新文件夹,然后在该文件夹中创建一个新的 SchoolContext.cs 类文件:
using ContosoMySQLAzure.Models;
using System.Data.Entity;
namespace ContosoMySQLAzure.DAL
{
public class SchoolContext : DbContext
{
public SchoolContext() : base("SchoolContext")
{
Database.SetInitializer<SchoolContext>(new MyDbInitializer());
}
public DbSet<Student> Students { get; set; }
}
}
在 DAL 文件夹中添加另一个新类 MyDbInitializer.cs,这用于预加载一个空数据库并包含一些测试值:
using ContosoMySQLAzure.Models;
using System;
using System.Data.Entity;
namespace ContosoMySQLAzure.DAL
{
// after you have the application running, you might want to use:
// public class MyDbInitializer : CreateDatabaseIfNotExists<SchoolContext>
// but while debugging, when you want a clean and seeded DB, use:
public class MyDbInitializer : DropCreateDatabaseAlways<SchoolContext>
{
protected override void Seed(SchoolContext context)
{
// create 3 students to seed the database
context.Students.Add(new Student
{ ID = 1, FirstName = "Mark", LastName = "Richards", EnrollmentDate = DateTime.Now });
context.Students.Add(new Student
{ ID = 2, FirstName = "Paula", LastName = "Allen", EnrollmentDate = DateTime.Now });
context.Students.Add(new Student
{ ID = 3, FirstName = "Tom", LastName = "Hoover", EnrollmentDate = DateTime.Now });
base.Seed(context);
}
}
}
编辑 Web.config 文件,并添加一个连接字符串到 Azure 中的 MySQL 实例。我使用了 Azure 门户来创建我的 MySQL 数据库。您需要提前创建一个 Azure MySQL 帐户,并创建一个名为 ContosoDataBase
的空数据库。创建该数据库时,获取并保存连接字符串,然后用适合您帐户的值编辑下面的代码。
<connectionStrings>
<add name="SchoolContext" providerName="MySql.Data.MySqlClient"
connectionString="Server=us-cdbr-azure-west-c.cloudapp.net; Database=ContosoDataBase;
User Id=xxxxxxxxxxxxxx; Password=xxxxxxxx;" />
</connectionStrings>
注意:网络上许多早期的 MySQL EF 文章都显示了对 Web.config 文件中 <entityFramework>
部分的更改。然而,对于当前版本的 EF,Web.config 中的其他所有内容都可以正常工作,无需更改。
现在,要启用迁移(如果您的模型发生更改)并允许 EF 创建用于保存您的学生模型的数据库表,您需要执行一些额外的步骤。首先,再次从“工具”菜单中,选择“NuGet 包管理器”,然后选择“程序包管理器控制台”。插入以下命令:
>enable-migrations
这将创建一个名为 Migrations 的新文件夹,并在其中创建一个名为 Configuration
的新类文件。
Entity Framework Code First 使用迁移历史来跟踪模型更改并确保数据库的一致性,但是迁移历史表 __migrationhistory
的主键对于 MySql 来说太大了。我从 这个技巧 中获得了此问题的解决方案。
在 Migrations 文件夹下,添加一个名为 MySqlHistoryContext.cs 的新类,并添加以下代码:
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Migrations.History;
namespace ContosoMySQLAzure.Migrations
{
public class MySqlHistoryContext : HistoryContext
{
public MySqlHistoryContext(DbConnection connection, string defaultSchema)
: base(connection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();
}
}
}
并且在此迁移文件夹中,将 Configuration.cs 文件更改为:
namespace ContosoMySQLAzure.Migrations
{
using DAL;
using System.Data.Entity;
using System.Data.Entity.Migrations;
internal sealed class Configuration : DbMigrationsConfiguration<SchoolContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
// register mysql code generator
SetSqlGenerator("MySql.Data.MySqlClient",
new MySql.Data.Entity.MySqlMigrationSqlGenerator());
SetHistoryContextFactory("MySql.Data.MySqlClient",
(conn, schema) => new MySqlHistoryContext(conn, schema));
}
protected override void Seed(SchoolContext context)
{
}
}
}
接下来,创建一个自定义数据库初始化器,因为 MySQL 提供程序不支持 Entity Framework 迁移。向项目中添加一个名为 MySqlInitializer.cs 的新类文件(不要与 DAL 文件夹中的 MyDbInitializer.cs 文件混淆),并使用此代码:
using ContosoMySQLAzure.DAL;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace ContosoMySQLAzure
{
public class MySqlInitializer : IDatabaseInitializer<SchoolContext>
{
public void InitializeDatabase(SchoolContext context)
{
if (!context.Database.Exists())
{
// if database did not exist before - create it
context.Database.Create();
}
else
{
// query to check if MigrationHistory table is present in the database
var migrationHistoryTableExists =
((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery<int>(
string.Format(
"SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = '{0}' AND table_name = '__MigrationHistory'",
"ContosoDataBase")); // this last parameter is the name of your DB
// if MigrationHistory table is not there
// (which is the case first time we run) - create it
if (migrationHistoryTableExists.FirstOrDefault() == 0)
{
context.Database.Delete();
context.Database.Create();
}
}
}
}
}
为了使这个新的 MySqlInitializer
数据库初始化器能够工作,您需要更改应用程序的启动代码以注册初始化器。编辑 Global.asax.cs 并在以下方法中添加一行:
protected void Application_Start()
{
Database.SetInitializer(new MySqlInitializer()); // <<<<<< add this line
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); // ...
现在,创建一个 MVC 控制器来使用此数据。右键单击 Controller 文件夹,然后选择“添加新脚手架项”,选择“使用 Entity Framework 的 MVC 5 控制器”,Model
类是 Student
,数据上下文类是 SchoolContext
,名称是 StudentController
。
最后,编辑 _Layout.cshtml 文件以添加指向新控制器的链接:
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
<li>@Html.ActionLink("Students", "Index", "Student")</li>
</ul>
现在应该可以工作了,无论是本地连接到 Azure MySQL,还是您可以将应用程序部署到 Azure 并在云端运行所有内容。
历史
2015年10月1日:添加了对 Global.asax.cs 文件所需的更改,并在此处的 MyDbInitializer.cs 周围更改了文本。
// probably want this after app is debugged and "live"
//public class MyDbInitializer : CreateDatabaseIfNotExists<SchoolContext>
// this is appropriate for debugging when you want a fresh copy and seeded
public class MyDbInitializer : DropCreateDatabaseAlways<SchoolContext>