SQL Server: Isolation level leaks across pooled connections - sql-server

As demonstrated by previous Stack Overflow questions (TransactionScope and Connection Pooling and How does SqlConnection manage IsolationLevel?), the transaction isolation level leaks across pooled connections with SQL Server and ADO.NET (also System.Transactions and EF, because they build on top of ADO.NET).
This means, that the following dangerous sequence of events can happen in any application:
A request happens which requires an explicit transaction to ensure data consistency
Any other request comes in which does not use an explicit transaction because it is only doing uncritical reads. This request will now execute as serializable, potentially causing dangerous blocking and deadlocks
The question: What is the best way to prevent this scenario? Is it really required to use explicit transactions everywhere now?
Here is a self-contained repro. You will see that the third query will have inherited the Serializable level from the second query.
class Program
{
static void Main(string[] args)
{
RunTest(null);
RunTest(IsolationLevel.Serializable);
RunTest(null);
Console.ReadKey();
}
static void RunTest(IsolationLevel? isolationLevel)
{
using (var tran = isolationLevel == null ? null : new TransactionScope(0, new TransactionOptions() { IsolationLevel = isolationLevel.Value }))
using (var conn = new SqlConnection("Data Source=(local); Integrated Security=true; Initial Catalog=master;"))
{
conn.Open();
var cmd = new SqlCommand(#"
select
case transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'ReadUncommitted'
WHEN 2 THEN 'ReadCommitted'
WHEN 3 THEN 'RepeatableRead'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot'
end as lvl, ##SPID
from sys.dm_exec_sessions
where session_id = ##SPID", conn);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("Isolation Level = " + reader.GetValue(0) + ", SPID = " + reader.GetValue(1));
}
}
if (tran != null) tran.Complete();
}
}
}
Output:
Isolation Level = ReadCommitted, SPID = 51
Isolation Level = Serializable, SPID = 51
Isolation Level = Serializable, SPID = 51 //leaked!

The connection pool calls sp_resetconnection before recycling a connection. Resetting the transaction isolation level is not in the list of things that sp_resetconnection does. That would explain why "serializable" leaks across pooled connections.
I guess you could start each query by making sure it's at the right isolation level:
if not exists (
select *
from sys.dm_exec_sessions
where session_id = ##SPID
and transaction_isolation_level = 2
)
set transaction isolation level read committed
Another option: connections with a different connection string do not share a connection pool. So if you use another connection string for the "serializable" queries, they won't share a pool with the "read committed" queries. An easy way to alter the connection string is to use a different login. You could also add a random option like Persist Security Info=False;.
Finally, you could make sure every "serializable" query resets the isolation level before it returns. If a "serializable" query fails to complete, you could clear the connection pool to force the tainted connection out of the pool:
SqlConnection.ClearPool(yourSqlConnection);
This is potentially expensive, but failing queries are rare, so you should not have to call ClearPool() often.

In SQL Server 2014 this seem to have been fixed. If using TDS protocol 7.3 or higher.
Running on SQL Server version 12.0.2000.8 the output is:
ReadCommitted
Serializable
ReadCommitted
Unfortunately this change is not mentioned in any documentation such as:
Behavior Changes to Database Engine Features in SQL Server 2014
Breaking Changes to Database Engine Features in SQL Server 2014
But the change has been documented on a Microsoft Forum.
Update 2017-03-08
Unfortunately this was later "unfixed" in SQL Server 2014 CU6 and SQL Server 2014 SP1 CU1 since it introduced a bug:
FIX: The transaction isolation level is reset incorrectly when the SQL Server connection is released in SQL Server 2014
"Assume that you use the TransactionScope class in SQL Server client-side source code, and you do not explicitly open the SQL Server connection in a transaction. When the SQL Server connection is released, the transaction isolation level is reset incorrectly."
Workaround
It appears that, since passing through a parameter makes the driver use sp_executesql, this forces a new scope, similar to a stored procedure. The scope is rolled back after the end of the batch.
Therefore, to avoid the leak, pass through a dummy parameter, as show below.
using (var conn = new SqlConnection(connString))
using (var comm = new SqlCommand(#"
SELECT transaction_isolation_level FROM sys.dm_exec_sessions where session_id = ##SPID
", conn))
{
conn.Open();
Console.WriteLine(comm.ExecuteScalar());
}
using (var conn = new SqlConnection(connString))
using (var comm = new SqlCommand(#"
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
SELECT transaction_isolation_level FROM sys.dm_exec_sessions where session_id = ##SPID
", conn))
{
comm.Parameters.Add("#dummy", SqlDbType.Int).Value = 0; // see with and without
conn.Open();
Console.WriteLine(comm.ExecuteScalar());
}
using (var conn = new SqlConnection(connString))
using (var comm = new SqlCommand(#"
SELECT transaction_isolation_level FROM sys.dm_exec_sessions where session_id = ##SPID
", conn))
{
conn.Open();
Console.WriteLine(comm.ExecuteScalar());
}

For those using EF in .NET, you can fix this for your whole application by setting a different appname per isolation level (as also stated by #Andomar):
//prevent isolationlevel leaks
//https://stackoverflow.com/questions/9851415/sql-server-isolation-level-leaks-across-pooled-connections
public static DataContext CreateContext()
{
string isolationlevel = Transaction.Current?.IsolationLevel.ToString();
string connectionString = ConfigurationManager.ConnectionStrings["yourconnection"].ConnectionString;
connectionString = Regex.Replace(connectionString, "APP=([^;]+)", "App=$1-" + isolationlevel, RegexOptions.IgnoreCase);
return new DataContext(connectionString);
}
Strange this is still an issue 8 years later ...

I just asked a question on this topic and added a piece of C# code, which can help around this problem (meaning: change isolation level only for one transaction).
Change isolation level in individual ADO.NET transactions only
It is basically a class to be wrapped in an 'using' block, which queries the original isolation level before and restores it later.
It does, however, require two additional round trips to the DB to check and restore the default isolation level, and I am not absolutely sure that it will never leak the altered isolation level, although I see very little danger of that.

Related

Coldfusion 9 DSN less Connection MSSQL 2008 Windows Authentication

Update:
Though the original question asked about windows authentication, any connection method would be fine. As long as it does not require creating a DSN.
I'm trying to connect my database with Windows Authentication. Here is the code.
Please advise. Thanks.
<cfscript>
classLoader = createObject("java", "java.lang.Class");
classLoader.forName("sun.jdbc.odbc.JdbcOdbcDriver");
dm = createObject("java","java.sql.DriverManager");
dburl = "jdbc:sqlserver://192.168.3.150:1433;databasename=RsDB;integratedSecurity=true";
con = dm.getConnection(dburl,"", "");
st = con.createStatement();
rs = st.ExecuteQuery("SELECT top 10 * FROM pa (nolock)");
q = createObject("java", "coldfusion.sql.QueryTable").init(rs);
</cfscript>
<cfoutput query="q">
email = #email#<br />
</cfoutput>
ERROR :
Error Occurred While Processing Request
This driver is not configured for integrated authentication.
(Ignoring your question for a moment, ... )
While it is possible to do it with a manual connection, is there a specific reason you cannot just use a standard DSN? That would be far more efficient and require a lot less code/cleanup. Not to mention that it avoids some of the common pitfalls of manual connections.
AFAIK, you can set the DSN to use windows authentication, by entering AuthenticationMethod=Type2 under "Show Advanced Settings > Connection String". Leave the "username" and "password" fields blank.
<cfquery name="qTest" datasource="YourDSN">
SELECT SYSTEM_USER AS LoginName, DB_NAME() AS DatabaseName
</cfquery>
<cfdump var="#qTest#" label="Show Connection Settings">
*NB: The DSN will use the CF Service user account, so adjust as needed
Update:
If you absolutely must use a manual connection, try using the built in driver instead of the JDBCODBC bridge. However, be sure to always close the database objects. Otherwise, open connections will start to pile up and bad things will happen.
Keep in mind, opening database connections is expensive. By not using a DSN, you lose out on the benefits of connection pooling. So overall this method will be less efficient than simply using a DSN.
<cfscript>
classLoader = createObject("java", "java.lang.Class");
classLoader.forName("macromedia.jdbc.MacromediaDriver");
dm = createObject("java","java.sql.DriverManager");
dburl = "jdbc:macromedia:sqlserver://127.0.0.1:1433;databasename=RsDB;AuthenticationMethod=Type2";
con = dm.getConnection(dburl);
st = con.createStatement();
rs = st.ExecuteQuery("SELECT getDate() AS TestValue");
q = createObject("java", "coldfusion.sql.QueryTable").init(rs);
// ALWAYS close all objects!
rs.close();
st.close();
con.close();
writeDump(q);
</cfscript>
You could also use the SQL Server JDBC Driver instead. See also Connecting with Integrated Authentication On Windows. However, that requires adding the driver jars and a DLL (sqljdbc_auth.dll) to the server, which it sounds like you cannot do.
Driver Class: com.microsoft.sqlserver.jdbc.SQLServerDriver
URL: jdbc:sqlserver://localhost:1433;integratedSecurity=true;
Found an example here that shows the username and password in the URL.
<cfset connection = driverManager
.getConnection("jdbc:mysql://localhost:3306/demodb?user=demouser&password=demopass")>
Your URL for SQL Server doesn't pass in any credentials, give that a try.

ServiceStack Ormlite transactions broken?

I am using ServiceStack.Ormlite for SQL Server and just updated from 3.9.71 to 4.0.33.0 and now transactions for direct commands are failing. I can get ORMlite transactions working or direct commands, but not both.
The complication is I am doing some very complicated DB commands and since Sql.In() for a large list of GUIDs is massively slow I have a workaround which uses db.CreateCommand() and then passes the GUID list in as a custom table type.
Thus I need a single transaction to span across ORMLite commands and direct db commands. The following code used to work and now it fails.
For instance the following code used to work. I now get errors saying that the CreateCommand() should use the transaction. When I directly try then I get the indicated cast exception:
using (var db = DB.Connection.OpenDbConnection())
{
using (var transaction = db.OpenTransaction())
{
// Some ORMLite code
db.Delete<SomeType>();
using (var command = db.CreateCommand())
{
// Direct DB command
command.CommandText = "Delete from SomeTable where ...";
command.Parameters.Add(GUIDList)
command.ExecuteNonQuery();
}
}
}
Clarification:
In the code OpenTransaction() will work for the OrmLite code, but fail on the CreateCommand code. BeginTransaction() will fail for the OrmLite code.
The actual error is at command.ExecuteNonQuery(): ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.
To use Transactions in OrmLite you should use the OpenTransaction() API, e.g:
using (var trans = db.OpenTransaction())
{
//...
}
I've added a couple new API's to be able to use an OrmLite transaction with a raw ADO.NET IDbCommand in this commit.
Use a managed OrmLite DB Command
Use a managed OrmLite command with OpenCommand() which will automatically prepopulate the DB command with the current transaction, e.g:
using (var trans = db.OpenTransaction())
using (var command = db.OpenCommand())
{
command.CommandText = "Delete from SomeTable where ...";
}
Manually assign underlying DB Transaction
When using the underlying ADO.NET IDbCommand you will need to also manually assign the Transaction to the command yourself, i.e:
using (var trans = db.OpenTransaction())
using (var command = db.CreateCommand())
{
command.Transaction = trans.ToDbTransaction();
command.CommandText = "Delete from SomeTable where ...";
}
The ToDbTransaction() extension method lets you access the underlying ADO.NET IDbTransaction which is required when using the underlying ADO.NET IDbCommand.
Both of these new API's are available from v4.0.34+ that's now available on MyGet.
Here is my suggestion that works.It is based on previous answers
IDbConnection conn = DB.Connection;
IDbCommand cmd = conn.CreateCommand();
using (IDbTransaction transaction = conn.OpenTransaction())
{
//ADO.NET code
cmd.Transaction = transaction.ToDbTransaction();
cmd.CommandText = "...Some sql text";
cmd.executeNonQuery();
// Some ORMLite code
conn.Delete<SomeType>();
}

Open connection while another connection is open

i need to call a function that do some query while another connection is opened and its doing a transaction.
Ok i get this is weird, here some code:
Main part:
Using connection As New SqlConnection(connectionString)
connection.Open()
Dim command As SqlCommand = connection.CreateCommand()
Dim transaction As SqlTransaction
transaction = connection.BeginTransaction("myTransaction")
command.Connection = connection
command.Transaction = transaction
command.CommandText = sSQL
Try
command.ExecuteNonQuery()
Dim functionResult As String = myFunction(param1, param2)
If functionResult <> "" Then
'error! i need to rollback the first query done here!
transaction.Rollback()
else
transaction.Commit()
End If
Catch ex As Exception
transaction.Rollback()
End Try
End If
End Using
myFunction do lot of stuff, and a lot of querys. Every query needs to reopen connection (without transaction this time) but everytime i try to execute the first query inside my function i got timeout error from database (after 30 seconds).
I know i can do this work "copy-pasting" all the myFunction code inside that already opened connection and using the already opened connection, but i use that function more than once and i don't want to mess up my code.
How can i solve this?
edit for more information:
that was an already reduced version of the code i'm using, but here a reduced version on what "myFunction" do:
Dim connectionString As String = "my connection string"
Dim queryString As String = "SELECT id FROM foo WHERE param1 = #myValue"
Dim ds As DataSet = New DataSet()
Try
Using connection As New SqlConnection(connectionString)
Dim command As New SqlCommand(queryString, connection)
connection.Open()
command.CommandText = queryString
command.Parameters.Add("#myValue", SqlDbType.Int).Value = 10
Dim adapter As New SqlDataAdapter()
adapter.SelectCommand = command
adapter.Fill(ds, "randomName")
If ds.Tables("randomName").Rows.Count < 0 Then
'error!
connection.Close()
Return "error"
End If
End Using
Catch ex As Exception
Return "Database error - " & ex.Message
End Try
The code execution (even in debug) freeze on the adapter.Fill(ds, "randomName") command for 30 seconds, after that i get a timout error
You can use as many connections as you want, just make sure they don't interfere with each other. SQL server is very diligent about preserving data integrity, so if one uncommitted transaction conflicts with another uncommitted transaction, you get a deadlock.
You may want to play with transaction isolation level, default is READ COMMITTED for SQL server, try to set it to READ UNCOMMITTED. Please read the docs to be aware of the consequences.
From the above link:
In SQL Server, you can also minimize locking contention while protecting transactions from dirty reads of uncommitted data modifications using either:
The READ COMMITTED isolation level with the READ_COMMITTED_SNAPSHOT database option set to ON.
The SNAPSHOT isolation level.

"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED" not taking? Or am I looking in the wrong way?

We have a problem with some database code that apparently executes with the wrong isolation level. In this particular part of the code, it is supposed to execute with "READ UNCOMMITTED" to minimize locks. Inconsistent data is OK at this point.
However, the code actually reads with READ COMMITTED, and we can't figure out why.
Here's what we did:
Open the connection
Execute on this connection "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"
Hit a breakpoint
Execute the SQL
On the breakpoint, we issue this command to the database:
select s.session_id, s.transaction_isolation_level, st.text from sys.dm_exec_sessions s
inner join sys.sysprocesses sp on (sp.spid = s.session_id)
CROSS APPLY sys.dm_exec_sql_text(sp.sql_handle) st
This SQL reports 4 pooled connections right now, one of which is our connection that we can step beyond the breakpoint to execute our SQL with, that has this state:
53 2 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
ie. session 53 has isolation level 2 (READ COMMITTED), and the last SQL that was executed on this session was that "SET TRANSACTION ..." command.
How can this be?
We verified with SQL Profiler that this connection did not live before our .NET code opened it, so it was not reused from the connection pool.
Yet, with a fresh connection, and the only and first SQL executed on it explicitly told it to use READ UNCOMMITTED, how can the connection still be READ COMMITTED?
What should we look at here?
The connection string (with bits redacted) is like this:
SERVER=hostname;DATABASE=dbname;Integrated Security=false;USER ID=sa;PASSWORD=****;Application Name=appname;Type System Version=SQL Server 2000;Workstation ID=hostname;
The connections are normal SqlConnection connections, opened in the normal way.
Unfortunately we're unable to reproduce the problem if we write normal code opening a SqlConnection, so there has to be something with the application state, but since SqlProfiler and Sql Server both tells us that yes, the SQL was executed, but no, I don't care.
What can impact this?
The exact same code also opens other connections, that is, the code is executed many times and opens many connections, so more than one connection ends up in the pool, yet only the very first connection ends up having this problem.
This is SQL Server 2008 R2 and we have also reproduced this problem on 2012.
Edit
OK, some more information.
First, we are enabling pooling, or rather, we're not explicitly disabling it, nor are we twiddling the connection string to make "N" pools.
However, this connection is the first being opened with this particular connection string, thus it is not retrieved from the pool. Also see my note below about it being permanently "sick".
This connection is being set up like this:
var conn = new SqlConnection(...);
conn.StateChance += connection_StateChange;
private void connection_StateChange(Object sender, StateChangeEventArgs e)
{
if (e.CurrentState == ConnectionState.Open)
{
using (IDbCommand cmd = ((SqlConnection)sender).CreateCommand())
{
cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
cmd.ExecuteNonQuery();
}
We're not executing any other SQL before this.
Note that this code is used many times during the lifetime of the application, it is only the very first connection it opens that ends up being wrong.
This connection also becomes permanently sick. Since every time we open the connection (even though we might get it out of the connection pool), the above state change event executes, attempting to set the isolation level again. This also fails, but just for this single connection.
Additionally we've found one thing that impacts this since I posted this question.
By changing the connection string, that I posted above:
...;Type System Version=SQL Server 2000;...
to this:
...;Type System Version=SQL Server 2008;MultipleActiveResultSets=true;...
then this problem goes away, at the breakpoint listed earlier, the connection now has "READ UNCOMMITTED" state.
This was a red herring, the connection was no longer being reported in our overview until we had actually executed code there.
We're continuing our debugging.
The problem here is that the SqlConnection.BeginTransaction that does not take parameters defaults to read committed. I guess we didn't understand what the "default isolation level" text is on that page.
That page has this text:
If you do not specify an isolation level, the default isolation level is used. To specify an isolation level with the BeginTransaction method, use the overload that takes the iso parameter (BeginTransaction). The isolation level set for a transaction persists after the transaction is completed and until the connection is closed or disposed. Setting the isolation level to Snapshot in a database where the snapshot isolation level is not enabled does not throw an exception. The transaction will complete using the default isolation level.
(my highlight)
Here's a LINQPad script that demonstrates:
void Main()
{
using (var conn = new SqlConnection("Data Source=.;Initial Catalog=master;Integrated security=true"))
{
conn.Open();
Dump(conn, "after open");
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
cmd.ExecuteNonQuery();
}
Dump(conn, "after set iso");
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "BEGIN TRANSACTION";
cmd.ExecuteNonQuery();
}
Dump(conn, "after sql-based begin transaction");
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "COMMIT";
cmd.ExecuteNonQuery();
}
Dump(conn, "after sql-based commit");
var trans = conn.BeginTransaction();
Dump(conn, "after .net begin transaction", trans);
trans.Commit();
Dump(conn, "after .net commit");
}
}
public static void Dump(SqlConnection connection, string title, SqlTransaction transaction = null)
{
using (var cmd = new SqlCommand())
{
cmd.Connection = connection;
if (transaction != null)
cmd.Transaction = transaction;
cmd.CommandText = "SELECT transaction_isolation_level FROM sys.dm_exec_sessions WHERE session_id = ##SPID";
Debug.WriteLine(title + "=" + Convert.ToInt32(cmd.ExecuteScalar()));
}
}
It will output:
after open=2
after set iso=1
after sql-based begin transaction=1
after sql-based commit=1
after .net begin transaction=2
after .net commit=2
Here you can see that manually beginning and committing a transaction through SQL would not change the isolation level, but beginning a transaction in .NET without explicitly stating the isolation level still changes it to read committed.
Since everywhere we read, starting a transaction without explicitly stating the isolation level said that it inherited the isolation level of the session, I guess we didn't understand that .NET would not do the same.

TSQL - Executing CLR Permission

I got a sql procedure from a CLR (.net Assembly) that when executed returns an error
Msg 6522, Level 16, State 1, Procedure sp_HelloWorld, Line 0
A .NET Framework error occurred during execution of user defined routine or aggregate 'sp_HelloWorld':
System.Security.SecurityException: Request for the permission of type 'System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
System.Security.SecurityException:
at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
at System.Security.PermissionSet.Demand()
at System.Data.Common.DbConnectionOptions.DemandPermission()
at System.Data.SqlClient.SqlConnection.PermissionDemand()
at System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection)
at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Open()
at HelloWorld.SQLCLR.HelloWorld()
This is my SQL script
go
drop procedure HelloWorld
drop assembly HelloWorld
GO
create assembly HelloWorld from 'F:\HelloWorld.dll'
with permission_set = safe
Go
create procedure sp_HelloWorld
as external name HelloWorld.[HelloWorld.SQLCLR].HelloWorld
go
exec sp_HelloWorld
and this is my Class (Assembly)
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Security.Permissions;
using System.Data;
namespace HelloWorld
{
public class SQLCLR
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void HelloWorld()
{
string connectString1 = #"Data Source=localhost;Initial Catalog=ItemData;Integrated Security=True";
SqlClientPermission permission = new SqlClientPermission(PermissionState.None);
permission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);
permission.PermitOnly();
SqlConnection sqlcon = new SqlConnection(connectString1);
sqlcon.Open();
SqlCommand sqlcmd = new SqlCommand("SELECT Top 1 * FROM ItemData.dbo.Item", sqlcon);
SqlDataReader reader = sqlcmd.ExecuteReader();
SqlContext.Pipe.Send(reader);
sqlcon.Close();
}
}
}
The problem is simply that you are attempting to access an external resource in an Assembly that is marked as SAFE. Accessing external resources requires setting the Assembly to at least EXTERNAL_ACCESS (and in some cases UNSAFE). However, looking at your code, you are simply trying to connect to the local instance, and in that case there is a far easier (and faster) means of doing this: using "Context Connection = true;" as the ConnectionString.
The Context Connection is a direct connection to the current process / session, and it is sometimes referred to as the in-process connection. The benefits of using the Context Connection are:
can be done in Assemblies marked as SAFE
access to local temporary objects (temp tables and temp procedures, both with names starting with a single # instead of double ##)
access to SET CONTEXT_INFO and CONTEXT_INFO()
no connection startup overhead
Also:
whether you use the in-process, Context Connection or a regular / external connection, you do not need to formally request permission using SqlClientPermission
you should always clean up external resources by calling their Dispose() method. Not all objects have this, but SqlConnection, SqlCommand, and SqlDataReader certainly do. It is typical for people to wrap disposable objects in a using() block as it is a compiler macro that expands to a try / finally structure that calls the Dispose() method in the finally to ensure that it is called, even if an error occurs.
The Dispose() method of many / most disposable objects automatically handles the call to Close() so you usually do not need to call Close() explicitly.
Your code should look as follows:
[Microsoft.SqlServer.Server.SqlProcedure]
public static void HelloWorld()
{
using (SqlConnection sqlcon = new SqlConnection("Context Connection = true;")
{
using (SqlCommand sqlcmd = new SqlCommand("SELECT Top 1 * FROM ItemData.dbo.Item",
sqlcon))
{
sqlcon.Open();
using (SqlDataReader reader = sqlcmd.ExecuteReader())
{
SqlContext.Pipe.Send(reader);
}
}
}
}
I just wanted to add my two sense to this. I'm doing something very similiar and I'm getting the same error. Here is what I found, however b/c I don't have this level of access to the DB I can't test it.
Easiest( although not MSDN recommended just to jet a CLR proc to run)
is to set the permission level to External_Access...
SQL Server Host Policy Level Permission Sets The set of code access
security permissions granted to assemblies by the SQL Server host
policy level is determined by the permission set specified when
creating the assembly. There are three permission sets: SAFE,
EXTERNAL_ACCESS and UNSAFE.
The permision level is set on the properties pages of the CLR project
, database tab - set Permission Level-External, set Aassembly
Owner-dbo, and run tsql 'ALTER DATABASE DataBaseName SET TRUSTWORTHY
ON' This will get the job DONE! - and the SmtpClient wiill work ok...
Then do it right and Sign the Assenbly with a Strong name Key file...
Full Post Here...
Have you set your DB set to Trusrtworth ON and enabled clr?
Try this
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
ALTER DATABASE [YourDatabase] SET TRUSTWORTHY ON
GO
I have a guide here on how to use CLR Stored Procedures that might help.

Resources