1

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()
    {
      ...
    }

    ...
}

SqlFunctionそして、ac# 関数を使用して SQL スクリプトを生成し、この dll に含まれる属性を持つすべての関数を自動的に作成または更新したいと考えています。この 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

最初の部分は非常に単純ですが、2 番目の部分については、クラス Type、MethodInfo、および ParameterInfo のリフレクション メソッドを使用しておそらく可能です。誰かがすでにこれを行っていますか?

4

3 に答える 3

1

私はそれをテストしてデバッグしました:

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.");
    }
}
于 2012-05-21T16:49:28.513 に答える
1

次の手順を実行することをお勧めします: Visual Studio から、[ファイル] > [新しいプロジェクト] に移動し、テンプレートから SQL SERVER を選択します。

ここに画像の説明を入力

プロジェクトからプロパティを取得する

ここに画像の説明を入力

プロジェクト設定では「ターゲットプラットフォーム」を決定でき、SQLCLRではプログラミング言語を選択できることに言及してください

ここに画像の説明を入力

プロジェクトに新しいアイテム (SQL CLR C# ユーザー定義関数) を追加する

ここに画像の説明を入力

関数を書く

ここに画像の説明を入力

コードを書いた後、プロジェクトを右クリックし、最初にプロジェクトをビルドしてからSQLサーバーに公開します

ここに画像の説明を入力

次のウィンドウで、SQL サーバーとデータベースへの接続をセットアップします。

ここに画像の説明を入力

終了後、適切なアセンブリと関数がデータベースに作成されていることがわかります

ここに画像の説明を入力

于 2016-11-29T19:03:46.390 に答える
0

アプローチは、dll(それがあなたのものである場合)または作成して参照できるCLR dllでコンパイルされた既知の関数でリフレクションを使用することです。そこでは、.NET の本格的な REGEX サポートを使用して、関数の作成に必要な一致した名前を SQL SERVER に返すことができます。

これはちょっとした回避策ですが、.NET リフレクションを使用するネイティブの TSQL メソッドについては知りません。

-編集-

.NET から TSQL スクリプトを作成する必要がある場合は、次のようなものを使用できます (デバッグされていませんが、ほとんどのものはそこにあるはずです)。

public string encode(Type containerClass, string SSRVassemblyName)
    {
        string proTSQLcommand="";
        MethodInfo[] methodName = containerClass.GetMethods();
        foreach (MethodInfo method in methodName)
        {
            proTSQLcommand += "CREATE FUNCTION ";
            if (method.ReflectedType.IsPublic)
            {
                bool hasParams = false;
                proTSQLcommand += method.Name +"(";
                ParameterInfo[] curmethodParams = method.GetParameters();
                if (curmethodParams.Length > 0)
                    for (int i = 0; i < curmethodParams.Length; i++)
                    {
                        proTSQLcommand +=(hasParams?",":"")+ String.Format("@{0} {1}",curmethodParams[i].Name,CLRtypeTranscoder(curmethodParams[i].ParameterType.Name)) ;
                        hasParams = true;
                    }
                proTSQLcommand += (hasParams ? "," : "") + String.Format("@RetVal {0} OUT", CLRtypeTranscoder(method.ReturnType.Name));

            }
            //note that I have moved the result of the function to an output parameter 
            proTSQLcommand += "RETURNS INT ";
            //watch this! the assembly name you have to use is the one of SSRV, to reference the one of the CLR library you have to use square brackets as follows
            proTSQLcommand += String.Format("AS EXTERNAL NAME {0}.[{1}.{2}].{3}; GO ",SSRVassemblyName, GetAssemblyName(containerClass.Name),containerClass.Name,method.Name);
        }
        return proTSQLcommand;
    }
    public string CLRtypeTranscoder(string CLRtype)
    { //required as the mapping of CLR type depends on the edition of SQL SERVER you are using
        switch (CLRtype.ToUpper())
        {
            case "STRING":
                return "VARCHAR(MAX)";
            case "INT":
                return "INT";
        }
        throw new   ArgumentOutOfRangeException();
    }
    public static String GetAssemblyName(String typeName)
    {
        foreach (Assembly currentassemblAssy in AppDomain.CurrentDomain.GetAssemblies())
        {
            Type t = currentassemblAssy.GetType(typeName, false, true);
            if (t != null) { return currentassemblAssy.FullName; }
        }

        return "not found";
    }
于 2012-05-16T10:16:06.493 に答える