C# 从程序集dll自动创建sql server函数
我在名为C# 从程序集dll自动创建sql server函数,c#,sql-server,sql-server-2008,tsql,sql-server-2005,C#,Sql Server,Sql Server 2008,Tsql,Sql Server 2005,我在名为UserFunctions.dll的程序集dll中有几个函数,例如: public static partial class VariousFunctions { [SqlFunction(IsDeterministic = true, IsPrecise = true)] public static SqlBoolean RegexMatch(SqlString expression, SqlString pattern) { ... }
UserFunctions.dll的程序集dll中有几个函数,例如:
public static partial class VariousFunctions
{
[SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlBoolean RegexMatch(SqlString expression, SqlString pattern)
{
...
}
[SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlInt32 WorkingDays(SqlDateTime startDateTime, SqlDateTime endDateTime)
{
...
}
[SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlString getVersion()
{
...
}
...
}
我想用一个c#函数生成sql脚本,以自动创建或更新此dll中包含属性SqlFunction
的所有函数。
此sql脚本应如下所示:
-- Delete all functions from assembly 'UserFunctions'
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'DROP FUNCTION ' + STUFF(
(
SELECT
', ' + QUOTENAME(assembly_method)
FROM
sys.assembly_modules
WHERE
assembly_id IN (SELECT assembly_id FROM sys.assemblies WHERE name = 'UserFunctions')
FOR XML PATH('')
), 1, 1, '')
-- SELECT @sql
IF @sql IS NOT NULL EXEC sp_executesql @sql
-- Create all functions from assembly 'UserFunctions'
CREATE FUNCTION RegexMatch(@expression NVARCHAR(MAX), @pattern NVARCHAR(MAX)) RETURNS BIT
AS EXTERNAL NAME UserFunctions.VariousFunctions.RegexMatch;
GO
CREATE FUNCTION WorkingDays(@startDateTime DATETIME, @endDateTime DATETIME) RETURNS INTEGER
AS EXTERNAL NAME UserFunctions.VariousFunctions.WorkingDays;
GO
CREATE FUNCTION getVersion() RETURNS VARCHAR(MAX)
AS EXTERNAL NAME UserFunctions.VariousFunctions.getVersion;
GO
第一部分非常简单,但对于第二部分,这可能是使用类Type、MethodInfo和ParameterInfo的反射方法实现的。
有人已经这样做了?一种方法可以是在dll(如果是您的dll)中编译的已知函数中使用反射,或者在您可以创建和引用的CLR dll中使用反射。在那里,您可以使用.NET成熟的正则表达式支持将创建函数所需的匹配名称返回给SQL SERVER
这是一种变通方法,但我不知道使用.NET反射的本机TSQL方法
-编辑-
如果您需要从.NET创建一个TSQL脚本,您可以使用类似的东西(未安装bug,但大多数东西都应该在那里)
public string encode(输入container类,string-SSRVassemblyName)
{
字符串proTSQLcommand=“”;
MethodInfo[]methodName=containerClass.GetMethods();
foreach(methodName中的MethodInfo方法)
{
proTSQLcommand+=“创建函数”;
if(方法.ReflectedType.IsPublic)
{
bool hasParams=false;
proTSQLcommand+=method.Name+“(”;
ParameterInfo[]curmethodParams=method.GetParameters();
如果(curmethodParams.Length>0)
对于(int i=0;i
我已经对它进行了测试和调试:
static void Main(string[] args)
{
Assembly clrAssembly = Assembly.LoadFrom(@"Path\to\your\assembly.dll");
string sql = CreateFunctionsFromAssembly(clrAssembly, permissionSetType.UNSAFE);
File.WriteAllText(sqlFile, sql);
}
/// <summary>
/// permissions available for an assembly dll in sql server
/// </summary>
public enum permissionSetType { SAFE, EXTERNAL_ACCESS, UNSAFE };
/// <summary>
/// generate sql from an assembly dll with all functions with attribute SqlFunction
/// </summary>
/// <param name="clrAssembly">assembly object</param>
/// <param name="permissionSet">sql server permission set</param>
/// <returns>sql script</returns>
public static string CreateFunctionsFromAssembly(Assembly clrAssembly, permissionSetType permissionSet)
{
const string sqlTemplate = @"
-- Delete all functions from assembly '{0}'
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'DROP FUNCTION ' + STUFF(
(
SELECT
', ' + assembly_method
FROM
sys.assembly_modules
WHERE
assembly_id IN (SELECT assembly_id FROM sys.assemblies WHERE name = '{0}')
FOR XML PATH('')
), 1, 1, '')
IF @sql IS NOT NULL EXEC sp_executesql @sql
GO
-- Delete existing assembly '{0}' if necessary
IF EXISTS(SELECT 1 FROM sys.assemblies WHERE name = '{0}')
DROP ASSEMBLY {0};
GO
{1}
GO
-- Create all functions from assembly '{0}'
";
string assemblyName = clrAssembly.GetName().Name;
StringBuilder sql = new StringBuilder();
sql.AppendFormat(sqlTemplate, assemblyName, CreateSqlFromAssemblyDll(clrAssembly, permissionSet));
foreach (Type classInfo in clrAssembly.GetTypes())
{
foreach (MethodInfo methodInfo in classInfo.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
if (Attribute.IsDefined(methodInfo, typeof(SqlFunctionAttribute)))
{
StringBuilder methodParameters = new StringBuilder();
bool firstParameter = true;
foreach (ParameterInfo paramInfo in methodInfo.GetParameters())
{
if (firstParameter)
firstParameter = false;
else
methodParameters.Append(", ");
methodParameters.AppendFormat(@"@{0} {1}", paramInfo.Name, ConvertClrTypeToSql(paramInfo.ParameterType));
}
string returnType = ConvertClrTypeToSql(methodInfo.ReturnParameter.ParameterType);
string methodName = methodInfo.Name;
string className = (classInfo.Namespace == null ? "" : classInfo.Namespace + ".") + classInfo.Name;
string externalName = string.Format(@"{0}.[{1}].{2}", assemblyName, className, methodName);
sql.AppendFormat(@"CREATE FUNCTION {0}({1}) RETURNS {2} AS EXTERNAL NAME {3};"
, methodName, methodParameters, returnType, externalName)
.Append("\nGO\n");
}
}
}
return sql.ToString();
}
/// <summary>
/// Generate sql script to create assembly
/// </summary>
/// <param name="clrAssembly"></param>
/// <param name="permissionSet">sql server permission set</param>
/// <returns></returns>
public static string CreateSqlFromAssemblyDll(Assembly clrAssembly, permissionSetType permissionSet)
{
const string sqlTemplate = @"
-- Create assembly '{0}' from dll
CREATE ASSEMBLY [{0}]
AUTHORIZATION [dbo]
FROM 0x{2}
WITH PERMISSION_SET = {1};
";
StringBuilder bytes = new StringBuilder();
using (FileStream dll = File.OpenRead(clrAssembly.Location))
{
int @byte;
while ((@byte = dll.ReadByte()) >= 0)
bytes.AppendFormat("{0:X2}", @byte);
}
string sql = String.Format(sqlTemplate, clrAssembly.GetName().Name, permissionSet, bytes);
return sql;
}
/// <summary>
/// Convert clr type to sql type
/// </summary>
/// <param name="clrType">clr type</param>
/// <returns>sql type</returns>
private static string ConvertClrTypeToSql(Type clrType)
{
switch (clrType.Name)
{
case "SqlString":
return "NVARCHAR(MAX)";
case "SqlDateTime":
return "DATETIME";
case "SqlInt16":
return "SMALLINT";
case "SqlInt32":
return "INTEGER";
case "SqlInt64":
return "BIGINT";
case "SqlBoolean":
return "BIT";
case "SqlMoney":
return "MONEY";
case "SqlSingle":
return "REAL";
case "SqlDouble":
return "DOUBLE";
case "SqlDecimal":
return "DECIMAL(18,0)";
case "SqlBinary":
return "VARBINARY(MAX)";
default:
throw new ArgumentOutOfRangeException(clrType.Name + " is not a valid sql type.");
}
}
static void Main(字符串[]args)
{
Assembly CLRSSEMBLY=Assembly.LoadFrom(@“Path\to\your\Assembly.dll”);
字符串sql=CreateFunctionsFromAssembly(CLRSSEMBLY,permissionSetType.UNSAFE);
writealText(sqlFile,sql);
}
///
///sql server中程序集dll的可用权限
///
public enum permissionSetType{SAFE,EXTERNAL_ACCESS,UNSAFE};
///
///从具有属性SqlFunction的所有函数的程序集dll生成sql
///
///汇编对象
///sql server权限集
///sql脚本
公共静态字符串CreateFunctionsFromAssembly(程序集CLRSSEMBLY,permissionSetType permissionSet)
{
常量字符串sqlTemplate=@”
--从程序集“{0}”中删除所有函数
声明@sql NVARCHAR(最大值)
SET@sql='DROP FUNCTION'+STUFF(
(
挑选
“,”+组装法
从…起
sys.assembly\u模块
哪里
中的程序集\u id(从sys.assemblies中选择程序集\u id,其中name='{0}')
对于XML路径(“”)
), 1, 1, '')
如果@sql不是NULL,则EXEC sp_executesql@sql
去
--如有必要,删除现有程序集“{0}”
如果存在(从sys.assemblies中选择1,其中name='{0}')
删除程序集{0};
去
{1}
去
--从程序集“{0}”创建所有函数
";
字符串assemblyName=CLRSSEMBLY.GetName().Name;
StringBuilder sql=新的StringBuilder();
AppendFormat(sqlTemplate,assemblyName,CreateSqlFromAssemblyDll(CLRSSEMBLY,permissionSet));
foreach(在clrAssembly.GetTypes()中键入classInfo)
{
foreach(classInfo.GetMethods中的MethodInfo MethodInfo(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
if(Attribute.IsDefined(methodInfo,typeof(SqlFunctionAttribute)))
{
StringBuilder methodParameters=新的StringBuilder();
bool firstParameter=true;
foreach(methodInfo.GetParameters()中的ParameterInfo paramInfo)
{
if(第一个参数)
firstParameter=false;
其他的
methodParameters.Append(“,”);
AppendFormat(@“{0}{1}”、paramInfo.Name、ConvertClrTypeToSql(paramInfo.ParameterType));
}
字符串returnType=ConvertClrTypeToSql(methodInfo.ReturnParameter.ParameterType);
字符串methodName=meth
static void Main(string[] args)
{
Assembly clrAssembly = Assembly.LoadFrom(@"Path\to\your\assembly.dll");
string sql = CreateFunctionsFromAssembly(clrAssembly, permissionSetType.UNSAFE);
File.WriteAllText(sqlFile, sql);
}
/// <summary>
/// permissions available for an assembly dll in sql server
/// </summary>
public enum permissionSetType { SAFE, EXTERNAL_ACCESS, UNSAFE };
/// <summary>
/// generate sql from an assembly dll with all functions with attribute SqlFunction
/// </summary>
/// <param name="clrAssembly">assembly object</param>
/// <param name="permissionSet">sql server permission set</param>
/// <returns>sql script</returns>
public static string CreateFunctionsFromAssembly(Assembly clrAssembly, permissionSetType permissionSet)
{
const string sqlTemplate = @"
-- Delete all functions from assembly '{0}'
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'DROP FUNCTION ' + STUFF(
(
SELECT
', ' + assembly_method
FROM
sys.assembly_modules
WHERE
assembly_id IN (SELECT assembly_id FROM sys.assemblies WHERE name = '{0}')
FOR XML PATH('')
), 1, 1, '')
IF @sql IS NOT NULL EXEC sp_executesql @sql
GO
-- Delete existing assembly '{0}' if necessary
IF EXISTS(SELECT 1 FROM sys.assemblies WHERE name = '{0}')
DROP ASSEMBLY {0};
GO
{1}
GO
-- Create all functions from assembly '{0}'
";
string assemblyName = clrAssembly.GetName().Name;
StringBuilder sql = new StringBuilder();
sql.AppendFormat(sqlTemplate, assemblyName, CreateSqlFromAssemblyDll(clrAssembly, permissionSet));
foreach (Type classInfo in clrAssembly.GetTypes())
{
foreach (MethodInfo methodInfo in classInfo.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
if (Attribute.IsDefined(methodInfo, typeof(SqlFunctionAttribute)))
{
StringBuilder methodParameters = new StringBuilder();
bool firstParameter = true;
foreach (ParameterInfo paramInfo in methodInfo.GetParameters())
{
if (firstParameter)
firstParameter = false;
else
methodParameters.Append(", ");
methodParameters.AppendFormat(@"@{0} {1}", paramInfo.Name, ConvertClrTypeToSql(paramInfo.ParameterType));
}
string returnType = ConvertClrTypeToSql(methodInfo.ReturnParameter.ParameterType);
string methodName = methodInfo.Name;
string className = (classInfo.Namespace == null ? "" : classInfo.Namespace + ".") + classInfo.Name;
string externalName = string.Format(@"{0}.[{1}].{2}", assemblyName, className, methodName);
sql.AppendFormat(@"CREATE FUNCTION {0}({1}) RETURNS {2} AS EXTERNAL NAME {3};"
, methodName, methodParameters, returnType, externalName)
.Append("\nGO\n");
}
}
}
return sql.ToString();
}
/// <summary>
/// Generate sql script to create assembly
/// </summary>
/// <param name="clrAssembly"></param>
/// <param name="permissionSet">sql server permission set</param>
/// <returns></returns>
public static string CreateSqlFromAssemblyDll(Assembly clrAssembly, permissionSetType permissionSet)
{
const string sqlTemplate = @"
-- Create assembly '{0}' from dll
CREATE ASSEMBLY [{0}]
AUTHORIZATION [dbo]
FROM 0x{2}
WITH PERMISSION_SET = {1};
";
StringBuilder bytes = new StringBuilder();
using (FileStream dll = File.OpenRead(clrAssembly.Location))
{
int @byte;
while ((@byte = dll.ReadByte()) >= 0)
bytes.AppendFormat("{0:X2}", @byte);
}
string sql = String.Format(sqlTemplate, clrAssembly.GetName().Name, permissionSet, bytes);
return sql;
}
/// <summary>
/// Convert clr type to sql type
/// </summary>
/// <param name="clrType">clr type</param>
/// <returns>sql type</returns>
private static string ConvertClrTypeToSql(Type clrType)
{
switch (clrType.Name)
{
case "SqlString":
return "NVARCHAR(MAX)";
case "SqlDateTime":
return "DATETIME";
case "SqlInt16":
return "SMALLINT";
case "SqlInt32":
return "INTEGER";
case "SqlInt64":
return "BIGINT";
case "SqlBoolean":
return "BIT";
case "SqlMoney":
return "MONEY";
case "SqlSingle":
return "REAL";
case "SqlDouble":
return "DOUBLE";
case "SqlDecimal":
return "DECIMAL(18,0)";
case "SqlBinary":
return "VARBINARY(MAX)";
default:
throw new ArgumentOutOfRangeException(clrType.Name + " is not a valid sql type.");
}
}