【问题标题】:"Convert" Entity Framework program to raw SQL将实体框架程序“转换”为原始 SQL
【发布时间】:2014-06-04 06:34:05
【问题描述】:

我使用 Entity Framework 为一个项目创建了一个原型,现在它正在工作,我想让程序准备好投入生产。

我在使用 EF 时面临许多挑战,其中最大的挑战是并发管理(它是一个财务软件)。

鉴于似乎无法使用 EF 处理悲观并发,我不得不切换到 SQL 中的存储过程。

说实话,我有点害怕可能代表的工作量。

我想知道以前是否有人遇到过同样的情况,以及使用 EF 将 .net 代码转换为原始 SQL 的最佳策略是什么。

编辑: 我正在调查 CLR,但尚不清楚是否可以用它来管理悲观并发。在这种情况下,它是比 TSQl 更有趣的选项吗?如果我理解得很好,它将允许我重用我的部分 C# 代码和调用另一个函数的函数结构。

【问题讨论】:

  • 您使用的是哪个版本的实体框架?您也是先使用代码,先使用数据库还是先使用模型?无论答案是什么,您仍然可以使用 EF,这取决于它将做多少工作。我们使用 Code First EF 5,仍然在 CUD 上进行悲观并发,但不进行读取。
  • 我首先使用的是 EF 6 模型。你如何用 EF 实现悲观并发?
  • 这有点复杂,但除了更新 SQL Server 之外,我们都使用 EF。我们使用 T-SQL 执行此操作,但仍使用 EF 实体跟踪。我会添加一个答案来展示。
  • 您可以将所有业务事务(或工作单元)包含在一个具有高隔离级别(例如可序列化,甚至可重复读取)的 TransactionScope 中。
  • 我在想那个 Gert,问题是:如果在一个 TSQL 事务中我调用了几个 CLR,那么这些 CLR 存储过程上使用的所有行也会被锁定吗? (我需要的行为)

标签: sql .net sql-server entity-framework stored-procedures


【解决方案1】:

我在那里,好消息是如果你不想放弃实体框架,你不必放弃。坏消息是您必须自己更新数据库。这并不像看起来那么难。我目前正在使用 EF 5,但计划转到 EF 6。我不明白为什么这仍然不适用于 EF 6。

首先是在 DbContext 的构造函数中将其转换为 IObjectContextAdapter 并获得对 ObjectContext 的访问。我为此创建了一个属性

public virtual ObjectContext ObjContext
{
    get
    {
        return ((IObjectContextAdapter)this).ObjectContext;
    }
}

一旦您订阅了 SavingChanges 事件 - 这不是我们的确切代码,有些内容会从其他方法中复制出来并重做。这只是让您了解您需要做什么。

ObjContext.SavingChanges += SaveData;

private void SaveData(object sender, EventArgs e)
{
    var context = sender as ObjectContext;
    if (context != null)
    {
        context.DetectChanges();
        var tsql = new StringBuilder();
        var dbParams = new List<KeyValuePair<string, object>>();

        var deletedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted);
        foreach (var delete in deletedEntites)
        {
            // Set state to unchanged - so entity framework will ignore
            delete.ChangeState(EntityState.Unchanged);
            // Method to generate tsql for deleting entities
            DeleteData(delete, tsql, dbParams);
        }

        var addedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
        foreach (var add in addedEntites)
        {
            // Set state to unchanged - so entity framework will ignore
            add.ChangeState(EntityState.Unchanged);
            // Method to generate tsql for added entities
            AddData(add, tsql, dbParams);
        }

        var editedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
        foreach (var edit in editedEntites)
        {
            // Method to generate tsql for updating entities
            UpdateEditData(edit, tsql, dbParams);
            // Set state to unchanged - so entity framework will ignore
            edit.ChangeState(EntityState.Unchanged);
        }
        if (!tsql.ToString().IsEmpty())
        {
            var dbcommand = Database.Connection.CreateCommand();
            dbcommand.CommandText = tsql.ToString();

            foreach (var dbParameter in dbParams)
            {
                var dbparam = dbcommand.CreateParameter();
                dbparam.ParameterName = dbParameter.Key;
                dbparam.Value = dbParameter.Value;
                dbcommand.Parameters.Add(dbparam);
            }
            var results = dbcommand.ExecuteNonQuery();
        }
    }
}

为什么我们在更新后将实体设置为未修改,因为你可以这样做

var changed properties = edit.GetModifiedProperties();

获取所有已更改属性的列表。由于所有实体现在都标记为未更改,因此 EF 不会向 SQL 发送任何更新。

您还需要处理从实体到表以及从属性到字段的元数据。这并不难做到,但弄乱元数据确实需要一些时间来学习。有时我仍然在挣扎。我将所有这些重构到一个 IMetaDataHelper 接口中,在其中我将它传递给实体类型和属性名称以获取表和字段 - 同时缓存结果,因此我不必一直查询元数据。

最后,tsql 是一个批处理,它包含我们想要的所有 T-SQL,带有锁定提示并包含事务级别。我们还将数字字段从仅设置为 nfield = 10 更改为在 TSQL 中为 nfield = nfield + 2 如果用户将它们更新为 2 以避免并发问题。

一旦有人开始编辑您的实体,您就不会锁定 SQL,但我不知道您如何使用存储过程来获得它。

总而言之,我花了大约 2 天的时间来为我们完成这一切。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多