1. 程式人生 > >PCB MS SQL 標量函式與表值函式(CLR) 實現檔案與目錄操作 PCB 工程系統 模擬windows域帳號登入

PCB MS SQL 標量函式與表值函式(CLR) 實現檔案與目錄操作 PCB 工程系統 模擬windows域帳號登入

 一.C#寫SQL SERVER(CLR)實現檔案操作

 標量函式: 檔案移動 ,複製,檢測檔案存在,寫入新檔案文字,讀取文字,建立目錄,刪除目錄,檢測目錄是否存在

        /// <summary>  
        ///  將現有檔案複製到新檔案。允許覆蓋同名的檔案。
        /// </summary>  
        /// <param name="sourceFileName">要複製的檔案</param>  
        /// <param name="destFileName">
目標檔案的名稱。不能是目錄。</param> /// <param name="overwrite">如果可以覆蓋目標檔案,則為 true;否則為 false。</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static SqlBoolean FileCopy (string sourceFileName, string destFileName, bool overwrite) {
try { File.Copy (sourceFileName, destFileName, overwrite); return File.Exists (destFileName); } catch (Exception) { return false; } return false; } /// <summary> /// 將指定檔案移到新位置,並提供指定新檔名的選項。
/// </summary> /// <param name="sourceFileName">要複製的檔案</param> /// <param name="destFileName">目標檔案的名稱。不能是目錄。</param> /// <param name="overwrite">如果可以覆蓋目標檔案,則為 true;否則為 false。</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static SqlBoolean FileMove (string sourceFileName, string destFileName, bool overwrite) { try { bool isExists = File.Exists (destFileName); if (isExists && overwrite) File.Delete (destFileName); if (isExists && !overwrite) return false; File.Move (sourceFileName, destFileName); return File.Exists (destFileName); } catch (Exception) { return false; } return false; } /// <summary> /// 確定指定的檔案是否存在。 /// </summary> /// <param name="FilePath">檔案路徑</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static SqlBoolean FileExists (string FilePath) { try { return File.Exists (FilePath); } catch (Exception) { return false; } return false; } /// <summary> /// 建立一個新檔案,在其中寫入指定的字串,然後關閉檔案。如果目標檔案已存在,則覆蓋該檔案。 /// </summary> /// <param name="FilePath">檔案路徑</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static bool FileWriteText (string FilePath, string Contents) { try { File.WriteAllText (FilePath, Contents); return File.Exists (FilePath); } catch (Exception) { return false; } return false; } /// <summary> /// 讀取文字 /// </summary> /// <param name="FilePath">檔案路徑</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static string FileReadText (string FilePath) { try { return File.ReadAllText (FilePath); } catch (Exception) { return ""; } return ""; } /// <summary> /// 建立目錄 /// </summary> /// <param name="FilePath">檔案路徑</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static bool DirectoryCreateDirectory (string DirPath) { try { Directory.CreateDirectory (DirPath); return Directory.Exists (DirPath); } catch (Exception) { return false; } return false; } /// <summary> /// 刪除目錄 /// </summary> /// <param name="FilePath">檔案路徑</param> /// <param name="recursive">是否刪除所有子目錄與檔案</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static bool DirectoryDelete (string DirPath, bool recursive) { try { Directory.Delete (DirPath, recursive); return !Directory.Exists (DirPath); } catch (Exception) { return false; } return false; } /// <summary> /// 目錄是否存在 /// </summary> /// <param name="FilePath">檔案路徑</param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static bool DirectoryExists (string DirPath) { try { return Directory.Exists (DirPath); } catch (Exception) { return false; } return false; }
View Code

表值函式:讀取文字,獲取檔案資訊,獲取子目錄清單

    /// <summary>
    /// 表值函式
    ///--屬性                    --說明
    ///--DataAccess            --指示該函式是否涉及訪問儲存在SQL Server的資料
    ///--FillRowMethodName        --在同一個類的方法的名稱作為表值函式(TVF),這個引數在表值函式中才會用到,用於指定表值函式的資料填充方法
    ///--IsDeterministic        --指示使用者定義的函式是否是確定性的
    ///--IsPrecise                --指示函式是否涉及不精確計算,如浮點運算
    ///--Name                    --函式在SQL Server中註冊時使用的函式的名稱
    ///--SystemDataAccess        --指示該函式是否需要訪問儲存在系統目錄或SQL Server虛擬系統表中的資料
    ///--TableDefinition        --如果方法作為表值函式(TVF),則為一個字串,該字串表示表結構的定義
    /// </summary>
    public partial class SQLfunction
    {
        /// <summary>
        /// SQL Server 讀取文字轉為表
        /// </summary>
        /// <param name="separator"></param>
        /// <param name="pendingString"></param>
        /// <returns></returns>
        [Microsoft.SqlServer.Server.SqlFunction(
            DataAccess = DataAccessKind.Read,
            IsDeterministic = true,
            Name = "FileReadText2Table",
            FillRowMethodName = "SqlSplit_FillRow",
            TableDefinition = "SerialNumber int,StringValue nvarchar(1024)")]
        public static IEnumerable FileReadText2Table(string path)
        {
            string[] strs = { };
                        strs = File.ReadAllLines(path);
            List<ResultData> resultDataList = new List<ResultData>();
            for (int i = 0; i < strs.Length; i++)
            {
                resultDataList.Add(new ResultData(i + 1, strs[i]));
            }
            return resultDataList;
        }

        /// <summary>
        /// SQL Server 檔案資訊獲取
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [Microsoft.SqlServer.Server.SqlFunction(
            DataAccess = DataAccessKind.Read,
            IsDeterministic = true,
            Name = "FileInfo2Table",
            FillRowMethodName = "SqlKeyValue_FillRow",
            TableDefinition = "SerialNumber int,StringKey nvarchar(1024),StringValue nvarchar(1024)")]
        public static IEnumerable FileInfo2Table(string path)
        {
            List<ResultKeyValueData> resultDataList = new List<ResultKeyValueData>();
            FileInfo fileInfo = new FileInfo(path);
            resultDataList.Add(new ResultKeyValueData(1, "FullName", fileInfo.FullName));
            resultDataList.Add(new ResultKeyValueData(2, "DirectoryName", fileInfo.DirectoryName));
            resultDataList.Add(new ResultKeyValueData(3, "Name", Path.GetFileNameWithoutExtension(fileInfo.FullName)));
            resultDataList.Add(new ResultKeyValueData(4, "Extension", fileInfo.Extension));
            resultDataList.Add(new ResultKeyValueData(5, "IsReadOnly", fileInfo.IsReadOnly.ToString()));
            resultDataList.Add(new ResultKeyValueData(6, "CreationTime", fileInfo.CreationTime.ToString()));
            resultDataList.Add(new ResultKeyValueData(7, "LastAccessTime", fileInfo.LastAccessTime.ToString()));
            resultDataList.Add(new ResultKeyValueData(8, "LastWriteTime", fileInfo.LastWriteTime.ToString()));
            resultDataList.Add(new ResultKeyValueData(9, "Length", fileInfo.Length.ToString()));
            resultDataList.Add(new ResultKeyValueData(10, "Attributes", fileInfo.Attributes.ToString()));
            return resultDataList;
        }

        /// <summary>
        /// SQL Server 獲取目錄--子目錄清單
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [Microsoft.SqlServer.Server.SqlFunction(
            DataAccess = DataAccessKind.Read,
            IsDeterministic = true,
            Name = "DirectoryGetFiles",
            FillRowMethodName = "SqlSplit_FillRow",
            TableDefinition = "SerialNumber int,StringValue nvarchar(1024)")]
        public static IEnumerable DirectoryGetFiles(string path)
        {
            string[] strs = { };
            strs = Directory.GetFiles(path);
            List<ResultData> resultDataList = new List<ResultData>();
            for (int i = 0; i < strs.Length; i++)
            {
                resultDataList.Add(new ResultData(i + 1, strs[i]));
            }
            return resultDataList;
        }

        /// <summary>
        /// SQL Server 獲取目錄--檔案清單
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [Microsoft.SqlServer.Server.SqlFunction(
            DataAccess = DataAccessKind.Read,
            IsDeterministic = true,
            Name = "DirectoryGetDirectories",
            FillRowMethodName = "SqlSplit_FillRow",
            TableDefinition = "SerialNumber int,StringValue nvarchar(1024)")]
        public static IEnumerable DirectoryGetDirectories(string path)
        {
            string[] strs = { };
            IntPtr admin_token = IntPtr.Zero;
            if (WinLogonHelper.LogonUser(ref admin_token) != 0)
            {
                using (WindowsIdentity wid_admin = new WindowsIdentity(admin_token))
                {
                    using (WindowsImpersonationContext wic = wid_admin.Impersonate())
                    {
                        strs = Directory.GetDirectories(path);
                    }
                }
            }
            List<ResultData> resultDataList = new List<ResultData>();
            for (int i = 0; i < strs.Length; i++)
            {
                resultDataList.Add(new ResultData(i + 1, strs[i]));
            }
            return resultDataList;
        }


    }
   
    /// <summary>
    /// 表值函式
    ///--屬性                    --說明
    ///--DataAccess            --指示該函式是否涉及訪問儲存在SQL Server的資料
    ///--FillRowMethodName        --在同一個類的方法的名稱作為表值函式(TVF),這個引數在表值函式中才會用到,用於指定表值函式的資料填充方法
    ///--IsDeterministic        --指示使用者定義的函式是否是確定性的
    ///--IsPrecise                --指示函式是否涉及不精確計算,如浮點運算
    ///--Name                    --函式在SQL Server中註冊時使用的函式的名稱
    ///--SystemDataAccess        --指示該函式是否需要訪問儲存在系統目錄或SQL Server虛擬系統表中的資料
    ///--TableDefinition        --如果方法作為表值函式(TVF),則為一個字串,該字串表示表結構的定義
    /// </summary>
    public partial class SQLfunction
    {
        /// <summary>
        /// SQL Server 字串分割方法
        /// </summary>
        /// <param name="separator"></param>
        /// <param name="pendingString"></param>
        /// <returns></returns>
        [Microsoft.SqlServer.Server.SqlFunction(
            DataAccess = DataAccessKind.Read,
            IsDeterministic = true,
            Name = "SqlSplit",
            FillRowMethodName = "SqlSplit_FillRow",
            TableDefinition = "SerialNumber int,StringValue nvarchar(1024)")]
        public static IEnumerable SqlSplit(SqlString separator, SqlString pendingString)
        {
            string _separator = string.Empty;
            string _pendingString = string.Empty;
            if (pendingString.IsNull) return null;
            _pendingString = pendingString.ToString();
            if (string.IsNullOrEmpty(_pendingString)) return null;
            _separator = separator.IsNull ? "," : separator.ToString();
            _separator = string.IsNullOrEmpty(_separator) ? "," : _separator;
            string[] strs = _pendingString.Split(new string[] { _separator }, StringSplitOptions.RemoveEmptyEntries);
            List<ResultData> resultDataList = new List<ResultData>();
            for (int i = 0; i < strs.Length; i++)
            {
                resultDataList.Add(new ResultData(i + 1, strs[i]));
            }
            return resultDataList;
        }


        #region 表值變數 Id,Value
        /// <summary>
        /// 填充資料方法
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="serialNumber"></param>
        /// <param name="stringValue"></param>
        public static void SqlSplit_FillRow(Object obj, out SqlInt32 SerialNumber, out SqlString StringValue)
        {
            ResultData resultData = (ResultData)obj;
            SerialNumber = resultData.SerialNumber;
            StringValue = resultData.StringValue;
        }


        /// <summary>
        /// 定義返回型別
        /// </summary>
        public class ResultData
        {
            /// <summary>
            /// 序號,即行號
            /// </summary>
            public SqlInt32 SerialNumber { get; set; }

            /// <summary>
            /// 分割後的每個子字串
            /// </summary>
            public SqlString StringValue { get; set; }

            public ResultData(SqlInt32 serialNumber, SqlString stringValue)
            {
                SerialNumber = serialNumber;
                StringValue = stringValue;
            }
        }

        #endregion


        #region 表值變數 ID,Key,Value
        /// <summary>
        /// 填充資料方法
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="serialNumber"></param>
        /// <param name="stringValue"></param>
        public static void SqlKeyValue_FillRow(Object obj, out SqlInt32 SerialNumber, out SqlString StringKey, out SqlString StringValue)
        {
            ResultKeyValueData resultData = (ResultKeyValueData)obj;
            SerialNumber = resultData.SerialNumber;
            StringKey = resultData.StringKey;
            StringValue = resultData.StringValue;
        }


        /// <summary>
        /// 定義返回型別
        /// </summary>
        public class ResultKeyValueData
        {
            /// <summary>
            /// 序號,即行號
            /// </summary>
            public SqlInt32 SerialNumber { get; set; }
            /// <summary>
            ////// </summary>
            public SqlString StringKey { get; set; }
            /// <summary>
            ////// </summary>
            public SqlString StringValue { get; set; }

            public ResultKeyValueData(SqlInt32 serialNumber, SqlString stringKey, SqlString stringValue)
            {
                SerialNumber = serialNumber;
                StringKey = stringKey;
                StringValue = stringValue;
            }
        }

        #endregion

    }
View Code

二.SQL伺服器CLR配置(允許SQL呼叫.net程式)

    sp_configure 'show advanced options', 1; 
    RECONFIGURE WITH override
    GO 
    sp_configure 'clr enabled', 1; 
    RECONFIGURE WITH override
    GO
    Sp_changedbowner 'sa',true   --sa改為當前登入使用者名稱
    alter database [dbname] set trustworthy on    --bbname 改為自己的庫名

三.註冊 CLR 程式集

   create  ASSEMBLY SQLfunctionAssembly   
   FROM 'D:\SQLClr.dll'      --改為自己C#寫的dll路徑填寫
   WITH PERMISSION_SET = UNSAFE;   

        建立的.net程式集資料會寫入下表:

  select * from sys.assemblies 
  select  * from sys.assembly_files

    

四.建立標量函式與表值函式(分別2類函式舉例)

   1.標量函式----檔案複製

CREATE FUNCTION [dbo].[FileCopy](@sourceFileName [nvarchar](max), @destFileName [nvarchar](max), @overwrite [bit])
RETURNS [bit] WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME [SQLfunctionAssembly].[SQLClr.SQLfunction].[FileCopy]

    2.表值函式----獲取檔案資訊

CREATE FUNCTION [dbo].[FileInfo2Table](@path [nvarchar](max))
RETURNS  TABLE (
    [SerialNumber] [int] NULL,
    [StringKey] [nvarchar](max) NULL,
    [StringValue] [nvarchar](max) NULL
) WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME [SQLfunctionAssembly].[SQLClr.SQLfunction].[FileInfo2Table]

五.測試檔案操作函式

   測試3個函式 

   說明一下D:\features檔案是指伺服器上的D盤features檔案,而不是客戶端的檔案哦.

DECLARE @file  VARCHAR(MAX)
SET @file = 'D:\features'
--1.讀取檔案屬性到Table select * from dbo.FileInfo2Table(@file)
--2.讀取文字到Table
select * from dbo.FileReadText2Table(@file)
-- 3.讀取文字
select dbo.FileReadText(@file)

   測試結果:

 六.SQL SERVER訪問共享目錄方法

      採用SQL SERVER操作檔案是不允許操作區域網中的共享檔案的,若想實現的話需Windows模擬域帳號登入,另一篇文章有講到的。PCB 工程系統 模擬windows域帳號登入

       以檔案複製為例程式碼如下:

        /// <summary>  
        /// 讀取文字
        /// </summary>  
        /// <param name="FilePath">檔案路徑</param>  
        /// <returns></returns>  
        [Microsoft.SqlServer.Server.SqlFunction]
        public static string FileReadText (string FilePath) {
            try {
                IntPtr admin_token = IntPtr.Zero;
                if (WinLogonHelper.LogonUser (ref admin_token) != 0) {
                    using (WindowsIdentity wid_admin = new WindowsIdentity (admin_token)) {
                        using (WindowsImpersonationContext wic = wid_admin.Impersonate ()) {
                            return File.ReadAllText (FilePath);
                        }
                    }
                }
            } catch (Exception) {
                return "";
            }
            return "";
        }

        public class WinLogonHelper {
            /// <summary>
            /// 模擬windows登入域
            /// </summary>
            [DllImport ("advapi32.DLL", SetLastError = true)]
            public static extern int LogonUser (string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
            public static int LogonUser (ref IntPtr phToken) {
                return WinLogonHelper.LogonUser ("使用者名稱", "域名", "密碼", 2, 0, ref phToken);
            }
        }