【问题标题】:Execute a SQL stored procedure before every query generated by EntityFramework在 EntityFramework 生成的每个查询之前执行 SQL 存储过程
【发布时间】:2010-10-14 06:06:38
【问题描述】:

每次查询我的 ObjectContext 之前,我都需要执行一个 SQL 存储过程。我想要实现的是将CONTEXT_INFO 设置为稍后将用于我的大多数查询的值。

有人做过吗?这可能吗?

[编辑]

目前我通过打开连接并在我的 ObjectContext 构造函数中执行存储过程来实现这一点,如下所示:

public partial class MyEntitiesContext
{       
    public MyEntitiesContext(int contextInfo) : this()
    {
        if (Connection.State != ConnectionState.Open)
        {
            Connection.Open(); // open connection if not already open
        }

        var connection = ((EntityConnection)Connection).StoreConnection;

        using (var cmd = connection.CreateCommand())
        {
            // run stored procedure to set ContextInfo to contextInfo
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "[dbo].[SetContextInfo]";
            cmd.Parameters.Add(new SqlParameter("@ci", _contextInfo));
            cmd.ExecuteNonQuery();
        }
        // leave the connection open to reuse later
    }
}

然后在我的集成测试中:

[TestMethod]
public void TestMethod1()
{
    using (var ctx = new MyEntitiesContext(1))
    {               
        Assert.AreEqual(2, ctx.Roles.ToList().Count);
        Assert.AreEqual(2, ctx.Users.ToList().Count);
    }
}

但这需要我保持连接打开 - 这很容易出错,因为我总是需要 CONTEXT_INFO,而另一个开发人员可能很容易做到:

[TestMethod]
public void TestMethod2()
{
    using (var ctx = new MyEntitiesContext(1))
    {               
        // do something here
        // ... more here :)
        ctx.Connection.Close(); // then out of the blue comes Close();
        // do something here
        Assert.AreEqual(2, ctx.Roles.ToList().Count);
        Assert.AreEqual(2, ctx.Users.ToList().Count); // this fails since the where
        // clause will be:
        // WHERE ColumnX = CAST(CAST(CONTEXT_INFO() AS BINARY(4)) AS INT)
        // and CONTEXT_INFO is empty - there are no users with ColumnX set to 0
        // while there are 2 users with it set to 1 so this test should pass
    }
}

以上意味着我可以像在我的测试中那样编写代码并且一切都是绿色的(耶!)但是我的同事在他的业务逻辑的某个地方使用了来自 TestMethod2 的代码,这一切都搞砸了 - 没有人知道在哪里以及为什么所有测试都是绿色的:/

[EDIT2]

This blog post 当然没有回答我的问题,但实际上解决了我的问题。也许使用 NHibernate 会更适合我的目的:)

【问题讨论】:

  • 谢谢 - 我想要的是在多租户应用程序中过滤租户数据。我不想将 TenantId 传递给每个存储过程,而是使用依赖于 CONTEXT_INFO 的视图,因此我想始终在查询数据库之前设置 CONTEXT_INFO。我仍然想使用 EF。我从一篇很棒的博文中得到了这个想法:blogs.imeta.co.uk/jyoung/archive/2010/03/22/845.aspx

标签: nhibernate entity-framework stored-procedures entity-framework-4 objectcontext


【解决方案1】:

我们使用了这种模式。

但是我们这样做的方式是调用存储过程作为每个数据库上下文中的第一个操作。

【讨论】:

  • 这很容易出错——也许可以用 AOP(如 PostSharp)很好地实现,但我仍然不想那样做。实际上,我可以编写自己的 ADO.NET 提供程序来包装 SqlClient,并且始终在提供 DbConnection 之前先执行存储过程,然后返回连接。但我不知道这是否可能与 SqlClient 的架构..
  • 我们将调用存储过程的代码放在它自己的方法中,然后我们可以进行计数并检查它被调用的次数与使用数据库上下文的次数是否匹配。
  • 好主意。仍然有人可以调用 ctx.Connection.Close();并且您计算 ctx 使用和方法调用的测试毫无价值..
【解决方案2】:

我终于找到了答案。我可以使用来自EFProviderWrappers 的 EFProvider 包装器工具包来包装连接。 为此,我主要必须从EFProviderWrapperConnection 派生并覆盖DbConnection.Open() 方法。我已经使用 Tracing 提供程序进行了尝试,它工作正常。使用我的解决方案对其进行测试后,我将添加更多信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 2015-11-20
    • 2017-06-26
    相关资源
    最近更新 更多