【问题标题】:How to prevent DbContext from altering the database?如何防止 DbContext 更改数据库?
【发布时间】:2013-09-22 16:45:19
【问题描述】:

我正在学习实体框架(目前使用的是 EF6 测试版),并且我正在现有数据库上使用代码优先模式。实体和DbContext 类是使用 T4 模板自动创建的。

我想阻止 DbContext 在运行时创建/更改数据库中的任何内容。

我该怎么做?

【问题讨论】:

  • 您是如何创建数据库的?默认情况下,EF Code First 会向数据库添加一个表,以便能够判断您的模型是否已更改。如果数据库不是由 codefirst/migrations 创建的,则 db 中没有数据,并且 EF 不会触及数据库的架构。如果您在谈论数据 - 任何时候调用 .SaveChanges() 数据库中的数据都可以添加/修改/删除
  • CodeFirst 不会更改您的数据库,如果它没有创建它。
  • 它确实创建了新对象,因为我一开始没有正确映射模式。我最终在默认 dbo 架构中重新创建了所有表。
  • 是的,它创建了新对象,因为它找不到任何对象,因为它们没有在默认模式中定义。请注意,由于它创建了表,因此它还添加了迁移历史表,其中保存了有关模型的信息。因此,它能够判断模型已更改,但与它创建的表相比,而不是已经存在的表。它还将使用它创建的表而不是原始表。如果您更改模型禁用初始化程序后,您的模型将不再匹配数据库,并可能导致运行时问题或数据损坏。
  • 您应该使用.ToTable(tableName, schemaName) 映射到非默认架构中的表。

标签: .net database entity-framework orm code-first


【解决方案1】:

我在使用 DBFirst 和 EF 6.1 时遇到了这个问题,它正在创建一个基于 EntityName 的新表。所以我修改了 OnModelCreating 虚拟方法来映射我的表。

因此,如果 EntityName 被命名为 MyStuffs,因为默认情况下 EnitityNames 是复数形式,您可以选择在 EF 生成期间取消选择它。在我的例子中,向导正在生成表格的复数版本。

public class MyContext: DbContext
{
    public MyContext()
        : base("name=MyEntityConnectionName")
    {
    }
    public MyContext(string connectionString)
        : base(connectionString)
    {
    }
    #region methods

    public static MyContext Create()
    {
        return new MyContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        // Map Entities to their tables.
        modelBuilder.Entity<MyModel>().ToTable("MyStuff"); //prevent a new table from being added since we added a mapping
    }

    #endregion
    public virtual DbSet<MyModel> MyEntities{ get; set; }
}

【讨论】:

    【解决方案2】:

    当您执行 enable-migrations 命令时,您将看到文件夹 /Migrations,您可以在该文件夹下找到名为 Configuration.cs 的文件.在该文件的构造函数中,默认情况下,有一个属性设置为 value:

     AutomaticMigrationsEnabled = false;
    

    这将确保您的数据库不会自动迁移,而是通过手动调用每个迁移。

    但是,如果您的数据库比您的域模型大,即您只是在现有数据库的一部分上进行操作,那么在您的应用程序的某个位置启动(如果它是 ASP.NET 应用程序,Application_Start 事件处理程序),您需要添加以下代码:

    Database.SetInitializer<YourDbContext>(null);
    

    否则,Entity Framework 将抱怨您的域模型中的数据库定义与数据库的实际当前状态不匹配。

    编辑:

    如果您想确保 YourDbContext 没有尝试更改数据库,请执行以下操作:

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
        throw new YourCustomException("Code first changes are not allowed."); 
    }
    

    再次编辑:

    我正在尝试了解您要完成的场景。如果您有现有的数据库,并且您想手动更新它,这就是方法:

    1. 使用 DbContext 生成器模板生成 DbContext 和实体 从现有数据库。
    2. 运行启用迁移
    3. 做添加迁移初始
    4. 如果一切都完成了,第 3 步应该会生成空迁移 适当地。您可以删除它。
    5. 现在,每当您进行一些更改时,您都需要进行添加迁移 更换名字。这不会进入数据库,除非你这样做 更新数据库。

    【讨论】:

    • 如果我在 OnModelCreating 方法中抛出异常,那么我还能在哪里通过 fluent api 定义配置?
    • 好的,我想了解您要做什么。我再次编辑了我的帖子,看看这对你是否有意义。
    • 正如我所说,我有一个生成实体和 DbContext 的 T4 模板。我需要它,因为我有命名和接口实现要求要遵循。默认生成器没有给我一种有效的方法来允许这种情况发生(除非我对它的工作原理一无所知)。不过,我认为您的答案非常完整,因此我将其标记为已接受。非常感谢您的帮助。 :)
    • 我希望 EF 创建表,但我不希望它添加迁移历史表。我怎样才能做到这一点?
    【解决方案3】:

    将上下文的初始化程序设置为null

    当应用程序启动时,这应该使数据库保持现有状态。

    请注意,如果您在更改 dbContext 类和/或数据库架构后尝试运行应用程序,则会遇到异常,因为您的数据库架构将不再符合上下文的预期。

    【讨论】:

    • 我不能阻止引用上下文程序集的任何其他程序集更改它的初始化程序是对的吗?我真的很不舒服。 :( 如果上下文以允许数据库更改的方式设置,我希望我可以在 SaveChanges 上引发异常。猜猜它不应该这样使用。
    • 如果您担心数据库的结构被更改,您可以将上下文的连接字符串更改为不授予它创建/更改表权限的连接字符串。如果您担心现有表的内容被更改,您可以使用对要保护的表具有只读权限的连接字符串。编辑:澄清一下,您需要在 SQL 服务器中设置新的(受限)帐户,并更改连接字符串以便使用这些帐户进行连接。
    • 为了解决这个问题:“如果上下文设置为允许数据库更改,我希望我可以在 SaveChanges 上引发异常。”,我修​​改了我的帖子。检查EDIT之间的解决方案,它可能会帮助你。
    • @ThinTim 我没有为该 dbcontext 使用专用连接字符串,它可以与任何 DBConnection 实例一起使用。如果由于某种原因没有授予某个用户对某个表的权限,那么我更喜欢让应用程序抛出 SecurityException 而不是无论如何都只是获取/写入数据。这是一件重要的事情,我不能就这样置之不理,恐怕。 :s
    • 我不确定我是否理解您的问题。如果您不希望您的应用程序更新任何数据,为什么不简单地避免调用SaveChanges()?如果您需要在调用SaveChanges() 之前检查用户是否有权进行更改,为什么不将上下文包装在执行这些规则的业务逻辑层中呢?如果您需要 sql server 来防止写入/更改,为什么不分配一个自定义连接字符串供 EF 使用?如果您需要对其他程序集隐藏上下文,为什么不将其设为internal
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多