├── .gitattributes ├── .gitignore ├── README.md └── SqlClrCustomSendMail-master ├── SqlClrCustomSendMail.sln └── SqlClrCustomSendMail ├── Configuration ├── SysConfig.cs ├── SysProfile.cs └── SysProfiles.cs ├── DataAccess └── DataAccess.cs ├── Database ├── MakeSomeNullValues.sql ├── PreDeploy.sql └── SchemaDefinition.sql ├── Extensions └── MailMessageExtensions.cs ├── Functions ├── CleanMemory.cs ├── ConCatHtml.cs ├── CustomSendMailHelp.cs └── QueryToHtml.cs ├── HtmlSupport ├── DetermineStyle.cs └── RenderArray.cs ├── Logging ├── EMailTracker.cs ├── LogEntry.cs └── SaveEmail.cs ├── Procedures └── CustomSendMail.cs ├── Properties └── AssemblyInfo.cs ├── Security └── EncryptSupport.cs ├── Setup └── Setup.sql ├── SqlClrCustomSendMail.sqlproj ├── Test ├── CleanUp.sql ├── HelpTest.sql ├── Test.sql └── Test2.sql └── keySqlClrCustomSendMail.snk /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLCLR solution to replace 'sp_send_dbmail' with custom stored procedure 2 | The code written in any .NET language to be incorporated into your SQL Server instance and called from a stored procedure or function 3 | 4 | 5 | If you do not want to do anything with the source code, 6 | copy the t-sql script from 'Setup' directory to the SSMS. 7 | 8 | 9 | In that script, replace the phrase 'your database name' with the real name of your database. 10 | Similar do with 'DataPath' and 'LogPath'. 11 | 12 | 13 | This script also fills out the table of profiles. 14 | 15 | Use the encryption program described in the article to fill the profile table. 16 | 17 | To clean up environment, use CleanUp script located in 'Test' directory. 18 | 19 | 20 | ## To send e-mail 21 | 22 | 23 | EXEC [EMAIL].[CLRSendMail] @profileName = N'SimpleTalk' 24 | ,@mailTo = N'yourEmail@Email.com' 25 | ,@mailSubject = N'First test' 26 | ,@mailBody = N'Mail body'; 27 | 28 | 29 | ## To include query result in e-mail body 30 | 31 | 32 | DECLARE @body as nvarchar(max) 33 | 34 | 35 | SET @body = EMAIL.QueryToHtml('SELECT * FROM EMAIL.PROFILES', '', 'EMAIL.Profiles', '#', 2, 0, 'ST_BLUE') 36 | 37 | 38 | EXEC [EMAIL].[CLRSendMail] @profileName = N'SimpleTalk' 39 | ,@mailTo = N'yourEmail@Email.com' 40 | ,@mailSubject = N'Test QueryToHtml' 41 | ,@mailBody = @body; 42 | 43 | 44 | ## To include multiple query results in e-mail body 45 | 46 | 47 | SET @body = (SELECT 48 | EMAIL.ConCatHtml(@body, (SELECT 49 | EMAIL.QueryToHtml('SELECT 50 | * 51 | FROM EMAIL.Configurations','', 52 | 'EMAIL.Configuration', '#', 2, 0, 'ST_RED')) 53 | )); 54 | 55 | 56 | EXEC [EMAIL].[CLRSendMail] @profileName = N'SimpleTalk' 57 | ,@mailTo = N'yourEmail@Email.com' 58 | ,@mailSubject = N'Test ConCatHtml' 59 | ,@mailBody = @body; 60 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.15 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "SqlClrCustomSendMail", "SqlClrCustomSendMail\SqlClrCustomSendMail.sqlproj", "{90F8F52F-4065-4B02-858E-F9AB941C5284}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {90F8F52F-4065-4B02-858E-F9AB941C5284}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {90F8F52F-4065-4B02-858E-F9AB941C5284}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {90F8F52F-4065-4B02-858E-F9AB941C5284}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 17 | {90F8F52F-4065-4B02-858E-F9AB941C5284}.Release|Any CPU.ActiveCfg = Release|Any CPU 18 | {90F8F52F-4065-4B02-858E-F9AB941C5284}.Release|Any CPU.Build.0 = Release|Any CPU 19 | {90F8F52F-4065-4B02-858E-F9AB941C5284}.Release|Any CPU.Deploy.0 = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | EndGlobal 25 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Configuration/SysConfig.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace SqlClrCustomSendMail 3 | { 4 | public class SysConfig 5 | { 6 | // Implemented 7 | /// 8 | /// Maximum file size in bytes. Default 1MB 9 | /// 10 | public int MaxFileSize { get; set; } = 1000000; 11 | 12 | // Implemented 13 | /// 14 | /// File extension that are not allowed 15 | /// 16 | public string ProhibitedExtension { get; set; } = "exe,dll,vbs,js"; 17 | 18 | public ELoggingLevel LoggingLevel { get; set; } = ELoggingLevel.Minimal; 19 | 20 | /// 21 | /// Do we save e-mails? 22 | /// 23 | public bool SaveEmails { get; set; } = true; 24 | 25 | /// 26 | /// What is configuration name 27 | /// 28 | public string Name { get; set; } 29 | 30 | /// 31 | /// How we send an e-mail? 32 | /// 33 | public bool SendAsync { get; set; } = true; 34 | 35 | /// 36 | /// Do we need piping 37 | /// 38 | public bool NoPiping { get; set; } = true; 39 | 40 | /// 41 | /// Do we save attachments 42 | /// 43 | public bool SaveAttachments { get; set; } = true; 44 | } 45 | 46 | public enum ELoggingLevel 47 | { 48 | Minimal = 0, 49 | Usual = 1, 50 | Maximum = 2 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Configuration/SysProfile.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Mail; 2 | 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace SqlClrCustomSendMail 6 | { 7 | public class SysProfile 8 | { 9 | /// 10 | /// An instance of SmptClient 11 | /// 12 | public SmtpClient Client 13 | { 14 | get; 15 | set; 16 | } 17 | /// 18 | /// Default from address 19 | /// 20 | public string DefaultFromAddress 21 | { 22 | get; 23 | set; 24 | } 25 | /// 26 | /// Default display name 27 | /// 28 | public string DefaultDisplayName 29 | { 30 | get; 31 | set; 32 | } 33 | /// 34 | /// Default domain name for Exchange users 35 | /// 36 | public string DefaultDomain 37 | { 38 | get; 39 | set; 40 | } 41 | /// 42 | /// Default name 43 | /// 44 | public string Name 45 | { 46 | get; 47 | set; 48 | } 49 | /// 50 | /// Built-in name 51 | /// 52 | public string BuilInName 53 | { 54 | // ReSharper disable once UnusedAutoPropertyAccessor.Global 55 | get; 56 | set; 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Configuration/SysProfiles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Mail; 4 | using System.Text; 5 | 6 | namespace SqlClrCustomSendMail 7 | { 8 | public class SysProfiles 9 | { 10 | public SmtpClient client 11 | { 12 | get; set; 13 | } = null; 14 | public string defaultFromAddress 15 | { 16 | get; set; 17 | } = null; 18 | public string defaultGroupName 19 | { 20 | get; set; 21 | } = null; 22 | public string defaultDomain 23 | { 24 | get; set; 25 | } = null; 26 | public string name 27 | { 28 | get; set; 29 | } = null; 30 | public string builInName 31 | { 32 | get; set; 33 | } = null; 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/DataAccess/DataAccess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Globalization; 6 | using System.Net.Mail; 7 | using System.Text.RegularExpressions; 8 | 9 | // ReSharper disable once CheckNamespace 10 | namespace SqlClrCustomSendMail 11 | { 12 | internal class DataAccess 13 | { 14 | private readonly List _data = new List(); 15 | public int RowCount; 16 | public int FieldCount; 17 | public List GetData(string query, bool isSp, SqlParameter[] listOfParams, ref string html) 18 | { 19 | try 20 | { 21 | using (var cnn = new SqlConnection("context connection=true")) 22 | { 23 | using (var command = new SqlCommand(query, cnn)) 24 | { 25 | command.Parameters.Clear(); 26 | cnn.Open(); 27 | if (isSp) 28 | command.CommandType = CommandType.StoredProcedure; 29 | if (listOfParams != null) 30 | { 31 | foreach (var p in listOfParams) 32 | command.Parameters.Add(p); 33 | } 34 | 35 | var dr = command.ExecuteReader(); 36 | 37 | FieldCount = dr.FieldCount; 38 | var o = new object[FieldCount]; 39 | for (var i = 0; i < FieldCount; i++) 40 | o[i] = dr.GetName(i); 41 | 42 | _data.Add(o); 43 | RowCount++; 44 | 45 | while (dr.Read()) 46 | { 47 | var o1 = new object[FieldCount]; 48 | dr.GetValues(o1); 49 | _data.Add(o1); 50 | RowCount++; 51 | } 52 | 53 | } 54 | } 55 | } 56 | catch (Exception ex) 57 | { 58 | html += ex.Message; 59 | } 60 | 61 | return _data; 62 | } 63 | 64 | public static SysProfile GetProfile(SqlParameter[] listOfParams, EncryptSupport.Simple3Des wrapper, ref string errorMessage) 65 | { 66 | SysProfile p = null; 67 | 68 | 69 | try 70 | { 71 | using (var cnn = new SqlConnection("context connection=true")) 72 | { 73 | using (var command = new SqlCommand(@"SELECT TOP 1 74 | EnableSsl, 75 | DefaultCred, 76 | HostName, 77 | Port, 78 | UserName, 79 | Password, 80 | Domain, 81 | DefaultFrom, 82 | DefaultGroup, 83 | DeliveryMethod, 84 | TimeOut, 85 | ProfileName 86 | FROM EMAIL.PROFILES 87 | WHERE ProfileName=@name;", cnn)) 88 | { 89 | cnn.Open(); 90 | if (listOfParams != null) 91 | { 92 | foreach (var p1 in listOfParams) 93 | command.Parameters.Add(p1); 94 | } 95 | 96 | SqlDataReader dr = command.ExecuteReader(); 97 | if (dr.Read()) 98 | { 99 | p = new SysProfile 100 | { 101 | Client = new SmtpClient(), 102 | Name = (string) dr["ProfileName"] 103 | }; 104 | p.Client.EnableSsl = (bool)dr["EnableSsl"]; 105 | p.Client.UseDefaultCredentials = (bool)dr["DefaultCred"]; 106 | p.Client.Host = (string)dr["HostName"]; 107 | p.Client.Port = (int)dr["Port"]; 108 | p.Client.Timeout = (int)dr["TimeOut"]; 109 | p.Client.DeliveryMethod = (SmtpDeliveryMethod)Enum.Parse(typeof(SmtpDeliveryMethod), dr["DeliveryMethod"].ToString()); 110 | if (dr["Domain"] != DBNull.Value) 111 | { 112 | p.DefaultDomain = (string) dr["Domain"]; 113 | if (p.Client.UseDefaultCredentials == false) 114 | p.Client.Credentials = 115 | new System.Net.NetworkCredential(wrapper.DecryptData((string) dr["UserName"]), 116 | wrapper.DecryptData((string) dr["Password"]), p.DefaultDomain); 117 | 118 | // p.client.Credentials = new System.Net.NetworkCredential((string)dr["UserName"], (string)dr["Password"], p.defaultDomain); 119 | } 120 | else if (p.Client.UseDefaultCredentials == false) 121 | p.Client.Credentials = new System.Net.NetworkCredential(wrapper.DecryptData((string)dr["UserName"]), wrapper.DecryptData((string)dr["Password"])); 122 | 123 | p.DefaultFromAddress = (string)dr["DefaultFrom"]; 124 | if (dr["DefaultGroup"] != DBNull.Value) 125 | p.DefaultDisplayName = (string)dr["DefaultGroup"]; 126 | dr.Close(); 127 | } 128 | 129 | cnn.Close(); 130 | } 131 | } 132 | } 133 | catch (Exception ex) 134 | { 135 | p = null; 136 | errorMessage = ex.Message ; 137 | } 138 | 139 | return p; 140 | } 141 | 142 | 143 | public static SysConfig GetConfig(SqlParameter[] listOfParams, ref string errorMessage) 144 | { 145 | SysConfig p = null; 146 | 147 | 148 | try 149 | { 150 | using (var cnn = new SqlConnection("context connection=true")) 151 | { 152 | using (var command = new SqlCommand(@"SELECT TOP 1 153 | [MaxFileSize] 154 | ,[ProhibitedExtensions] 155 | ,[LoggingLevel] 156 | ,[SaveEmails] 157 | ,[SendAsync] 158 | ,[NoPiping] 159 | ,[SaveAttachments] 160 | ,[Name] 161 | FROM [EMAIL].[Configurations] 162 | WHERE Name=@name;", cnn)) 163 | { 164 | cnn.Open(); 165 | if (listOfParams != null) 166 | { 167 | foreach (var p1 in listOfParams) 168 | command.Parameters.Add(p1); 169 | } 170 | 171 | var dr = command.ExecuteReader(); 172 | if (dr.Read()) 173 | { 174 | p = new SysConfig 175 | { 176 | MaxFileSize = (Int32) dr["MaxFileSize"], 177 | ProhibitedExtension = (string) dr["ProhibitedExtensions"], 178 | LoggingLevel = 179 | (ELoggingLevel) Enum.Parse(typeof(ELoggingLevel), dr["LoggingLevel"].ToString()), 180 | SaveEmails = (bool) dr["SaveEmails"], 181 | NoPiping = (bool) dr["NoPiping"], 182 | SaveAttachments = (bool) dr["SaveAttachments"], 183 | SendAsync = (bool) dr["SendAsync"], 184 | Name = (string) dr["Name"] 185 | }; 186 | dr.Close(); 187 | } 188 | 189 | cnn.Close(); 190 | } 191 | } 192 | } 193 | catch (Exception ex) 194 | { 195 | p = null; 196 | errorMessage = ex.Message; 197 | if ( ex.InnerException != null) 198 | errorMessage += ex.InnerException.Message; 199 | 200 | } 201 | 202 | return p; 203 | } 204 | 205 | 206 | /// 207 | /// https://msdn.microsoft.com/en-us/library/ms131092.aspx 208 | /// 209 | /// 210 | /// 211 | /// 212 | private static SqlDbType DetermineSqlDbType(string input, ref string html) 213 | { 214 | var retValue = SqlDbType.NVarChar; 215 | try 216 | { 217 | 218 | if (Regex.IsMatch(input.ToLower(), "nvarchar")) // test it 219 | retValue = SqlDbType.NVarChar; 220 | else if (Regex.IsMatch(input.ToLower(), "int")) // test it 221 | retValue = SqlDbType.Int; 222 | else if (Regex.IsMatch(input.ToLower(), "char")) 223 | retValue = SqlDbType.Char; 224 | else if (Regex.IsMatch(input.ToLower(), "decimal")) // test it 225 | retValue = SqlDbType.Decimal; 226 | else if (Regex.IsMatch(input.ToLower(), "datetime")) // test it 227 | retValue = SqlDbType.DateTime; 228 | else if (Regex.IsMatch(input.ToLower(), "date")) 229 | retValue = SqlDbType.Date; 230 | else if (Regex.IsMatch(input.ToLower(), "bit")) // test it 231 | retValue = SqlDbType.Bit; 232 | else if (Regex.IsMatch(input.ToLower(), "bigint")) // test it 233 | retValue = SqlDbType.BigInt; 234 | else if (Regex.IsMatch(input.ToLower(), "binary")) 235 | retValue = SqlDbType.Binary; 236 | else if (Regex.IsMatch(input.ToLower(), "datetime2")) 237 | retValue = SqlDbType.DateTime2; 238 | else if (Regex.IsMatch(input.ToLower(), "datetimeoffset")) 239 | retValue = SqlDbType.DateTimeOffset; 240 | else if (Regex.IsMatch(input.ToLower(), "float")) // test it 241 | retValue = SqlDbType.Float; 242 | else if (Regex.IsMatch(input.ToLower(), "image")) 243 | retValue = SqlDbType.Image; 244 | else if (Regex.IsMatch(input.ToLower(), "money")) 245 | retValue = SqlDbType.Money; 246 | else if (Regex.IsMatch(input.ToLower(), "nchar")) // test it 247 | retValue = SqlDbType.NChar; 248 | else if (Regex.IsMatch(input.ToLower(), "ntext")) 249 | retValue = SqlDbType.NText; 250 | else if (Regex.IsMatch(input.ToLower(), "real")) 251 | retValue = SqlDbType.Real; 252 | else if (Regex.IsMatch(input.ToLower(), "smalldatetime")) 253 | retValue = SqlDbType.SmallDateTime; 254 | else if (Regex.IsMatch(input.ToLower(), "smallint")) // test it 255 | retValue = SqlDbType.SmallInt; 256 | else if (Regex.IsMatch(input.ToLower(), "smallmoney")) 257 | retValue = SqlDbType.SmallMoney; 258 | else if (Regex.IsMatch(input.ToLower(), "structed")) 259 | retValue = SqlDbType.Structured; 260 | else if (Regex.IsMatch(input.ToLower(), "text")) 261 | retValue = SqlDbType.Text; 262 | else if (Regex.IsMatch(input.ToLower(), "time")) 263 | retValue = SqlDbType.Time; 264 | else if (Regex.IsMatch(input.ToLower(), "timestamp")) 265 | retValue = SqlDbType.Timestamp; 266 | else if (Regex.IsMatch(input.ToLower(), "tinyint")) 267 | retValue = SqlDbType.TinyInt; 268 | else if (Regex.IsMatch(input.ToLower(), "uniqueidentifier")) // test it 269 | retValue = SqlDbType.UniqueIdentifier; 270 | else if (Regex.IsMatch(input.ToLower(), "varbinary")) 271 | retValue = SqlDbType.VarBinary; 272 | else if (Regex.IsMatch(input.ToLower(), "varchar")) 273 | retValue = SqlDbType.VarChar; 274 | else if (Regex.IsMatch(input.ToLower(), "variant")) 275 | retValue = SqlDbType.Variant; 276 | else if (Regex.IsMatch(input.ToLower(), "xml")) 277 | retValue = SqlDbType.Xml; 278 | 279 | 280 | 281 | } 282 | catch (Exception ex) 283 | { 284 | html += ex.Message; 285 | } 286 | 287 | return retValue; 288 | } 289 | 290 | private static int DeterminSize(string size, ref byte scale) 291 | { 292 | int retValue = 0; 293 | string[] splitter = size.Split(','); 294 | if (splitter.Length >= 1) 295 | { 296 | var refer = int.TryParse(splitter[0], out var outValue); 297 | if (refer) 298 | retValue = outValue; 299 | } 300 | 301 | if (splitter.Length == 2) 302 | { 303 | var res = byte.TryParse(splitter[1], out var ref1); 304 | if (res) 305 | scale = ref1; 306 | } 307 | 308 | return retValue; 309 | } 310 | 311 | public static SqlParameter[] MakeParams(string value, ref string html) 312 | { 313 | SqlParameter[] sp = null; 314 | try 315 | { 316 | var splitter = Regex.Replace(value, "\r\n", string.Empty, RegexOptions.IgnoreCase).Split(','); 317 | var pureString = new Dictionary(); 318 | for (var i = 0; i < splitter.Length; i++) 319 | { 320 | if (splitter[i].Contains("@") == false && i > 0) 321 | { 322 | pureString[i - 1] += "," + splitter[i]; 323 | continue; 324 | } 325 | 326 | pureString.Add(i, splitter[i].Trim()); 327 | } 328 | 329 | var counter = 0; 330 | foreach (var s in pureString.Values) 331 | { 332 | var s1 = new SqlParameter {ParameterName = s.Trim().Substring(0, s.Trim().IndexOf(' '))}; 333 | var valueSpliiter = s.Split('='); 334 | if (valueSpliiter.Length > 1) 335 | { 336 | var tester = valueSpliiter[0].Substring(valueSpliiter[0].IndexOf(' '), valueSpliiter[0].Length - valueSpliiter[0].IndexOf(' ')); 337 | s1.SqlDbType = DetermineSqlDbType(tester, ref html); 338 | if (tester.Contains("(")) 339 | { 340 | var pomValue = Regex.Replace(tester, " ", string.Empty, RegexOptions.IgnoreCase); 341 | var size = pomValue.Substring(pomValue.IndexOf("(", StringComparison.Ordinal) + 1, pomValue.IndexOf(")", StringComparison.Ordinal) - pomValue.IndexOf("(", StringComparison.Ordinal) - 1); 342 | byte scale = 0; 343 | var sizeTester = DeterminSize(size, ref scale); 344 | if (sizeTester != 0) 345 | s1.Size = sizeTester; 346 | if (scale != 0) 347 | s1.Scale = scale; 348 | 349 | } 350 | 351 | DetermineValue(valueSpliiter[1], ref s1); 352 | 353 | } 354 | 355 | if (sp == null) 356 | sp = new SqlParameter[pureString.Values.Count]; 357 | sp[counter] = s1; 358 | counter++; 359 | 360 | 361 | 362 | } 363 | 364 | } 365 | catch (Exception ex) 366 | { 367 | sp = null; 368 | html += ex.Message; 369 | } 370 | 371 | return sp; 372 | 373 | } 374 | 375 | 376 | 377 | private static void DetermineValue(string valueSplitter, ref SqlParameter s1) 378 | { 379 | if (s1.SqlDbType == SqlDbType.Int) 380 | { 381 | var succ = int.TryParse(valueSplitter, out var j); 382 | if (succ) 383 | s1.Value = j; 384 | } 385 | else if (s1.SqlDbType == SqlDbType.SmallInt) 386 | { 387 | var succ = short.TryParse(valueSplitter, out var j); 388 | if (succ) 389 | s1.Value = j; 390 | } 391 | else if (s1.SqlDbType == SqlDbType.BigInt) 392 | { 393 | var succ = long.TryParse(valueSplitter, out var j); 394 | if (succ) 395 | s1.Value = j; 396 | } 397 | else if (s1.SqlDbType == SqlDbType.Real) 398 | { 399 | var succ = float.TryParse(valueSplitter, out var j); 400 | if (succ) 401 | s1.Value = j; 402 | } 403 | else if (s1.SqlDbType == SqlDbType.Decimal) 404 | { 405 | var succ = decimal.TryParse(valueSplitter.Replace(".", CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator), out var j); 406 | if (succ) 407 | s1.Value = j; 408 | } 409 | else if (s1.SqlDbType == SqlDbType.Float) 410 | { 411 | var succ = double.TryParse(valueSplitter.Replace(".", CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator), out var j); 412 | if (succ) 413 | s1.Value = j; 414 | } 415 | else if (s1.SqlDbType == SqlDbType.Date) 416 | { 417 | var valueString = Regex.Replace(valueSplitter, "'", string.Empty, RegexOptions.IgnoreCase).Trim(); 418 | s1.Value = DateTime.ParseExact(valueString, "yyyy-MM-dd", CultureInfo.InvariantCulture); 419 | } 420 | else if (s1.SqlDbType == SqlDbType.DateTime || s1.SqlDbType == SqlDbType.SmallDateTime) 421 | { 422 | var valueString = Regex.Replace(valueSplitter, "'", string.Empty, RegexOptions.IgnoreCase).Trim(); 423 | s1.Value = DateTime.ParseExact(valueString, "yyyy-MM-dd hh:mm:ss.fff", CultureInfo.InvariantCulture); 424 | } 425 | else if (s1.SqlDbType == SqlDbType.UniqueIdentifier) 426 | { 427 | var valueString = Regex.Replace(valueSplitter, "'", string.Empty, RegexOptions.IgnoreCase).Trim(); 428 | s1.Value = new Guid(valueString); 429 | } 430 | else if (s1.SqlDbType == SqlDbType.Bit) 431 | { 432 | // Bit to boolean 433 | var valueString = Regex.Replace(valueSplitter, "'", string.Empty, RegexOptions.IgnoreCase).Trim(); 434 | var succ = bool.TryParse(valueString, out var result); 435 | if (succ) 436 | s1.Value = result; 437 | } 438 | else 439 | { 440 | var valueString = valueSplitter.Trim(); 441 | if (valueString.StartsWith("'")) 442 | valueString = valueString.Substring(1); 443 | 444 | if (valueString.EndsWith("'")) 445 | valueString = valueString.Substring(0, valueString.Length - 1); 446 | s1.Value = valueString; 447 | 448 | 449 | } 450 | 451 | } 452 | 453 | // Method to Execute query 454 | 455 | 456 | 457 | 458 | 459 | 460 | } 461 | 462 | 463 | } 464 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Database/MakeSomeNullValues.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Post-Deployment Script Template 3 | -------------------------------------------------------------------------------------- 4 | This file contains SQL statements that will be appended to the build script. 5 | Use SQLCMD syntax to include a file in the post-deployment script. 6 | Example: :r .\myfile.sql 7 | Use SQLCMD syntax to reference a variable in the post-deployment script. 8 | Example: :setvar TableName MyTable 9 | SELECT * FROM [$(TableName)] 10 | -------------------------------------------------------------------------------------- 11 | */ 12 | ALTER PROCEDURE [dbo].[CLRSendMail] 13 | @profileName NVARCHAR (MAX), 14 | @mailTo NVARCHAR (MAX), 15 | @mailSubject NVARCHAR (255)=N'SQLCLR Server Message', 16 | @mailBody NVARCHAR (MAX), 17 | @fromAddress NVARCHAR (500)=NULL, 18 | @displayName NVARCHAR (400)=NULL, 19 | @mailCc NVARCHAR (4000)=NULL, 20 | @blindCopyRec NVARCHAR (4000)=NULL, 21 | @replyAddress NVARCHAR (4000)=NULL, 22 | @fileAttachments NVARCHAR (4000)=NULL, 23 | @requestReadReceipt BIT=0, 24 | @deliveryNotification SMALLINT=0, 25 | @sensitivity SMALLINT=-1, 26 | @mailPriorty SMALLINT=0, 27 | @bodyHtml BIT=1, 28 | @configName NVARCHAR(20)=NULL 29 | AS EXTERNAL NAME [SimpleTalk.SQLCLR.SendMail].[StoredProcedures].[CLRSendMail]; 30 | 31 | GO 32 | 33 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 34 | ----------------------------------------------------------------Add default record in profile table 35 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 36 | -- REPLACE YOUR LOGIN NAME WITH encrypted value of your login name 37 | --E.g. If your login name is TestAccount@gmail.com and the secret work 'SimpleTalk' then replace N'your login name' WITH N'BsqZkKED9s2XT/1mRt4ci2at9lb4ewGAN1I0RWiEYgzEl39KIo/ONgpM0FqOI3KC' 38 | --Do the same for password 39 | 40 | IF ( SELECT COUNT(*) FROM EMAIL.PROFILES ) = 0 41 | BEGIN 42 | INSERT INTO email.profiles (profilename, EnableSsl, DefaultCred, Port, HostName, UserName, Password,DefaultFrom) 43 | SELECT 44 | 'SimpleTalk' 45 | ,1 46 | ,0 47 | ,587 48 | ,'smtp.gmail.com' 49 | ,N'your login name' 50 | ,N'your password' 51 | ,'your default from address'; 52 | 53 | ---Use these values if you decide to apply T-SQL encryption by using ENCRYPTBYCERT function 54 | --,ENCRYPTBYCERT(CERT_ID('TestCert'), N'your login name') 55 | --,ENCRYPTBYCERT(CERT_ID('TestCert'), N'your password') 56 | --,'darko.martinovic@outlook.com'; 57 | 58 | 59 | END 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 64 | ----------------------------------------------------------------Add default record in configuration table 65 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 66 | IF ( SELECT COUNT(*) FROM [Email].[Configurations] ) = 0 67 | BEGIN 68 | INSERT INTO [Email].[Configurations] (name) 69 | SELECT 70 | 'default'; 71 | END 72 | 73 | 74 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 75 | ----------------------------------------------------------------Transfer to email schema 76 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 77 | IF EXISTS ( SELECT * 78 | FROM sysobjects 79 | WHERE id = object_id(N'[EMAIL].[CLRSendMail]') 80 | and OBJECTPROPERTY(id, N'IsProcedure') = 1 ) 81 | BEGIN 82 | DROP PROCEDURE [EMAIL].[CLRSendMail] 83 | END 84 | 85 | ALTER SCHEMA EMAIL TRANSFER dbo.CLRSendMail; 86 | 87 | --Transfer function 88 | IF EXISTS ( SELECT * 89 | FROM sysobjects 90 | WHERE id = object_id(N'[EMAIL].[CustomSendMailHelp]') 91 | and type = N'FT' ) 92 | BEGIN 93 | DROP FUNCTION [EMAIL].[CustomSendMailHelp] 94 | END 95 | 96 | ALTER SCHEMA EMAIL TRANSFER dbo.CustomSendMailHelp; 97 | 98 | 99 | 100 | --Transfer function 101 | IF EXISTS (SELECT 102 | * 103 | FROM sysobjects 104 | WHERE id = OBJECT_ID(N'[EMAIL].[CleanMemory]') 105 | AND type = N'FS') 106 | BEGIN 107 | DROP FUNCTION [EMAIL].[CleanMemory] 108 | END 109 | 110 | ALTER SCHEMA EMAIL TRANSFER dbo.CleanMemory; 111 | 112 | 113 | 114 | --Transfer function QueryToHtml 115 | IF EXISTS (SELECT 116 | * 117 | FROM sysobjects 118 | WHERE id = OBJECT_ID(N'[EMAIL].[QueryToHtml]') 119 | AND type = N'FS') 120 | BEGIN 121 | DROP FUNCTION [EMAIL].[QueryToHtml] 122 | END 123 | 124 | ALTER SCHEMA EMAIL TRANSFER dbo.QueryToHtml; 125 | 126 | 127 | --Transfer function ConCatHtml 128 | IF EXISTS (SELECT 129 | * 130 | FROM sysobjects 131 | WHERE id = OBJECT_ID(N'[EMAIL].[ConCatHtml]') 132 | AND type = N'FS') 133 | BEGIN 134 | DROP FUNCTION [EMAIL].[ConCatHtml] 135 | END 136 | 137 | ALTER SCHEMA EMAIL TRANSFER dbo.ConCatHtml; 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 143 | ----------------------------------------------------------------Alter authorization 144 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 145 | ALTER AUTHORIZATION ON ASSEMBLY::[SimpleTalk.SQLCLR.SendMail] TO [UserSqlClrCustomSendMail] -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Database/PreDeploy.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Post-Deployment Script Template 3 | -------------------------------------------------------------------------------------- 4 | This file contains SQL statements that will be appended to the build script. 5 | Use SQLCMD syntax to include a file in the post-deployment script. 6 | Example: :r .\myfile.sql 7 | Use SQLCMD syntax to reference a variable in the post-deployment script. 8 | Example: :setvar TableName MyTable 9 | SELECT * FROM [$(TableName)] 10 | -------------------------------------------------------------------------------------- 11 | */ 12 | 13 | USE MASTER 14 | GO 15 | --Copy snk from VS solution to path visible for your sql server instance 16 | --Replace this path value, you password as you want 17 | IF NOT EXISTS (SELECT * FROM sys.asymmetric_keys WHERE name = N'keySendMail') 18 | BEGIN 19 | CREATE ASYMMETRIC KEY keySendMail 20 | FROM FILE = 'D:\VS2017_PROJECTS\SqlClrCustomSendMail\SqlClrCustomSendMail\keySqlClrCustomSendMail.snk' 21 | ENCRYPTION BY PASSWORD = '@Str0ngP@$$w0rd' 22 | END 23 | GO 24 | 25 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Database/SchemaDefinition.sql: -------------------------------------------------------------------------------- 1 |  2 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 3 | ----------------------------------------------------------------Create schema 4 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 5 | IF NOT EXISTS ( 6 | SELECT schema_name 7 | FROM information_schema.schemata 8 | WHERE schema_name = 'EMAIL' ) 9 | 10 | BEGIN 11 | EXEC sp_executesql N'CREATE SCHEMA EMAIL' 12 | END 13 | 14 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 15 | ----------------------------------------------------------------Create table profiles 16 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 17 | IF NOT EXISTS (SELECT * 18 | FROM sys.objects 19 | WHERE object_id = OBJECT_ID(N'[Email].[Profiles]') 20 | AND type IN (N'U')) 21 | BEGIN 22 | CREATE TABLE [Email].[Profiles] ( [ProfileName ] [char](20) NOT NULL 23 | , [EnableSsl] [bit] NOT NULL DEFAULT 1 24 | , [DefaultCred] [bit] NOT NULL DEFAULT 0 25 | , [HostName] [nvarchar](50) NOT NULL DEFAULT 'smtp.gmail.com' 26 | , [Port] [int] NOT NULL DEFAULT 587 27 | , [UserName] [nvarchar](200) NULL 28 | , [Password] [nvarchar](200) NULL 29 | , [DOMAIN] [nvarchar](200) NULL --Exchange domain name 30 | , [DefaultFrom] [nvarchar](500) NOT NULL --Default from address 31 | , [DefaultGroup] [nvarchar](100) NULL --Default displayName 32 | , [DeliveryMethod][tinyint] NOT NULL DEFAULT 0 33 | , [TimeOut] [int] NOT NULL DEFAULT 100000 34 | , CONSTRAINT [PK_Email_Profile_ProfileName] PRIMARY KEY CLUSTERED 35 | ( 36 | [ProfileName] ASC 37 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] 38 | --DROP TABLE [Email].[Profiles]; 39 | END 40 | GO 41 | ------------------------------------Use these columns definition if you decide to apply T-SQL encryption 42 | --, [UserName] [varbinary](128) NOT NULL 43 | --, [Password] [varbinary](128) NOT NULL 44 | --, [DOMAIN] [varbinary](128) NULL --Exchange domain name 45 | 46 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 47 | ----------------------------------------------------------------Create table configurations 48 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 49 | IF NOT EXISTS (SELECT * 50 | FROM sys.objects 51 | WHERE object_id = OBJECT_ID(N'[Email].[Configurations]') 52 | AND type IN (N'U')) 53 | BEGIN 54 | CREATE TABLE [Email].[Configurations] ( [Name] [char](20) NOT NULL 55 | , [MaxFileSize] [int] NOT NULL DEFAULT 1000000 56 | , [ProhibitedExtensions] [nvarchar](50) NOT NULL DEFAULT 'exe,dll,vbs,js' 57 | , [LoggingLevel] [tinyint] NOT NULL DEFAULT 0 58 | , [SaveEmails] [bit] NOT NULL DEFAULT 1 59 | , [SendAsync] [bit] NOT NULL DEFAULT 1 60 | , [NoPiping] [bit] NOT NULL DEFAULT 1 61 | , [SaveAttachments] [bit] NOT NULL DEFAULT 1 62 | , CONSTRAINT [PK_Email_Configurations_Name] PRIMARY KEY CLUSTERED 63 | ( 64 | [Name] ASC 65 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] 66 | --DROP TABLE [Email].[Configurations]; 67 | END 68 | GO 69 | 70 | 71 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 72 | ----------------------------------------------------------------Create table monitorlog 73 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 74 | IF NOT EXISTS (SELECT * 75 | FROM sys.objects 76 | WHERE object_id = OBJECT_ID(N'[Email].[MonitorLog]') 77 | AND type IN (N'U')) 78 | BEGIN 79 | CREATE TABLE [Email].[MonitorLog] ( [ID] [bigint] IDENTITY(1,1) NOT NULL 80 | , [TimeStamp] [datetime] NOT NULL DEFAULT(GETDATE()) 81 | , [Source] [nvarchar](50) NOT NULL 82 | , [Type] [nvarchar](20) NOT NULL 83 | , [Message] [nvarchar](100) NOT NULL 84 | , [Data] [nvarchar](MAX) NULL 85 | , CONSTRAINT [PK_Email_MonitorLog_Id] PRIMARY KEY CLUSTERED 86 | ( 87 | [ID] ASC 88 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] 89 | --DROP TABLE [Email].[MonitorLog]; 90 | END 91 | GO 92 | 93 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 94 | ----------------------------------------------------------------Create table mail items 95 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 96 | IF NOT EXISTS (SELECT * 97 | FROM sys.objects 98 | WHERE object_id = OBJECT_ID(N'[Email].[MailItems]') 99 | AND type in (N'U')) 100 | BEGIN 101 | CREATE TABLE [Email].[MailItems]( [mailitem_id] [bigint] IDENTITY(1,1) NOT NULL, 102 | [profileName] [char](20) NULL, 103 | [configurationName] [char](20) NULL, 104 | [recipients] [varchar](max) NULL, 105 | [copy_recipients] [varchar](max) NULL, 106 | [blind_copy_recipients] [varchar](max) NULL, 107 | [subject] [nvarchar](255) NULL, 108 | [from_address] [varchar](max) NULL, 109 | [reply_to] [varchar](max) NULL, 110 | [body] [nvarchar](max) NULL, 111 | [body_format] [varchar](20) NULL, 112 | [importance] [varchar](6) NULL, 113 | [sensitivity] [varchar](50) NULL, 114 | [file_attachments] [nvarchar](max) NULL, 115 | [last_mod_date] [datetime] NOT NULL DEFAULT (GETDATE()), 116 | [last_mod_user] [sysname] NOT NULL DEFAULT (SUSER_SNAME()), 117 | CONSTRAINT [PK_Email_MailItems_MailId] PRIMARY KEY CLUSTERED 118 | ( 119 | [mailitem_id] ASC 120 | ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] 121 | END 122 | GO 123 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 124 | ----------------------------------------------------------------Add foreign keys 125 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 126 | 127 | IF NOT EXISTS (SELECT 128 | * 129 | FROM sys.objects o 130 | WHERE o.object_id = OBJECT_ID(N'[EMAIL].[FK_MailItems_Profile]') 131 | AND OBJECTPROPERTY(o.object_id, N'IsForeignKey') = 1) 132 | BEGIN 133 | ALTER TABLE [EMAIL].[MailItems] 134 | WITH CHECK ADD CONSTRAINT [FK_MailItems_Profile] 135 | FOREIGN KEY ([profileName]) 136 | REFERENCES [EMAIL].[Profiles] ([ProfileName]) 137 | END 138 | 139 | IF NOT EXISTS (SELECT 140 | * 141 | FROM sys.objects o 142 | WHERE o.object_id = OBJECT_ID(N'[EMAIL].[FK_MailItems_Configurations]') 143 | AND OBJECTPROPERTY(o.object_id, N'IsForeignKey') = 1) 144 | BEGIN 145 | ALTER TABLE [EMAIL].[MailItems] 146 | WITH CHECK ADD CONSTRAINT [FK_MailItems_Configurations] 147 | FOREIGN KEY ([configurationName]) 148 | REFERENCES [EMAIL].[Configurations] ([Name]) 149 | END 150 | 151 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 152 | ----------------------------------------------------------------Create table mail attachments 153 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 154 | IF NOT EXISTS (SELECT * 155 | FROM sys.objects 156 | WHERE object_id = OBJECT_ID(N'[EMail].[MailAttachments]') 157 | AND type in (N'U')) 158 | BEGIN 159 | CREATE TABLE [EMail].[MailAttachments]( 160 | [attachment_id] [bigint] IDENTITY(1,1) NOT NULL, 161 | [mailitem_id] [bigint] NOT NULL, 162 | [filename] [nvarchar](512) NOT NULL, 163 | [filesize] [bigint] NOT NULL, 164 | [attachment] [varbinary](max) NULL, 165 | [last_mod_date] [datetime] NOT NULL DEFAULT (GETDATE()), 166 | [last_mod_user] [sysname] NOT NULL DEFAULT (SUSER_SNAME()), 167 | CONSTRAINT [PK_Email_MailAttachments_AttachmentId] PRIMARY KEY CLUSTERED 168 | ( 169 | [attachment_id] ASC 170 | ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) 171 | 172 | END 173 | GO 174 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 175 | ----------------------------------------------------------------Add foreign keys 176 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 177 | 178 | IF NOT EXISTS (SELECT 179 | * 180 | FROM sys.objects o 181 | WHERE o.object_id = OBJECT_ID(N'[EMAIL].[FK_MailAttachments_MailItems]') 182 | AND OBJECTPROPERTY(o.object_id, N'IsForeignKey') = 1) 183 | BEGIN 184 | ALTER TABLE [EMAIL].[MailAttachments] 185 | WITH CHECK ADD CONSTRAINT [FK_MailAttachments_MailItems] 186 | FOREIGN KEY ([mailitem_id]) 187 | REFERENCES [EMAIL].[MailItems] ([mailitem_id]) 188 | END 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 193 | ----------------------------------------------------------------Create type for attachments 194 | -------------------------------------------------------------------------------------------------------------------------------------------------------- 195 | IF NOT EXISTS (SELECT 196 | * 197 | FROM sys.types st 198 | JOIN sys.schemas ss 199 | ON st.schema_id = ss.schema_id 200 | WHERE st.name = N'TVP_Emails' 201 | AND ss.name = N'EMail') 202 | CREATE TYPE [EMail].[TVP_Emails] AS TABLE 203 | ( 204 | [FileName] [nvarchar](260) NOT NULL, 205 | [FileSize] [bigint] NOT NULL, 206 | [Attachment] [varbinary](max) NOT NULL 207 | ) 208 | GO 209 | 210 | 211 | USE MASTER 212 | GO 213 | --Copy snk from VS solution to path visible for your sql server instance 214 | --Replace this path value, you password as you want 215 | IF NOT EXISTS (SELECT * FROM sys.asymmetric_keys WHERE name = N'keySendMail') 216 | BEGIN 217 | CREATE ASYMMETRIC KEY keySendMail 218 | FROM FILE = $(keyPath) 219 | ENCRYPTION BY PASSWORD = '@Str0ngP@$$w0rd' 220 | END 221 | GO 222 | 223 | --Use database where your installed the assembly 224 | USE [$(DatabaseName)]; 225 | GO 226 | 227 | --Create login if not exists 228 | IF NOT EXISTS (SELECT loginname 229 | FROM master.dbo.syslogins 230 | WHERE name = 'SqlClrCustomSendMail') 231 | BEGIN 232 | CREATE LOGIN SqlClrCustomSendMail FROM ASYMMETRIC KEY keySendMail 233 | END 234 | GO 235 | 236 | USE MASTER 237 | GO 238 | --Grant rights to newly create login 239 | GRANT UNSAFE ASSEMBLY TO SqlClrCustomSendMail; 240 | 241 | USE [$(DatabaseName)]; 242 | GO 243 | --Create user 244 | IF NOT EXISTS (SELECT name 245 | FROM sys.database_principals 246 | WHERE name = 'UserSqlClrCustomSendMail') 247 | BEGIN 248 | CREATE USER UserSqlClrCustomSendMail FOR LOGIN SqlClrCustomSendMail 249 | END 250 | 251 | 252 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Extensions/MailMessageExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Mail; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace SqlClrCustomSendMail 5 | { 6 | public static class MailMessageExtension 7 | { 8 | public static string HeaderInformation(this MailMessage mm) 9 | { 10 | return $@"From : '{mm.From.Address.Trim()}' 11 | To : '{mm.To[0].Address.Trim()}' 12 | Subject : '{mm.Subject.Trim()}'"; 13 | } 14 | public static string Recipiens(this MailMessage mm) 15 | { 16 | var retValue = string.Empty; 17 | foreach (var eml in mm.To) 18 | { 19 | retValue += eml.Address + ";"; 20 | } 21 | return retValue; 22 | } 23 | 24 | public static string CopyRecipiens(this MailMessage mm) 25 | { 26 | string retValue = null; 27 | foreach (var eml in mm.CC) 28 | { 29 | if (retValue == null) 30 | retValue = string.Empty; 31 | retValue += eml.Address + ";"; 32 | } 33 | return retValue; 34 | } 35 | public static string BccCopyRecipiens(this MailMessage mm) 36 | { 37 | string retValue = null; 38 | foreach (var eml in mm.Bcc) 39 | { 40 | if (retValue == null) 41 | retValue = string.Empty; 42 | retValue += eml.Address + ";"; 43 | } 44 | return retValue; 45 | } 46 | 47 | 48 | public static string ReplyToAddresses(this MailMessage mm) 49 | { 50 | string retValue = null; 51 | foreach (var eml in mm.ReplyToList) 52 | { 53 | if (retValue == null) 54 | retValue = string.Empty; 55 | retValue += eml.Address + ";"; 56 | } 57 | return retValue; 58 | } 59 | 60 | 61 | public static string AttachmentsPath(this MailMessage mm) 62 | { 63 | string retValue = null; 64 | foreach (var eml in mm.Attachments) 65 | { 66 | if (retValue == null) 67 | retValue = string.Empty; 68 | retValue += eml.Name + ";"; 69 | } 70 | return retValue; 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | } 80 | } 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/CleanMemory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlTypes; 3 | 4 | public partial class UserDefinedFunctions 5 | { 6 | [Microsoft.SqlServer.Server.SqlFunction] 7 | public static SqlString CleanMemory() 8 | { 9 | for (int i = 0; i <= GC.MaxGeneration; i++) 10 | { 11 | #if NET_4_5 12 | GC.Collect(i, GCCollectionMode.Forced,true,true); 13 | #elif NET_4_0 14 | GC.Collect(i, GCCollectionMode.Forced,true); 15 | #elif NET_3_5 16 | GC.Collect(i, GCCollectionMode.Forced); 17 | #else 18 | GC.Collect(i, GCCollectionMode.Forced); 19 | #endif 20 | 21 | 22 | } 23 | GC.WaitForPendingFinalizers(); 24 | return new SqlString(string.Empty); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/ConCatHtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlTypes; 3 | using System.Xml.Linq; 4 | using Microsoft.SqlServer.Server; 5 | // ReSharper disable RedundantAssignment 6 | 7 | // ReSharper disable once UnusedMember.Global 8 | // ReSharper disable once CheckNamespace 9 | public partial class UserDefinedFunctions 10 | { 11 | 12 | [SqlFunction(Name = "ConCatHtml")] 13 | public static SqlString ConCatHtml 14 | ( 15 | SqlString mHtml, //main html 16 | SqlString sHtml //add on html 17 | ) 18 | { 19 | string retValue = string.Empty; 20 | try 21 | { 22 | var mainHtml = mHtml.Value; 23 | var addOnHtml = sHtml.Value; 24 | var styleAddOnName = DetermineStyleName(ExtractStyle(addOnHtml)); 25 | var doc = XDocument.Parse(mainHtml); 26 | if (styleAddOnName.Equals(string.Empty) == false) 27 | { 28 | if (mainHtml.Contains(styleAddOnName) == false) 29 | { 30 | var mainStyle = ExtractStyle(mainHtml); 31 | var style = doc.Element("html")?.Element("head")?.Element("style"); 32 | var addonStyle = ExtractStyle(addOnHtml); 33 | style?.Add(addonStyle); 34 | } 35 | } 36 | var body = doc.Element("html")?.Element("body"); 37 | var addOn = ExtractBodyElement(addOnHtml); 38 | if (body != null) 39 | { 40 | body.LastNode.AddAfterSelf(addOn.Descendants("div")); 41 | retValue = doc.ToString(); 42 | doc = null; 43 | } 44 | 45 | // ReSharper disable once RedundantAssignment 46 | body = null; 47 | // ReSharper disable once RedundantAssignment 48 | addOn = null; 49 | mainHtml = null; 50 | addOnHtml = null; 51 | styleAddOnName = null; 52 | mHtml = null; 53 | sHtml = null; 54 | 55 | 56 | //Not allowed! Require change the assembly permission level to UNSAFE 57 | //GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; 58 | GC.Collect(); 59 | //for (int i = 0; i <= GC.MaxGeneration; i++) 60 | //{ 61 | // GC.Collect(i, GCCollectionMode.Forced); 62 | 63 | //} 64 | GC.WaitForPendingFinalizers(); 65 | 66 | //GC.WaitForPendingFinalizers(); 67 | //long memUsed2 = GC.GetTotalMemory(true); 68 | //if (memUsed2 != memUsed) 69 | //{ 70 | // //if (Debugger.IsAttached) 71 | // // Debugger.Break(); 72 | //} 73 | // Collect all generations of memory. 74 | 75 | } 76 | catch (Exception ex) 77 | { 78 | retValue = ex.Message; 79 | } 80 | return retValue; 81 | 82 | } 83 | 84 | 85 | private static string ExtractStyle(string tester) 86 | { 87 | var result = ""; 88 | var doc = XDocument.Parse(tester); 89 | var style = doc.Element("html")?.Element("head")?.Element("style"); 90 | if (style != null) 91 | { 92 | result = style.Value; 93 | doc = null; 94 | } 95 | 96 | style = null; 97 | return result; 98 | 99 | } 100 | 101 | private static string DetermineStyleName(string style) 102 | { 103 | var styleName = "unknowen"; 104 | 105 | if (style.Contains("table")) 106 | { 107 | var tester = style.Split(new[] { "table" }, StringSplitOptions.RemoveEmptyEntries); 108 | var valueString = tester[0]; 109 | styleName = valueString.Substring(valueString.IndexOf(".", StringComparison.Ordinal), valueString.Length - valueString.IndexOf(".", StringComparison.Ordinal)).Trim(); 110 | } 111 | return styleName; 112 | 113 | } 114 | 115 | private static XElement ExtractBodyElement(string tester) 116 | { 117 | var doc = XDocument.Parse(tester); 118 | return doc.Element("html")?.Element("body"); 119 | } 120 | 121 | 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/CustomSendMailHelp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlTypes; 3 | using Microsoft.SqlServer.Server; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | 8 | 9 | 10 | // ReSharper disable once CheckNamespace 11 | public partial class UserDefinedFunctions 12 | { 13 | [SqlFunction(Name = "CustomSendMailHelp", 14 | FillRowMethodName = "ConfigTable_FillRow", 15 | DataAccess = DataAccessKind.None, 16 | IsDeterministic = true, 17 | TableDefinition = @"ParametarName nvarchar(100), ParametarDescription_______________________________________________________________________________________________________ nvarchar(max)")] 18 | // ReSharper disable once UnusedMember.Global 19 | public static IEnumerable CustomSendMailHelp( 20 | SqlString procedureName // 21 | ) 22 | { 23 | Dictionary list = GetList(procedureName.Value); 24 | 25 | return list; 26 | 27 | } 28 | // ReSharper disable once UnusedMember.Local 29 | private static void ConfigTable_FillRow(object obj, out SqlString parametarName, out SqlString parametarDescription) 30 | { 31 | parametarName = ((KeyValuePair)(obj)).Key; 32 | parametarDescription = ((KeyValuePair)(obj)).Value; 33 | } 34 | 35 | private static Dictionary GetList(string procedureName) 36 | { 37 | var data = new Dictionary(); 38 | if (procedureName.ToLower().Equals("email.clrsendmail")) 39 | { 40 | 41 | 42 | 43 | 44 | 45 | //@group[nvarchar](4000) = N'''', 46 | 47 | //@requestReadReceipt[bit] = True, 48 | //@deliveryNotification[smallint] = 1, 49 | //@configName[nvarchar](20) = N'''' 50 | 51 | 52 | data.Add("@profileName", "Is the name of the profile to send the message from. Can be built in or user defined(readable from database)." + 53 | " @profileName must be specified."); 54 | 55 | data.Add("@mailTo", "Is a semicolon-delimited list of e-mail addresses to send the message to."); 56 | 57 | data.Add("@mailSubject", "Is the subject of the e-mail message. The subject is of type nvarchar(255). If no subject is specified, the default is 'SQLCLR Server Message'"); 58 | 59 | data.Add("@mailBody", "Is the body of the e-mail message. The message body is of type nvarchar(max), with a default of NULL"); 60 | 61 | data.Add("@fromAddress", "Is the value of the 'from address' of the email message. This is an optional parameter used to override the settings in the mail profile. " + 62 | " SMTP security settings determine if these overrides are accepted. If no parameter is specified, the default is NULL. "); 63 | 64 | data.Add("@displayName", "Display name. This parameter is connected with parameter @fromAddress"); 65 | 66 | data.Add("@mailCC", "Is a semicolon-delimited list of e-mail addresses to carbon copy the message to."); 67 | 68 | data.Add("@blindCopyRec", "Is a semicolon-delimited list of e-mail addresses to blind carbon copy the message to."); 69 | 70 | data.Add("@replyAddress", "Is the value of the 'reply to address' of the email message. It accepts only one email " + 71 | "address as a valid value. This is an optional parameter used to override the settings in the mail profile."); 72 | 73 | data.Add("@fileAttachments", "Is a semicolon-delimited list of file names to attach to the e-mail message. Files in the list must " + 74 | "be specified as absolute paths. The attachments list is of type nvarchar(max). By default, SQLCLR Mail limits file attachments to 1 MB per file"); 75 | 76 | //Not implemented 77 | data.Add("@requestReadReceipt", "Request read receipt. Not implemented in sp_send_dbmail"); 78 | 79 | data.Add("@deliveryNotification", "Delivery notification.Not implemented in sp_send_dbmail."); 80 | //end 81 | 82 | data.Add("@sensitivity", "Is the sensitivity of the message. " + 83 | "The parameter may contain one of the following values: Normal,Personal,Private & Confidential "); 84 | 85 | data.Add("@mailPriorty", "Is the importance of the message." + 86 | "The parameter may contain one of the following values: Low,Normal & High "); 87 | 88 | 89 | data.Add("@bodyHtml", "Is the format of the message body" + 90 | "Can be HTML or TEXT"); 91 | 92 | data.Add("@configName", "Is the name of the configuration. " + 93 | " Configuration is responsible for protecting system of large file attachements"); 94 | } 95 | else if (procedureName.ToLower().Equals("email.querytohtml")) 96 | { 97 | data.Add("@Query", "Query or stored procedure which we will to transform into HTML. Stored procedure always should begin with keyword EXEC."); 98 | data.Add("@Params", "Query or stored procedure parametars"); 99 | data.Add("@Caption", "Table header"); 100 | data.Add("@Footer", "Table footer. Number of records will be printed when you pass #"); 101 | data.Add("@Rm", "0-means no rotation,1-auto rotation if number of columns is greater then number of rows, 2-means always rotate"); 102 | data.Add("@Rco", "Rotate column ordinal. Valid value is for -1 to max column value. If you specify @Rm parametar = 0 @Rco parametar is ignored."); 103 | data.Add("@style", "There are 7 predefined styles, namely StBlue, ST_BLACK, ST_BROWN, ST_ROSE,ST_RED,ST_GREEN and StSimple. You can pass your custom css stylesheet as well."); 104 | } 105 | else if (procedureName.ToLower().Equals("email.concathtml")) //ConCatHtml 106 | { 107 | data.Add("@mHtml", "Main html string. Should be well formatted. It means should have html, head and body tags."); 108 | data.Add("@sHtml", "Html string we want to merge with the main html string"); 109 | } 110 | else 111 | { 112 | data.Add("Error", "Procedure or function " + procedureName + " does not exists!"); 113 | } 114 | 115 | for (int i = 0; i <= GC.MaxGeneration; i++) 116 | { 117 | #if NET_4_5 118 | GC.Collect(i, GCCollectionMode.Forced,true,true); 119 | #elif NET_4_0 120 | GC.Collect(i, GCCollectionMode.Forced,true); 121 | #elif NET_3_5 122 | GC.Collect(i, GCCollectionMode.Forced); 123 | #else 124 | GC.Collect(i, GCCollectionMode.Forced); 125 | #endif 126 | } 127 | 128 | GC.WaitForPendingFinalizers(); 129 | 130 | return data; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/Functions/QueryToHtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlTypes; 3 | using Microsoft.SqlServer.Server; 4 | using SqlClrCustomSendMail; 5 | using System.Data.SqlClient; 6 | using System.Collections.Generic; 7 | // ReSharper disable RedundantAssignment 8 | 9 | // ReSharper disable once CheckNamespace 10 | public partial class UserDefinedFunctions 11 | { 12 | 13 | 14 | [SqlFunction( 15 | Name = "QueryToHtml", 16 | DataAccess = DataAccessKind.Read, 17 | SystemDataAccess = SystemDataAccessKind.Read, 18 | IsDeterministic = true)] 19 | public static SqlString QueryToHtml 20 | ( 21 | SqlString query, //query we passed 22 | [SqlFacet(IsNullable = true, MaxSize = 4000)] 23 | SqlString Params, 24 | [SqlFacet(IsNullable = true, MaxSize = 200)] 25 | SqlString caption, //caption 26 | [SqlFacet(IsNullable = true, MaxSize = 1000)] 27 | SqlString footer, //footer 28 | [SqlFacet(IsNullable = true, MaxSize = 4)] 29 | SqlInt16 rm, //rotate mode 30 | [SqlFacet(IsNullable = true, MaxSize = 4)] 31 | SqlInt16 rco, //rotate column ordinal 32 | [SqlFacet(IsNullable = true, MaxSize = 4000)] 33 | SqlString style 34 | ) 35 | { 36 | 37 | 38 | var html = ""; 39 | var errorString = ""; 40 | var foo = (RotateMode)Enum.Parse(typeof(RotateMode), rm.ToString()); 41 | 42 | var mySp = false; 43 | var queryValue = query.Value; 44 | 45 | //Make query parametars 46 | SqlParameter[] listOfParams = null; 47 | 48 | 49 | 50 | 51 | 52 | //Dictionary ds = DataAccess.GetData(queryValue, mySp, listOfParams, ref html); 53 | DataAccess da; 54 | var splitQueries = queryValue.Split(';'); 55 | List ds; 56 | var counter = 0; 57 | 58 | foreach (var singleQuery in splitQueries) 59 | { 60 | if (singleQuery.Trim() == "") 61 | continue; 62 | var queryToSend = singleQuery; 63 | if (singleQuery.ToUpper().StartsWith("EXEC")) 64 | { 65 | mySp = true; 66 | queryToSend = singleQuery.Substring("EXEC".Length, query.Value.Length - "EXEC".Length).Trim(); 67 | } 68 | da = new DataAccess(); 69 | if (Params.Value.Equals(string.Empty) == false) 70 | listOfParams = DataAccess.MakeParams(Params.Value, ref html); 71 | 72 | ds = da.GetData(queryToSend, mySp, listOfParams, ref errorString); 73 | if (errorString.Length == 0) 74 | { 75 | var rotate = false; 76 | var myRco = 0; 77 | if ((foo == RotateMode.Force) || (foo == RotateMode.Auto && da.RowCount < da.FieldCount)) 78 | { 79 | rotate = true; 80 | if (rco.IsNull == false) 81 | myRco = rco.Value; 82 | } 83 | if (rotate == false) 84 | html += RenderArray.MakeTable(ds, caption.Value, footer.Value, style.Value, da.FieldCount, da.RowCount, counter, splitQueries.Length - 1); 85 | else 86 | { 87 | if (myRco == -1) 88 | html += RenderArray.MakeTableRotateKeyValue(ds, caption.Value, footer.Value, style.Value, 89 | da.FieldCount, da.RowCount, counter, splitQueries.Length - 1); 90 | else 91 | html += RenderArray.MakeTableRotate(ds, caption.Value, footer.Value, style.Value, da.FieldCount, 92 | da.RowCount, myRco, counter, splitQueries.Length - 1); 93 | } 94 | } 95 | 96 | else 97 | { 98 | html = errorString; 99 | } 100 | counter++; 101 | ds = null; 102 | } 103 | try 104 | { 105 | ds = null; 106 | da = null; 107 | Params = null; 108 | queryValue = null; 109 | query = null; 110 | errorString = null; 111 | splitQueries = null; 112 | listOfParams = null; 113 | caption = null; 114 | footer = null; 115 | style = null; 116 | //GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.Default; 117 | GC.Collect(); 118 | GC.WaitForPendingFinalizers(); 119 | GC.Collect(); 120 | GC.WaitForPendingFinalizers(); 121 | 122 | 123 | } 124 | catch (Exception ex) 125 | { 126 | html += ex.Message; 127 | } 128 | return html; 129 | } 130 | 131 | 132 | private enum RotateMode 133 | { 134 | // ReSharper disable once UnusedMember.Local 135 | None = 0, 136 | Auto = 1, 137 | Force = 2 138 | } 139 | 140 | 141 | } 142 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/HtmlSupport/DetermineStyle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | // ReSharper disable once CheckNamespace 3 | namespace SqlClrCustomSendMail 4 | { 5 | internal static class DetermineStyle 6 | { 7 | private const string StRed = "ST_RED"; 8 | private const string StGreen = "ST_GREEN"; 9 | private const string StRose = "ST_ROSE"; 10 | private const string StBrown = "ST_BROWN"; 11 | private const string StBlack = "ST_BLACK"; 12 | public const string StBlue = "ST_BLUE"; 13 | private const string StSimple = "ST_SIMPLE"; 14 | private const string StNoStyle = "ST_NOSTYLE"; 15 | 16 | 17 | public static string DetermineStyleName(string style) 18 | { 19 | var styleName = "unknowen"; 20 | string[] tester; 21 | 22 | if (style.Contains("table.")) 23 | { 24 | tester = style.Split(new[] { "table." }, StringSplitOptions.RemoveEmptyEntries); 25 | var valueString = tester[1]; 26 | styleName = valueString.Substring(0, valueString.IndexOf("{", StringComparison.Ordinal)).Trim(); 27 | // ReSharper disable once RedundantAssignment 28 | valueString = null; 29 | } 30 | 31 | // ReSharper disable once RedundantAssignment 32 | tester = null; 33 | return styleName; 34 | 35 | } 36 | 37 | 38 | 39 | public static string GetStyle(string style) 40 | { 41 | var defaultStyle = @" 42 | 123 | 124 | "; 125 | 126 | switch (style) 127 | { 128 | //Apply default style 129 | case StRed: 130 | defaultStyle = @" 131 | "; 208 | break; 209 | case StGreen: 210 | defaultStyle = @" 211 | "; 283 | break; 284 | case StRose: 285 | defaultStyle = @" 286 | "; 362 | break; 363 | case StBlack: 364 | defaultStyle = @" 365 | "; 440 | break; 441 | case StBrown: 442 | defaultStyle = @" 443 | "; 515 | break; 516 | case StSimple: 517 | defaultStyle = @" 518 | 559 | "; 560 | break; 561 | case StNoStyle: 562 | defaultStyle = @" 563 | "; 567 | break; 568 | default: 569 | if (style != "" && style != StBlue) 570 | { 571 | defaultStyle = style; 572 | } 573 | 574 | break; 575 | } 576 | return defaultStyle; 577 | 578 | } 579 | 580 | } 581 | } 582 | 583 | -------------------------------------------------------------------------------- /SqlClrCustomSendMail-master/SqlClrCustomSendMail/HtmlSupport/RenderArray.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace SqlClrCustomSendMail 6 | { 7 | internal static class RenderArray 8 | { 9 | public static string MakeTable(List dt, string caption, string footer, string style, int maxColumn, 10 | int maxCounter, int counter, int lastCounter) 11 | { 12 | 13 | var html = new StringBuilder(); 14 | var isCustomStyle = style.Contains("