我使用的是 EF5 代码优先,因此我有一个带有 System.Data.Entity.Database 类型的数据库属性的 DbContext。
我发现的问题是,当您使用相同的参数多次调用同一个 SP 时,它会引发异常并显示消息:“SqlParameter 已被另一个 SqlParameterCollection 包含”。
这可以用下面的代码来演示。首先创建一个 DbContext 派生并将其连接到数据库。代码中的存储过程不必存在。第一次调用 SP 会出错,说 SP 不存在,但是,第二个异常是我们感兴趣的异常。
var pa = new SqlParameter[]
{
new SqlParameter("@Name", SqlDbType.NVarChar) { Value = "test" }
};
var dc = new MyWebContext(); // derived from DbContext
try
{
dc.Database.ExecuteSqlCommand("spImport @Name", pa);
}
catch { }
dc.Database.ExecuteSqlCommand("spImport @Name", pa); // fails with "The SqlParameter is already contained by another SqlParameterCollection"
有时,我确实需要用相同的参数调用相同的 SP 两次或更多次。这是一个有效的要求。我的假设是调用 ExecuteSqlCommand 是非常短暂的,应该可以在同一个上下文中多次调用。
上下文似乎挂在第一个调用的参数信息上,这导致第二个调用出现问题。
这是堆栈跟踪:
在 System.Data.SqlClient.SqlParameterCollection.Validate(Int32 index, Object value) 在 System.Data.SqlClient.SqlParameterCollection.AddRange(Array values) 在 System.Data.Objects.ObjectContext.CreateStoreCommand(String commandText, Object[] 参数)在 System.Data.Objects.ObjectContext.ExecuteStoreCommand(String commandText, Object[] 参数) 在 System.Data.Entity.Internal.InternalContext.ExecuteSqlCommand(String sql, Object[] 参数) 在 System.Data.Entity.Database.ExecuteSqlCommand (字符串 sql,Object[] 参数)在 EF5ExecuteSqlCommandBugReproduction.Program.Main(String[] args) 在 c:\EF5ExecuteSqlCommandBugReproduction\EF5ExecuteSqlCommandBugReproduction\Program.cs:System.AppDomain._nExecuteAssembly 的第 26 行(RuntimeAssembly 程序集,String[] args)在 System.AppDomain。Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ThreadHelper.ThreadStart_Context(Object state) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback 的 ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) Threading.ThreadHelper.ThreadStart()ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
我会很感激任何指导。如果您认为这是 EF 的错误,那么我会报告它。非常感谢
解决方案:将参数列表创建和 ExecuteSqlCommand 封装到一个内联函数中,然后重新调用它,而不是重新调用 ExecuteSqlCommand。这将确保创建一个新的 SqlParameter 数组。var dc = new SpondleWebContext(); // 派生自 DbContext
Action act = () =>
{
var pa = new SqlParameter[] { new SqlParameter("@Name", SqlDbType.NVarChar) { Value = "test" } };
dc.Database.ExecuteSqlCommand("spImport @Name", pa);
};
try { act(); } catch { }
act();