sql - 如何在 Entity Framework 5 中使用基于 SQL session 的表

标签 sql entity-framework entity-framework-5 wcf-data-services sql-sessions

我有一个 WCF 数据服务,并且我打算在插入或插入时使用一些基于 session 的表函数(创建可在当前 session 中使用的临时表) 更新

我尝试使用 SaveChanges 方法,如下所示:

public partial class MyContext: DbContext
{
    public override int SaveChanges()
    {          
        var res = SetValues(true);
        var s = Database.SqlQuery<string>("SELECT [Key] FROM  TempContextView").ToList();
        System.IO.File.AppendAllText(@"c:\Temp\session.txt", $"SIZE S: {s.Count}, script res: {res}");
        foreach (var element in s)
        {            
            System.IO.File.AppendAllText(@"c:\Temp\session.txt", $"RES: {element}"); //never reached
        }
        return base.SaveChanges();
    }

    public int SetValues(bool insert)
    {
        System.IO.File.AppendAllText(@"c:\Temp\session.txt", "SetV: " + insert);
        return Database.ExecuteSqlCommand(insert ? "INSERT INTO TempContextView ([Key],[Value]) VALUES('Flag', '1')" : "DELETE FROM TempContextView WHERE[Key] = 'Flag'");
    }
}

TempContextView是一个 View ,它提供由函数创建的临时表:

SELECT  TOP (32) [Key], Value
FROM    Schema1.getContextTable()
ORDER BY [Key]

function [Schema1].[getContextTable]()
  RETURNS @Context TABLE([Key] varchar(126), [Value] varchar(126))
  WITH SCHEMABINDING
as...

但是,当我从该函数创建的表中选择值时,它不会返回任何内容(查询大小为 0,但插入返回 1)。

这是否意味着我无法在 session 中使用 EF?或者每个 EF 函数都使用自己的上下文? 由于 session 表被其他触发器使用,因此我需要有正确的键值。

对此我该怎么办? EF 是否能够使用这些类型的功能有任何提示吗?

更新:

我了解到 EF 在每个执行命令之前使用 exec sp_reset_connection,并且它重置所有临时变量和表

因此我尝试创建一个事务来强制 EF 在一个 session 中执行命令:

  using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
  {
    Database.ExecuteSqlCommand("INSERT INTO TempContextView ([Key],[Value]) VALUES('Flag', '1')"); //session #1?
    base.SaveChanges(); //session #2? :(
    scope.Complete();
  }

它仍然会创建新 session ,因此我无法真正合并这两个命令。

enter image description here

有什么建议吗?

最佳答案

EF 将为每个命令打开和关闭 SqlConnection(导致连接重置),除非

  1. 显式打开连接并将其传递给 DbContext 构造函数,
  2. 调用DbContext.Database.Connection.Open(),或
  3. 使用事务,这将导致连接池每次都返回相同的连接。

编辑:
看起来,当连接从隔离连接池中 check out 时,TransactionScope 不会抑制连接重置。因此,使用 TransactionScope 时,您仍然需要显式打开 DbContext.Database.Connection 才能在命令之间使用 session 状态。

但是 DbContext.Database.BeginTransaction() 可以工作(可能是通过在 DbContext 的生命周期内阻止连接池)。

这是一个使用 sp_set_sesson_context 的完整工作示例:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace ConsoleApp6
{
    [Table("Customers")]
    public class Customer
    {
        public int CustomerID { get; set; }
        public string Name { get; set; }

        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public string UpdatedBy { get; set; }
    }

    class Db : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<Db>());

            using (var db = new Db())
            {
                db.Database.Initialize(false);
                db.Database.ExecuteSqlCommand(@"
create trigger tg_customer on customers after insert, update 
as
begin
   update customers set UpdatedBy = cast(SESSION_CONTEXT(N'user') as varchar(200))
   where CustomerId in (select CustomerId from inserted);
end");
            }

            using (var db = new Db())
            {
                using (var tran = db.Database.BeginTransaction())
                {
                    db.Database.Log = m => Console.WriteLine(m);

                    db.Database.ExecuteSqlCommand(
                        "EXEC sp_set_session_context 'user', 'joe'"); //set session context

                    var c = db.Customers.Create();

                    c.Name = "Fred";

                    db.Customers.Add(c);

                    db.SaveChanges();
                    Console.WriteLine(c.UpdatedBy); //joe
                    tran.Commit();
                }
            }

            using (var db = new Db())
            {
                using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    db.Database.Connection.Open();
                    db.Database.ExecuteSqlCommand(
                        "EXEC sp_set_session_context 'user', 'alice'"); //set session context

                    var fred = db.Customers.Where(c => c.Name == "Fred").Single();

                    fred.Name = "Fred Jones";

                    db.SaveChanges();

                    Console.WriteLine(fred.UpdatedBy); //alice
                    scope.Complete();
                }
            }

            Console.ReadKey();
        }
    }
}

关于sql - 如何在 Entity Framework 5 中使用基于 SQL session 的表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44749315/

相关文章:

webforms - 什么 DataAnnotation 来创建 SQL Identity 列?

sql - 在 SET 赋值中,您可以使用变量以编程方式定义应为 SET 的列名吗?

sql - 看不懂SQL(Oracle)建表代码

mysql - 仅当列为空时才更新列——在replace into语句中

c# - 将 ObjectResult<int?> 转换为 List<int>

c# - 使用最小起订量模拟 EF DbContext

entity-framework - 覆盖 Entity Framework 5 中的 SaveChanges

python - 仅当日期和时间不冲突时才保存新实体(播放)

entity-framework - SQL Server 数据工具和 Edmx

c# - 嵌套列表的 LINQ to Entities 投影