【问题标题】:EF6: where to create stored procedures?EF6:在哪里创建存储过程?
【发布时间】:2016-05-25 21:25:29
【问题描述】:

我正在使用 Entity Framework 6 Code First。要使用我的数据库,我需要一个存储过程。每当创建数据库时,都需要创建此存储过程。

编辑:存储过程的原因写在这篇文章的末尾。

从 Stackoverflow 我知道如何创建存储过程以及如何调用它。我的问题是我在哪里创建它?

我的第一直觉是在 DbContext.OnModelCreating。每当创建模型时都会调用此函数。在这个函数中,您将告诉模型构建者模型应该是什么样子。

唉,在这个函数中我还不能使用 DbContext。有一些函数可以覆盖插入/更新/删除过程,但我需要一个不同的过程。

我考虑在 DropCreateDataBaseIfModelChanges 的 Seed 函数中添加该函数,但这会阻止我的 DbContext 的用户使用他们自己想要的数据来种子数据库。

DbMigrations 似乎是为了将现有数据库迁移到较新的数据库而开发的。在创建数据库的第一个版本时不使用。

那么在哪里创建存储过程呢?


编辑。我需要存储过程的原因如下

我的服务收到通知,说客户今天在某事上花钱了。我需要记住每位客户的总天数。

  • 如果客户在日期没有花费任何费用,也就是说,如果不存在带有 (customerId, date) 的记录,则插入带有花费值的记录
  • 如果客户已经在 datae 上花费了一些东西,即如果已经存在带有 (customerId, date) 的记录,则将花费的值添加到记录中的值中

类似这样的:

public void InsertOrAdd(int customerId, DateTime date, decimal value)
{
    using (var dbContext = CreateContext())
    {
        var retrievedRecord = dbContext.Find(...)
        if (retrievedRecord == null)
        {
            InsertRecord(customerId, date, value);
        }
        else
        {
            retrievedRecord.Value += value;
            dbContext.SaveChanges();
        }
    }
}

问题是在我的查找之后,其他人可能已经插入了一条记录。在那种情况下,我应该增加价值。相反,将有两条记录。或者,当我正在处理现有记录并添加值时,其他人可能会处理相同的现有记录。因此,应该在数据库中使用类似

的语句添加值
update [CustomerSpends]
Set [SpentValue] = [SpentValue] + @Value
Where [CustomerId] = @CustomerId and [SpentDate] = @SpentDate

第二次修改 正如* Ivan Yuriev* 已经提到的,上述问题可以使用交易来解决。但是,我仍然会遇到添加支出的问题。

如果这将是一个 3 步过程,就像 Ivan 建议的那样,那就是:

  • 获取现有记录
  • 将花费的值添加到记录中的总值中
  • 保存更改

这样做的缺点是我总是需要两次往返数据库。此外,我还得到了一系列客户支出。如果其中任何一个处理失败,则所有内容都需要回滚。即使使用事务也可能存在竞争条件

最高的Transaction Isolation level (MSDN) 被读提交。

已提交阅读 指定语句不能读取已被其他事务修改但未提交的数据。这可以防止脏读。当前事务中各个语句之间的其他事务可能会更改数据,从而导致不可重复读取或幻像数据。

当使用上面定义的三步过程时,我获取记录 X,总花费值为 5.00 美元。虽然我将 $3 添加到总数中,但其他人也可以获取记录 X,因为它还没有被修改。另一个人收到了总计 5 美元的记录。在我添加后,总值变为 8 美元。我使用 SaveChanges 更新该值并继续处理其他记录。过了一会儿,我提交了我的更改。仍然有$ 5记录的另一个进程应该添加$ 2,以$ 7的值结束,而最终结果应该是$ 10

使用事务不能阻止其他人读取数据。因此获取-增值-更新应该在一个SQL语句中完成

【问题讨论】:

  • 实体框架代码优先旨在使思维过程远离使用存储过程/sql。数据库迁移实际上用于表示您的数据库,通常在启动新数据库时使用,但可以通过逆向工程的任何一种方式使用。此代码表示与播种一起用于拆除数据库并根据环境需要将其备份。您需要 SP 是否有特定原因?
  • 类似:记住每位客户每天的成本。如果客户今天已经产生了成本,则添加新值,否则创建今天的成本并使用该值进行初始化。如果我确实查找并检测到它不存在,我可以添加,但与此同时其他人可能也做了同样的事情。因此,这必须在一个 SQL 语句中。可能类似于合并
  • 为什么不用事务来避免别人往数据库中插入记录呢?

标签: c# entity-framework stored-procedures


【解决方案1】:

通过创建空白迁移并在Up方法中添加您的存储过程并在Down方法中删除该存储过程来进行管理的最佳方式。

这将像这样工作。

1) 通过在包管理器控制台上运行命令添加空白迁移

add-migration 'SomeName' -IgnoreChanges

它将创建迁移文件

2) 现在在 Up 中添加存储过程并在 Down 中删除存储过程

public partial class SomeName: DbMigration
    {
        public override void Up()
        {
            Sql(@"Add Command For StoredProcedure");

        }

        public override void Down()
        {
            Sql(@"Drop Command for Stored Procedure")
        }
    } 

在Down方法中添加Drop同样重要。

【讨论】:

    【解决方案2】:

    无处可去。我发现代码优先迁移非常有限,只有对 SQL Server 概念和功能缺乏深入了解的人才能使用。

    我走的路是:

    • 我维护一个主数据库并将架构同步到一个架构项目。
    • 定期生成增量脚本,然后我手动维护。
    • 我有一个库,它根据存储在扩展属性中的数据库版本号根据需要应用增量脚本。

    不使用迁移的原因很简单——你最终不得不提交太多的 SQL 脚本。迁移甚至不处理琐碎的情况,例如正确的完全配置的索引(过滤索引),不知道存储过程、触发器和视图,无法处理分区表,无法有效处理可能需要临时表的数据迁移在更改表格布局时存储数据。因为我不处理琐碎的数据库,所以我几乎每天都需要这些功能。

    实话实说——我从来没有先找到代码,即使是一种有效的方法。这太容易出错了。唯一更糟糕的是 db first 使用 EF 工具 - 主要是因为 edmx 设计器已经很烂了,完全无法处理更新。不过可以使用第三方编辑器,而且它们可以工作。

    【讨论】:

    • 我最近也有同样的想法……你会推荐哪些第三方工具?
    • Devart 有一个替代编辑器,它比 MS 给我们的未完成的编辑器领先一步。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-06
    • 2012-01-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-14
    • 2010-10-24
    • 2012-03-12
    相关资源
    最近更新 更多