如何在 Entity Framework 5 中使用基于 SQL 会话的表
Posted
技术标签:
【中文标题】如何在 Entity Framework 5 中使用基于 SQL 会话的表【英文标题】:How to use SQL session-based tables with Entity Framework 5 【发布时间】:2017-11-28 16:18:34 【问题描述】:我有一个 WCF 数据服务,我打算在 插入 时使用一些基于会话的表函数(创建可在当前会话中使用的临时表)或更新。
我尝试像这样使用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)。
这是否意味着我不能在会话中使用 EF?还是每个 EF 函数都使用自己的上下文? 由于会话表被其他触发器使用,我需要有正确的键值。
我该怎么办?如果 EF 能够使用这些类型的功能,有什么提示吗?
更新:
我了解到 EF 在每个执行命令之前使用exec sp_reset_connection
,它重置所有临时变量和表。
所以我尝试创建一个事务来强制 EF 在一个会话中执行命令:
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();
它仍然会创建新会话,因此我无法真正合并这两个命令。
有什么建议吗?
【问题讨论】:
“基于会话的表函数”是什么意思? @DavidBrowne-Microsoft 我的意思是上下文,因为它使用上下文一段时间(会话)。我是不是措辞不好? 这可能与事务有关,可能表不会更新,因为你在同一个事务中。尝试在没有事务的情况下运行命令 - ***.com/questions/36609208/… @ZivWeissman 您的建议看起来很有希望,但我使用的是 Entity Framework 5,它没有事务修饰符作为第一个参数。有其他选择吗? @Nestor 嗯,我认为在 ef5 上没有 ExecuteSqlCommand 的事务,但您可以尝试使用纯 ADO.NET? 【参考方案1】:EF 将为每个命令打开和关闭 SqlConnection(导致连接重置),除非
-
显式打开连接并将其传递给 DbContext 构造函数,
调用 DbContext.Database.Connection.Open(),或
使用事务,这将导致连接池每次返回相同的连接。
当连接从隔离连接池中签出时,看起来 TransactionScope 不会抑制连接重置。因此,使用 TransactionScope,您仍然必须显式打开 DbContext.Database.Connection 才能在命令之间使用会话状态。
但 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();
【讨论】:
我无法真正对交易产生影响。请检查我更新的初始帖子。 问题是在 Entity Framework 5 中没有Database.BeginTransaction()
,我认为这很关键......这是 Entity Framework 6 的代码吗?
然后使用选项 1),并在 DbContext 中显式打开连接。在 DbContext 关闭之前,它将保持打开状态。以上是关于如何在 Entity Framework 5 中使用基于 SQL 会话的表的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Entity Framework 5 中使用 SQL Server OFFSET 和 FETCH FIRST?
如何在 ASP.NET 5 中将 Entity Framework 6 与 MySQL 一起使用?
如何关闭 Entity Framework Core 5 中的所有约定
如何在MVC 5的类库项目中使用Entity Framework 6 Code First