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.");
    }
}