|
CLR Integration With Microsoft SQL Server |
|
Fundamentals of CLR Integration |
|
|
Transact-SQL provides as much flexibility as possible
but, because it is not a real programming language, it has some shortcomings
about many issues (memory management, exception handling, debugging, file
processing (or streaming), object-orientation (classes), or thread
management). Transact-SQL also lacks features of normal libraries (arrays,
collections, serialization, expressions, or string manipulations, etc).
|
To enhance or complement the functionality of a
Microsoft SQL Server database, you can use an external language. One way
you can do this is to use a language that supports the .NET Framework.
This is done through the common language runtime (CLR) and referred to as
CLR integration.
Integration with the CLR allows you to create database
objects or operations using a high-level language such as C#, C++, or
Visual Basic. These languages support object-oriented programming with the
ability to create classes, populate them with properties and/or take
action with methods, control the access levels on members of a class,
inherit from a class, line-inherit from classes (parents, grand-parents,
etc), and control inheritance (polymorphism and abstraction), etc. Taking
advantage of the .NET Framework from these classes, your code can also
involve collections, dynamic libraries, etc, which are features not
available in Transact-SQL.
Once the code is ready, create a library, called an
assembly, then import and use it in Microsoft SQL Server.
Code in the common language runtime (CLR) is written
in a language that supports the .NET Framework. This makes it possible to
write code in one language and use it in another language. The language is
also in charge of memory management. For this, it is referred to as
managed code.
In order to use the CLR in a Microsoft SQL Server
database, you must enable it. By default, it is not (enabled).
|
Some of the instructions in this lesson assume
that you have Microsoft Visual Studio (or an Express edition)
installed on your computer. If you don't, you may have to compile
the projects from the Command Prompt.
|
Practical
Learning: Initializing the CLR
|
|
- Start Microsoft SQL Server and connect to the server
- On the Standard toolbar, click the New Query button
- In the Query window, type the following:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO
- Press F5 to execute
- To create a new database and a table, type the following:
-- ===================================================
-- Database: CeilInn5
-- ===================================================
IF EXISTS(SELECT name FROM sys.databases
WHERE name = N'CeilInn5')
DROP DATABASE CeilInn5;
GO
CREATE DATABASE CeilInn5;
GO
USE CeilInn5;
GO
IF OBJECT_ID(N'Employees', N'U') IS NOT NULL
DROP TABLE Employees
GO
CREATE TABLE Employees
(
EmployeeNumber int NOT NULL,
DateHired date NULL,
FirstName nvarchar(20),
LastName nvarchar(20) NOT NULL,
HourlySalary money
);
GO
INSERT INTO Employees
VALUES(62480, N'19981025', N'James', N'Haans', 28.02),
(35844, N'20060622', N'Gertrude', N'Monay', 14.36),
(24904, N'20011016', N'Philom�ne', N'Guillon', 18.05),
(48049, N'20090810', N'Eddie', N'Monsoon', 26.22),
(25805, N'20040310', N'Peter', N'Mukoko', 22.48),
(58405, N'19950616', N'Chritian', N'Allen', 16.45);
GO
IF OBJECT_ID('Rooms', 'U') IS NOT NULL
DROP TABLE Rooms
GO
CREATE TABLE Rooms
(
RoomNumber nvarchar(10),
RoomType nvarchar(20) default N'Bedroom',
BedType nvarchar(40) default N'Queen',
Rate money default 85.95,
Available bit default 1
);
GO
USE CeilInn5;
GO
INSERT INTO Rooms(RoomNumber)
VALUES(N'104');
GO
INSERT INTO Rooms(RoomNumber, BedType, Rate, Available)
VALUES(N'105', N'King', 95.50, 1),
(N'106', N'King', 95.50, 1);
GO
INSERT INTO Rooms(RoomNumber, Available)
VALUES(N'107', 1);
GO
INSERT INTO Rooms(RoomNumber, BedType, Rate)
VALUES(N'108', N'King', 95.50);
GO
INSERT INTO Rooms(RoomNumber, Available)
VALUES(N'109', 1);
GO
INSERT INTO Rooms(RoomNumber, RoomType, Rate, Available)
VALUES(N'108', N'Conference Roome', 450, 1),
(N'110', N'Conference Roome', 650, 1);
GO
INSERT INTO Rooms(RoomNumber, BedType, Rate, Available)
VALUES(N'201', N'Queen', 90, 1),
(N'202', N'Queen', 90, 1),
(N'203', N'Queen', 90, 1),
(N'205', N'King', 98.85, 1),
(N'207', N'King', 98.75, 1);
GO
- To execute, press F5
As stated already, to provide CLR integration, you
will write your code in a language that supports the .NET Framework. This
means that the language will use classes available from that library.
The .NET Framework primarily supports databases
through an object called a data set. This object is represented by a class
named DataSet. In reality, a data set is a group of lists exactly
like the tables of a Microsoft SQL Server database. Normally, a data set
by itself means nothing. It is the objects (tables) it contains that
define it.
To support tables, the .NET Framework provides a class
named DataTable. As you know already, a table is made of columns
and records. The columns of a table are supported in the .NET Framework by
a class named DataColumn. A record of a table is represented in the
.NET Framework by a class named DataRow. All these classes are
defined in a namespace named System.Data. The System.Data
namespaces is created in the System.Data.dll assembly. This means
you will need this library in your project.
To provide support for Microsoft SQL Server databases,
the .NET Framework provides the System.Data.Sql and the
System.Data.SqlClient namespaces. Both namespaces are defined in
the System.Data.dll library.
The System.Data.Sql contains classes
that specically support Microsoft SQL Server.
The System.Data.SqlClient namespace contains
classes used to establish a connection to a server, create commands to a
database, execute the desired commands, or read data from database
objects (tables, views, etc). To use a Microsoft SQL Server database, you
will usually need to reference the System.Data.SqlClient namespace.
One of the primary requirements of a database system
is that of data types. The .NET Framework data types that support, or are
equivalent to, Microsoft SQL Server values are defined in the
System.Data.SqlTypes namespace. This namespace contains various
classes and structures that each provides an equivalent to a Microsoft SQL
Server data type. Here are the data types we started using in Lesson 5 and
here are their .NET Framework equivalents:
Microsoft SQL Server Data Type |
.NET Framework Equivalent |
bit |
SqlBoolean |
tinyint |
SqlByte |
smallint |
SqlInt16 |
int |
SqlIn32 |
bigint |
SqlInt64 |
GUID |
SqlGuid |
float, real |
SqlSingle |
decimal, numeric |
SqlDecimal, SqlDouble |
money, smallmoney |
SqlMoney |
date, time, datetime, datetime2,
smalldatetime, datetimeoffset |
SqlDateTime |
char, nchar, varchar, nvarchar,
text, ntext |
SqlString: To perform comparisons
on strings, you can use the SqlCompareOptions enumeration |
FILESTREAM |
SqlFileStream |
binary, varbinary |
SqlBinary |
xml |
SqlXml |
The System.Data.SqlTypes
namespace contains many other structures you can use to implement
functionality available from object-oriented languages but not directly
available in Microsoft SQL Server:
- SqlBytes: This class can be used to create a reference to a
stream of values
- SqlChars: This class can be used for an array of characters
- SqlTruncateException: This exception is thrown if you use
an incompatible value that must be truncated
- SqlNullValueException: This exception is thrown if the
value of a variable is set to null (C#) or Nothing
(Visual Basic)
The System.Data.SqlTypes namespace is defined
in the System.Data.dll assembly.
To provide additional functionality of a Microsoft SQL
Server database, the .NET Framework provides the System.Data.Sql
namespace. This namespace is also part of the System.Data.dll
library.
Equipped with all these data types, classes, and your
knowledge, you can implement the functionality appropriate to integrate
your code with a Microsoft SQL Server database. To assist you with the
necessary classes for this integration, the .NET Framework provides the
Microsoft.SqlServer.Server namespace that is equipped with various
interfaces, classes, and enumerations. To use one of the classes of the
Microsoft.SqlServer.Server namespace, you can reference it using an
attribute.
Although the Transact-SQL library provides many
functions, you may need functionality that none of those provides. You can
create your own and fully efficient function in C#, C++/CLI, or Visual
Basic. A CLR scalar-valued function (SVF) or scalar-valued user-defined
function is a function you create using CLR and that is made to be
accessed from Microsoft SQL Server.
The C++, Visual Basic, Pascal, Transact-SQL and many
other languages support the concept of functions. Some other languages
like C# or Java don't share that concept. They use only the word "method".
Here, we will use the word function in the first sense. Because we will
write our code in C#, we have to create classes and use methods.
Creating a
User-Defined Function
|
|
To assist you with creating a function, the .NET
Framework provides a class named SqlFunction. The SqlFunction
class is defined in the Microsoft.SqlServer.Server namespace. To
use this class, create a static method marked with the
SqlFunctionAtribute attribute.
If you want, you can create a function that will
access one or more records on the database. To do this, use the
DataAccess property, which is based on an enumeration named
DataAccessKind.
To prepare the CLR object, you can create a project
using the language of your choice or a programming environment and compile
it to create a dynamic link library (DLL).
If you want to indicate that the function you are
defining is deterministic, mark it with the IsDeterministic
attribute and assign a value of true or false.
Practical
Learning: Creating a Library
|
|
- Start Microsoft Visual Studio
- To create a new project, on the main menu, click File -> New ->
Project (or File -> New Project)
- In the Project Types tree view, expand Visual C# if necessary.
Under Visual C#, click Windows
- In the Templates list view, click Empty Project
- In the Name text box, replace the string with CeilInn
- To save the project, on the Standard toolbar, click the Save
button
- Set the Name to CeilInn1
- Set the Location to C:\
- Uncheck Create Directory For Solution
- Click Save
- In the Solution Explorer, under CeilInn1, right-click References
-> Add Reference...
- In the .NET tab of the Add Reference dialog box, click System
- Press and hold Ctrl
- Click System.Data
- Release Ctrl
- Click OK
- In the Solution Explorer, right-click CeilInn1 -> Add -> New
Item...
- In the Templates list, click Code File
- Set the Name to CeilInn
- Click Add
- In the Code Editor, type the following:
using System;
using Microsoft.SqlServer.Server;
public class Payroll
{
[Microsoft.SqlServer.Server.SqlFunction]
public static void Calculate(decimal Week1Time,
decimal Week2Time,
decimal HourlySalary,
out decimal RegularTime,
out decimal RegularPay,
out decimal Overtime,
out decimal OvertimePay,
out decimal TotalEarnings)
{
decimal overtimeSalary = 0.00M;
decimal week1RegularTime = 0.00M;
decimal week1RegularPay = 0.00M;
decimal week1Overtime = 0.00M;
decimal week1OvertimePay = 0.00M;
decimal week2RegularTime = 0.00M;
decimal week2RegularPay = 0.00M;
decimal week2Overtime = 0.00M;
decimal week2OvertimePay = 0.00M;
// The overtime is paid time and half
overtimeSalary = HourlySalary * 1.5M;
// If the employee worked under 40 hours, there is no overtime
if (Week1Time < 40)
{
week1RegularTime = Week1Time;
week1RegularPay = HourlySalary * week1RegularTime;
week1Overtime = 0M;
week1OvertimePay = 0M;
}
// If the employee worked over 40 hours, calculate the overtime
else if (Week1Time >= 40)
{
week1RegularTime = 40M;
week1RegularPay = HourlySalary * 40M;
week1Overtime = Week1Time - 40M;
week1OvertimePay = week1Overtime * overtimeSalary;
}
if (Week2Time < 40)
{
week2RegularTime = Week2Time;
week2RegularPay = HourlySalary * week2RegularTime;
week2Overtime = 0M;
week2OvertimePay = 0M;
}
else if (Week2Time >= 40)
{
week2RegularTime = 40;
week2RegularPay = HourlySalary * 40M;
week2Overtime = Week2Time - 40M;
week2OvertimePay = week2Overtime * overtimeSalary;
}
RegularTime = week1RegularTime + week2RegularTime;
Overtime = week1Overtime + week2Overtime;
RegularPay = week1RegularPay + week2RegularPay;
OvertimePay = week1OvertimePay + week2OvertimePay;
TotalEarnings = RegularPay + OvertimePay;
}
}
- In the Solution Explorer, right-click CeilInn1 and click
Properties...
- In the Properties window, click the arrow of the Output Type combo
box and select Class Library
- Close the window
- Save all
- To create the assembly, in the Solution Explorer, right-click
CeilInn1 and click Build
- Use a Windows utility such as Windows Explorer to get the path to
the DLL that was created
- Select that path and copy it or write it down
|
|