Why CLR Database User Defined Scalar and Table Value Function

Introduction

In this article, we will learn about CLR database user-defined scalar and table-valued function in C#.

Description

Why use CLR user-defined scalar and table-valued function instead of T-SQL user-defined scalar and table value function?
  1. If you'd prefer to write the code in (VB.Net, C#, etc.) than SQL.
  2. If you want to do complex operations on data in CLR.
  3. If you want to be close to the data (i.e. not in ASP.NET or a WinForms app etc.)
  4. The CLR has RegEx but SQL has not.
  5. You can call web services in CLR, but T-SQL does not.
  6. The CLR has better performance (if you had to do a lot of math on every row).
  7. You can re-use code.

CLR database user-defined scalar and table-valued function

This article will show how to create two types of CLR database functions,

  1. A user-defined scalar valued function that returns a single value.
  2. A user-defined table-valued function that returns a table.

Let’s see CLR database function practically:

Step 1

Create a library project (CLRProcedures) in visual studio as below.

C# 

Create one class files “CLRScalarAndTableValuedFunctions.cs”.

In file “CLRScalarAndTableValuedFunctions.cs” add the below lines of code.

  1. using Microsoft.SqlServer.Server;  
  2. using System.Collections;  
  3. using System.Data;  
  4. using System.Data.SqlClient;  
  5. using System.Data.SqlTypes;  
  6.   
  7. namespace CLRProcedures  
  8. {  
  9.     public class CLRScalarAndTableValuedFunctions  
  10.     {  
  11.         private class User  
  12.         {  
  13.             public int UserID { getset; }  
  14.             public string EmailAddress { getset; }  
  15.             public User(int userId, string emailAddress)  
  16.             {  
  17.                 this.UserID = userId;  
  18.                 this.EmailAddress = emailAddress;  
  19.             }  
  20.         }  
  21.   
  22.         [SqlFunction(DataAccess = DataAccessKind.Read)]  
  23.         public static int GetUserCountByEmail(string emailAddress)  
  24.         {  
  25.             using (SqlConnection connection = new SqlConnection("context connection=true"))  
  26.             {  
  27.                 connection.Open();  
  28.                 SqlCommand cmd = new SqlCommand(@"SELECT COUNT(*) FROM [dbo].[Users] WHERE EmailAddress=@EmailAddress", connection);  
  29.                 SqlParameter modifiedSinceParam = cmd.Parameters.Add("@EmailAddress", SqlDbType.NVarChar);  
  30.                 modifiedSinceParam.Value = emailAddress;  
  31.                 return (int)cmd.ExecuteScalar();  
  32.             }  
  33.         }  
  34.   
  35.         [SqlFunction(DataAccess = DataAccessKind.Read,  
  36.             FillRowMethodName = "FillRow")]  
  37.         public static IEnumerable GetUsersByEmail(string emailAddress)  
  38.         {  
  39.             ArrayList users = new ArrayList();  
  40.             using (SqlConnection connection = new SqlConnection("context connection=true"))  
  41.             {  
  42.                 connection.Open();  
  43.                 SqlCommand cmd = new SqlCommand(@"SELECT UserID,EmailAddress FROM [dbo].[Users] WHERE EmailAddress=@EmailAddress", connection);  
  44.                 SqlParameter modifiedSinceParam = cmd.Parameters.Add("@EmailAddress", SqlDbType.NVarChar);  
  45.                 modifiedSinceParam.Value = emailAddress;  
  46.                 using (SqlDataReader reader = cmd.ExecuteReader())  
  47.                 {  
  48.                     while (reader.Read())  
  49.                     {  
  50.                         users.Add(new User((int)reader.GetSqlInt32(0),(string)reader.GetSqlString(1)));  
  51.                     }  
  52.                 }  
  53.             }  
  54.             return users;  
  55.         }  
  56.         [SqlFunction(DataAccess = DataAccessKind.Read,  
  57.             FillRowMethodName = "FillRow",  
  58.             TableDefinition = "UserID int, EmailAddress nvarchar(150)")]  
  59.         public static IEnumerable GetUsersByEmailWithTableDefinition(string emailAddress)  
  60.         {  
  61.             ArrayList users = new ArrayList();  
  62.             using (SqlConnection connection = new SqlConnection("context connection=true"))  
  63.             {  
  64.                 connection.Open();  
  65.                 SqlCommand cmd = new SqlCommand(@"SELECT UserID,EmailAddress FROM [dbo].[Users] WHERE EmailAddress=@EmailAddress", connection);  
  66.                 SqlParameter modifiedSinceParam = cmd.Parameters.Add("@EmailAddress", SqlDbType.NVarChar);  
  67.                 modifiedSinceParam.Value = emailAddress;  
  68.                 using (SqlDataReader reader = cmd.ExecuteReader())  
  69.                 {  
  70.                     while (reader.Read())  
  71.                     {  
  72.                         users.Add(new User((int)reader.GetSqlInt32(0), (string)reader.GetSqlString(1)));  
  73.                     }  
  74.                 }  
  75.             }  
  76.             return users;  
  77.         }  
  78.         public static void FillRow(object user, out SqlInt32 UserID, out string EmailAddress)  
  79.         {  
  80.             User _user = (User)user;  
  81.             UserID = _user.UserID;  
  82.             EmailAddress = _user.EmailAddress;  
  83.   
  84.         }  
  85.     }  
  86. }  

Step 2

Open project property and set output path=C:\dlls as below,

C# 

Step 3

Build or compile library project and verify dll copied in output folder (C:\dlls).

Step 4

Open new query window for the targeted database in MSSQL Server Management Studio and add the below SQL statement in the query window to create a “Users” table with some data.

  1. CREATE TABLE [dbo].[Users](  
  2.     [UserID] [int] IDENTITY(1,1) NOT NULL,  
  3.     [UserName] [nvarchar](200) NOT NULL,  
  4.     [RealName] [nvarchar](200) NOT NULL,  
  5.     [EmailAddress] [varchar](150) NULL,  
  6.     [ModifiedDate] [dateNULL,  
  7.  CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED   
  8. (  
  9.     [UserID] ASC  
  10. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
  11. ON [PRIMARY]  
  12.   
  13. GO  
  14. SET ANSI_PADDING OFF  
  15. GO  
  16. SET IDENTITY_INSERT [dbo].[Users] ON   
  17.   
  18. GO  
  19. INSERT [dbo].[Users] ([UserID], [UserName], [RealName], [EmailAddress], [ModifiedDate]) VALUES (44, N'dinesh.singh', N'Dinesh', N'a@b.com'CAST(N'2018-05-15' AS Date))  
  20. GO  
  21. INSERT [dbo].[Users] ([UserID], [UserName], [RealName], [EmailAddress], [ModifiedDate]) VALUES (45, N'ramesh.singh', N'Ramesh', N'a@b.com'CAST(N'2018-05-15' AS Date))  
  22. GO  
  23. SET IDENTITY_INSERT [dbo].[Users] OFF  
  24. GO  

Step 5

Let’s create a CLR database scalar valued function that returns a value.

Open SQL management studio query window and write & execute below SQL statement to create scalar value function,

  1. CREATE ASSEMBLY StoredProcedures from 'c:\dlls\CLRProcedures.dll' WITH PERMISSION_SET = SAFE    
  2.   
  3. CREATE FUNCTION CLRGetUserCountByEmail(@emailAddress nvarchar(150))     
  4. RETURNS INT   
  5. AS EXTERNAL NAME StoredProcedures.[CLRProcedures.CLRScalarAndTableValuedFunctions].GetUserCountByEmail   

Note

First create assembly statement, after that run create function statement. Don’t run both at the same time otherwise you will get an error as below.

C# 

Let’s see whether CLR database assembly and function are created in SQL database object explorer or not.

C# 

From the above screen, CLR assembly and scalar valued function have been created in SQL database. Now let’s run the created CLR scalar valued function “CLRGetUserCountByEmail” and see the output.

C# 

Now you can see we are getting results from SQL database through CLR scalar value function “CLRGetUserCountByEmail”.

Step 6

Let’s create a CLR database table-valued function that returns a table.

Open SQL management studio query window and write & execute the below SQL statement to create the table-valued function,

  1. CREATE FUNCTION CLRGetUsersByEmail(@emailAddress nvarchar(150))     
  2. RETURNS TABLE (    
  3.    UserID int,    
  4.    EmailAddress nvarchar(150)    
  5. )    
  6. AS EXTERNAL NAME StoredProcedures.[CLRProcedures.CLRScalarAndTableValuedFunctions].GetUsersByEmail   

Let’s see whether CLR database table-valued function is created in SQL database object explorer or not.

C# 

Now it is clear that table-valued function has been created. Now let’s execute this table-valued function and see the result.

C# 

Now you can see we are getting results from SQL database through CLR database table valued function CLRGetUsersByEmail”.

Step 7

Let’s create a CLR database table-valued function that returns a table with custom table definition.

Open SQL management studio query window and write and execute the below SQL statement to create the table-valued function with custom table definition:

  1. CREATE FUNCTION CLRGetUsersByEmailWithTableDefinition(@emailAddress nvarchar(150))     
  2. RETURNS TABLE (    
  3.    UserID int,    
  4.    EmailAddress nvarchar(150)    
  5. )    
  6. AS EXTERNAL NAME StoredProcedures.[CLRProcedures.CLRScalarAndTableValuedFunctions].GetUsersByEmailWithTableDefinition   

Let’s see whether CLR database table-valued function is created in SQL database object explorer or not.

C# 

Now it is clear that table-valued function has been created. Now let’s execute this table-valued function and see the result.

C# 

Now you can see we are getting results from SQL database through CLR database table valued function “CLRGetUsersByEmailWithTableDefinition” with custom table definition. The result is the same as we were getting without table definition.

Points of interest

If you are getting an error as below while executing CLR database user-defined scalar and table-valued function,

 C#
 
Then you must run the below SQL statement to enable CLR.
  1. sp_configure 'show advanced options', 1;  
  2. GO  
  3. RECONFIGURE;  
  4. GO  
  5. sp_configure 'clr enabled', 1;  
  6. GO  
  7. RECONFIGURE;  
  8. GO  

Now you can run CLR procedure successfully.

To disable CLR integration, use the CLR enabled option of the sp_configure stored procedure.

  1. sp_configure 'show advanced options', 1;  
  2. GO  
  3. RECONFIGURE;  
  4. GO  
  5. sp_configure 'clr enabled', 0;  
  6. GO  
  7. RECONFIGURE;  
  8. GO  

To un-install CLR database scalar and table-valued function, you must write & execute below SQL statement in the MSSQL server management studio query window.

  1. IF EXISTS (SELECT name FROM sysobjects WHERE name = 'CLRGetUserCountByEmail')  
  2. DROP FUNCTION CLRGetUserCountByEmail  
  3.   
  4. IF EXISTS (SELECT name FROM sysobjects WHERE name = 'CLRGetUsersByEmail')  
  5. DROP FUNCTION CLRGetUsersByEmail  
  6.   
  7. IF EXISTS (SELECT name FROM sysobjects WHERE name = 'CLRGetUsersByEmailWithTableDefinition')  
  8. DROP FUNCTION CLRGetUsersByEmailWithTableDefinition  

To un-install CLR database assembly you must write & execute the below SQL statement in the MSSQL server management studio query window.

  1. IF EXISTS (SELECT name FROM sys.assemblies WHERE name = 'StoredProcedures')  
  2. DROP ASSEMBLY StoredProcedures  

Note
So far, I have not able to understand the uses of CLR database function attribute “TableDefinition = "UserID int, EmailAddress nvarchar(150)" but I promise as soon as I get a reasonable answer I will post an article on it.

Congratulation you have successfully created, installed and uninstalled CLR database user-defined scalar and table-valued function. If you have any query or concern just do let me know or put it in the comment box and I will respond as soon as possible. I am open to discussing anything, even silly questions as well. If you have any suggestion related to this article, please let me know and I promise I will improve this article to a maximum level.

Summary

In this article, we have learned how to create, install and uninstall CLR database user-defined scalar and table-valued function and assembly in C#.