【问题标题】:How do I create a database with EF and begin using it?如何使用 EF 创建数据库并开始使用它?
【发布时间】:2019-11-29 22:12:18
【问题描述】:

我正在编写一个小应用程序,作为练习来学习如何在 C# .NET 中使用 SQL 数据库。我的应用程序尝试打开与数据库的连接,如果失败,它会假定数据库不存在并创建它。该数据库仅由我的应用程序使用,并且始终是本地的,因此它比许多其他数据库情况要简单得多。下面是代码,基于我在 SO 以及 Andrew Troelsen 的“Pro C# 7: With .NET and .NET Core”中阅读的内容:

private static DbConnection connection = null;
private static MyAppModels context = null;
public static void SetupDatabase()
{
    string dataProvider = "System.Data.SqlClient";
    DbProviderFactory factory = DbProviderFactories.GetFactory(dataProvider);

    // get connection object (closes automatically at end of using scope)
    connection = factory.CreateConnection();
    if (connection == null) { /* throw Exception */ }

    string connectionString = @"Data Source=(localdb)\mssqllocaldb;Initial Catalog=MyAppDB;Integrated Security=True";
    connection.ConnectionString = connectionString;
    try { connection.Open(); }
    catch (Exception)
    {
        // database could not be found; try to create it...
        connection.ConnectionString = @"Data Source=(localdb)\mssqllocaldb;Integrated Security=True;database=master";
        connection.Open();
        string str = "CREATE DATABASE MyAppDB ON PRIMARY " +
                     "(NAME = MyApp_Data, FILENAME = 'C:\\Users\\Me\\Documents\\MyApp\\MyAppData.mdf', " +
                     "SIZE = 2MB, MAXSIZE = 50MB, FILEGROWTH = 10%) " +
                     "LOG ON (NAME = MyApp_Log, FILENAME = 'C:\\Users\\Me\\Documents\\MyApp\\MyAppLog.ldf', " +
                     "SIZE = 1MB, MAXSIZE = 10MB, FILEGROWTH = 10%)";
        DbCommand myCommand = connection.CreateCommand();
        myCommand.CommandText = str;
        try { myCommand.ExecuteNonQuery(); }
        catch (System.Exception ex) { /* throw Exception */ }
    }

    context = new MyAppModels(connection);
    return;
}

在我尝试向表中添加数据之前,这段代码似乎可以毫无问题地执行,此时我收到以下错误:

“支持 'MyAppModels' 上下文的模型在创建数据库后发生了变化。考虑使用 Code First 迁移来更新数据库 (http://go.microsoft.com/fwlink/?LinkId=238269)。”

如果我退出我的应用程序并重新打开它,它就能够连接到现有的数据库并毫无问题地添加记录。但我不希望我的应用程序的用户在首次安装后被要求退出并重新打开应用程序,只是为了解决这个问题!

我在这里查看了其他几个帖子,包括:

The model backing the 'POSContext' context has changed since the database was created

The model backing the context has changed since the database was created

这两个似乎都指向同一个解决方案。此外,第二个引用了 Microsoft EF 团队成员的以下引用:

第一次创建模型时,我们会运行 DatabaseInitializer 来执行诸如创建数据库(如果数据库不存在)或添加种子数据等操作。默认的 DatabaseInitializer 尝试将使用模型所需的数据库架构与存储在使用数据库创建的 EdmMetadata 表中的架构哈希进行比较(当 Code First 是创建数据库的人时)。现有的数据库不会有 EdmMetadata 表,因此不会有哈希......如果该表丢失,今天的实现将抛出。在我们发布最终版本之前,我们将努力改变这种行为,因为它是默认的。在此之前,现有数据库通常不需要任何数据库初始化程序,因此可以通过调用以下方法为您的上下文类型关闭它:

Database.SetInitializer<YourDbContext>(null);

我真的不明白这一切。但我还是尝试将该代码添加到我的 DbContext 类中:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    Database.SetInitializer<MyAppModels>(null);
    base.OnModelCreating(modelBuilder);
}

现在,当我尝试将第一条记录添加到表时出现以下错误:

“更新条目时发生错误。有关详细信息,请参阅内部异常。”

内部异常: “字符串或二进制数据将被截断。语句已终止。”

现在,如果我关闭并重新打开应用程序,每次都会出现相同的错误。所以现在情况更糟了。通过查看其他 SO 帖子,我看到此错误指向输入数据库的数据超出字段大小的问题。但是我在尝试添加的第一条记录上遇到了错误,它肯定不会超过该表中的任何字段大小。

我怀疑我不应该将 null 传递给 SetInitializer()。我注意到了评论,“......我们运行一个 DatabaseInitializer 来执行诸如创建数据库(如果它不存在)或添加种子数据之类的事情。”我的代码已经在尝试处理数据库创建。我的应用程序不应该这样做吗? SetInitializer() 如何知道放置数据库或 MAXSIZE 的位置?而且我认为我也不需要它来“添加种子数据”。

我还注意到了这条评论,“我们将在发布最终(原文如此)版本之前努力改变这种行为,因为它是默认的。”这使它听起来像一个错误。该评论是大约 9 年前写的,所以我希望它已经修复了。

在此期间,有没有人有任何关于如何让它正常工作的提示?

--更新--

我能够通过对上面显示的创建代码的一小部分添加来完成这项工作:

myCommand.ExecuteNonQuery();
// close & try again to open the database
connection.Close();
connection.ConnectionString = @"Data Source=(localdb)\mssqllocaldb;Integrated Security=True;Initial Catalog=MyAppDB";
connection.Open();

似乎没有必要,但它确实有效......

然后我尝试了其他方法。正如我在几个示例教程中看到的那样,我用一行替换了 SetupDatabase() 中的所有代码:

context = new MyAppModels();

这允许/依赖 EF 打开数据库,并在必要时创建它(在某些未定义/默认位置)。这在数据库已经存在时有效,但是当需要创建数据库时,它失败了:

System.Data.SqlClient.SqlException HResult=0x80131904
Message=CREATE FILE 遇到操作系统错误 5(访问被拒绝。) 尝试打开或创建物理文件时 'C:\Users\MyAppDB.mdf'。创建数据库失败。一些文件 无法创建列出的名称。检查相关错误。来源=.Net SqlClient 数据提供者

注意和 MyAppDB.mdf 之间没有反斜杠。我不知道为什么会发生这种情况或如何解决它。我在 App.config 中看不到任何可以控制它的明显内容...

<connectionStrings>
  <add name="MyAppConnection" connectionString="data source=(localdb)\mssqllocaldb;initial catalog=MyAppDB;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>

有人可以帮我解决这个问题,这样我就可以使用“纯”EF,还是我需要回到我的 hack 工作......?

【问题讨论】:

    标签: c# .net sql-server database entity-framework


    【解决方案1】:

    是代码,基于我在 SO 和“Pro C# 7: With .NET and .NET Core”中阅读的内容

    您似乎已尝试混合手动创建数据库和code first migrations

    结果很糟糕。

    我们能解决这个问题吗?

    是的...有一些额外的细节/代码,甚至可能需要从头开始重新设计很多东西,所以我不确定这是否值得花时间,因为您似乎正在研究技术,而不是尝试解决一些关键业务内部程序套件中的一些技术混乱。

    因此,我建议您决定要学习的内容并使用标准教程逐步完成:

    1. 您想了解如何使用 EF 上下文和代码优先迁移? - https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/
    2. 想了解如何使用现有的数据库? - https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/workflows/existing-database
    3. 想了解如何使用基本的 ADO.NET 类(DbConnection、DbDataReader) - https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ado-net-code-examples
    4. 想要在两者之间做点什么吗? - 只需确保在混合它们之前您已经分别使用过每个东西,并且您真的想尝试它(学术兴趣/实验愿望都很好,但对实际问题应用非标准方法通常是个坏主意)。

    P.S.:您似乎在使用 EF6(就像您使用 DatabaseInitializer 一样),但实际上我建议您开始学习 EF Core,因为它是 Entity Framework 的最新迭代。

    【讨论】:

    • 小崽子,我从经验中知道 SO 对学习者来说可能是残酷的,但我没想到会这样......
    • “您似乎已经尝试混合手动创建数据库和代码优先迁移。”让我解释一下 - 我找不到有关如何在 EF 中创建数据库的信息,所以我觉得我必须混合方法。它们提供了一种基于连接创建上下文的方法,所以我认为它应该可以工作。
    • "...结果很糟糕。我们能解决这个问题吗?是的...有一些额外的细节/代码,它甚至可能需要从头开始修改很多东西,所以我不是肯定值得花时间”我认为你在放大问题。大约 30 行代码。从头开始重写/重写是微不足道的。此外,我的其余代码都是 EF,与您在此处看到的不同,因此不需要返工。我已经成功地将 > 11,500 条记录导入到 7 个表中。只是最初的创建部分很不稳定。
    • 感谢您抽出时间提供一些链接。我可以看到,一旦我接近发布,迁移可能会变得有用,但在大量开发过程中,它看起来完全是矫枉过正。这绝对超出了我在这里提出的问题。所以我可以使用 DbDataReader,但我认为以一种更 OO 的方式做事会很好。
    • 我通常认为 Microsoft 文档非常糟糕。 Def 不是为学习者编写的。它也已过时 - 有些页面已有 3 年历史,但 EF 一直在发生变化,正如您向我指出的 EF Core 一样。我在那里看到的关于在新数据库中使用 EF 的视频是针对 Visual Studio 2012 的!!!真的不知道有多少仍然适用。就像 SO,MS 信息解决了如何,没有真正解释大局 - 为什么和何时。对学习者不友好。我会(再次)在 YouTube 上搜索。
    【解决方案2】:

    您发布的代码是您如何使用“动态”SQL“手动”创建数据库。使用 Entity Framework 时,您根本不需要使用这种方法。

    开始使用实体框架的最简单方法之一是使用模型优先:

    • 向您的项目添加新的“ADO.NET 实体数据模型”。
    • 选择“空 EF 设计器模型”。
    • 编辑模型以设计您的数据库。
    • 保存后(或在 VS2019 中,您必须在 .tt 文件上“运行自定义工具”),您将创建所有实体类。
    • 在模型中选择“从模型生成数据库”以生成一个 SQL 文件,其中包含使用该模型创建新数据库所需的所有 SQL 语句。此步骤还会在数据库和 C# 代码之间创建所有必要的绑定。 (所有这些都存在于幕后。)

    您添加到模型中的实体成为数据库中的表和 C# 代码中的类。

    您添加的属性成为数据库中的字段和 C# 代码中的属性。

    您添加的关联成为数据库中的外键关系和 C# 代码中的集合(“导航属性”)。

    然后您可以开始使用 LINQ 和 IQueryable 在 C# 代码中构建查询。

    您可以对模型进行更改,然后再次执行“从模型生成数据库”过程以获取创建数据库的新 SQL 文件,但请注意,它首先会nuke任何现有的执行脚本时的数据库。这在测试时可以,但它也有助于使用版本控制系统,以便您可以看到对 SQL 的更改,然后对数据库结构进行手动更改,以确保数据库与模型匹配,而无需删除它首先。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-26
      • 1970-01-01
      • 2020-12-16
      • 2013-10-11
      • 1970-01-01
      • 1970-01-01
      • 2021-06-24
      • 2020-12-03
      相关资源
      最近更新 更多