2012-05-16 52 views
1

我有一個名爲UserFunctions.dll一個集DLL多種功能,例如創建SQL Server的功能自動包含在此dll中的屬性SqlFunction的所有功能。 該SQL腳本應該是這樣的:從集DLL自動

-- 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 

第一部分是很簡單,但對於第二部分這可能是可能使用的類類型,一個MethodInfo和的ParameterInfo的思考方法。 有人已經這樣做了?

回答

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

一種方法可以使用反射在一個已知的函數中編譯的dll(萬一它是你的)或在CLR DLL,你可以創建和引用。在那裏你可以使用完全成熟的.NET的REGEX支持來返回SQL SERVER創建函數所需的匹配名稱。

這是一個有點一種解決辦法,但我不知道本地TSQL方法的使用.NET反射

CNC中

,如果你需要創建從TSQL腳本。NET可以使用這樣的事情(undebugged,但大部分東西應該有)

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"; 
    } 
+0

也許我還沒有這麼好解釋的問題被創建 ,但我想生成使用AC前面所述的SQL #函數,而不是TSQL方法。 – psadac

0

我建議你做到以下幾點: 從Visual Studio ,轉到文件>新建項目...然後從模板中選擇SQL Server從您的凸出

enter image description here

起飛性能ECT

enter image description here

提到,在項目設置,你可以決定你的 「目標平臺」 和SQLCLR,你可以選擇你的編程語言

enter image description here

添加一個新的項目(SQL CLR C#用戶定義功能)到您的項目

enter image description here

寫功能

enter image description here

編寫代碼後,右鍵點擊您的項目,先建項目,然後將其發佈到你的SQL Server

enter image description here

下一個窗口,設置您連接到您的SQL Server和數據庫

enter image description here

完成後,你可以看到正確的組裝和功能在你的數據庫

enter image description here