65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2015年9月19日

CPOL

4分钟阅读

viewsIcon

18961

我想使用 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 语句。请非常小心 StudentStudentsStudent 是一个类和一个类文件。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>
© . All rights reserved.