CLR内的动态sql连接字符串
本文关键字:连接 字符串 sql 动态 CLR | 更新日期: 2023-09-27 18:02:00
尝试创建一个CLR,将SQL表写入以管道分隔的文件。
还没有测试输出,所以不确定它是否正在工作,因为在尝试创建连接字符串时遇到了一个障碍。我希望它是动态的,这样它就可以根据正在执行的sql server计算出连接字符串。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Security;
using System.Security.Principal;
using Microsoft.SqlServer.Server;
public partial class CLR_uspExportToFiles
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void uspExportToFiles(string TableOrQuery, string Delimiter, int Debug, string FilePath, string Filename)
{
var output = FilePath + Filename;
using (System.IO.StreamWriter file = new System.IO.StreamWriter(output))
{
const string DATASOURCE = "Data Source=";
const string INITIALCATALOG = ";Initial Catalog=";
const string INTEGRATEDSECURITY = ";Integrated Security=True;Enlist=false;";
string ConnString;
string InstanceName;
string DbName;
//Establish a connection in the current context to dynamically get the current
//Server and Database Name.
using (SqlConnection sysconn = new SqlConnection("context connection=true"))
{
SqlCommand GetSQLSystemProperties = new SqlCommand();
GetSQLSystemProperties.Connection = sysconn;
sysconn.Open();
//Get the current SQL Server instance name
GetSQLSystemProperties.CommandText = "SELECT @@Servername";
InstanceName = (string)GetSQLSystemProperties.ExecuteScalar();
//Get the current Database Name
GetSQLSystemProperties.CommandText = "SELECT DB_NAME()";
DbName = (string)GetSQLSystemProperties.ExecuteScalar();
sysconn.Close();
//Dynamically construct the connection string to establish a connection outside
//of the current context, so that any error written to the error table won't be
//rolled back.
ConnString = DATASOURCE + InstanceName + INITIALCATALOG + DbName + INTEGRATEDSECURITY;
using (SqlConnection conn = new SqlConnection(ConnString))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM @TableOrQuery", conn))
{
//cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@TableOrQuery", TableOrQuery);
//cmd.Parameters.AddWithValue("@Del", Delimiter);
//cmd.Parameters.AddWithValue("@Debug", Debug);
//cmd.Parameters.AddWithValue("@FilePath", FilePath);
//cmd.Parameters.AddWithValue("@Filename", Filename);
using (SqlDataAdapter sda = new SqlDataAdapter())
{
sda.SelectCommand = cmd;
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
//Build the Text file data.
string txt = string.Empty;
foreach (DataColumn column in dt.Columns)
{
//Add the Header row for Text file.
txt += column.ColumnName + "|";
}
//Add new line.
txt += "'r'n";
file.WriteLine(txt);
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
//Add the Data rows.
txt += row[column.ColumnName].ToString() + "|";
}
//Add new line.
txt += "'r'n";
file.WriteLine(txt);
}
}
}
};
}
}
}
}
}
通过
获得错误Msg 6522, Level 16, State 1, Procedure CLR_uspExportToFiles, Line 0
A .NET Framework error occurred during execution of user-defined routine or aggregate "CLR_uspExportToFiles":
System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
System.Data.SqlClient.SqlException:
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover, SqlAuthenticationMethod authType)
at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePa...
和不确定如何修复
几点注意事项:
- 你应该使用
Sql***
类型而不是原生的。net类型。意思是,用SqlString
(或者有时用SqlChars
)代替string
,用SqlInt32
代替int
,等等。完整列表请参见映射CLR参数数据。 我认为你打开文件和数据库连接的顺序不对。查询可能不返回任何内容:并非所有查询都返回结果集和一些错误。我不会打开文件,直到命令成功完成,并表明返回的东西。 - 我看不出有任何理由打开到服务器的外部连接。我看到了关于不想要某些东西回滚的评论,但这没有多大意义。
- 您不需要两个查询(
SELECT @@SERVERNAME
和SELECT DB_NAME()
),因为您不需要第二个外部连接。 - 即使您确实需要(或只是想要)这两条信息,您只需要查询
@@SERVERNAME
。您可以通过Database
属性从SqlConnection
获得数据库名称。是的,当使用Context Connection
时,它将被设置为当前DB(当前用于该查询)。 - 不要使用
Parameters.AddWithValue
。单独创建参数,并指定SqlDbType
和max长度。 - 不要使用
DataTable
,除非您绝对肯定传入的任何表永远不会那么大(就数据/已用空间而言,而不是索引)。Fill
方法将整个结果集吸到内存中(即DataTable
)。这相当危险。您应该使用SqlDataReader
,并将结果集的每一行写入从SqlDataReader
读取的文件。 - 不需要
txt
变量。您可以在读取时通过write方法写出每个字段。 - 即使你确实想把这些片段连接成一个字符串,每行写一次,你不需要附加换行符(即
'r'n
),因为这是由WriteLine
方法处理的(同样适用于Console.Write
vsConsole.WriteLine
)。通过同时使用WriteLine
和附加'r'n
,每行数据将由空行分隔。
有关使用SQLCLR的更多信息,请参阅我在SQL Sever Central上撰写的系列文章:通往SQLCLR的阶梯(需要免费注册)。
另外,我已经编写了一个SQLCLR存储过程来完成这个任务(将查询的结果集转储到文件中),作为SQL#库的一部分。请注意,虽然有免费版本的SQL#, DB_BulkExport存储过程只在完整版本中可用。