Stairway to SQLCLR Level 5: Development (Using .NET within SQL Server)

Stairway to SQLCLR Level 5: Development (Using .NET within SQL Server)原文链接:http://www.sqlservercentral.com/articles/Stairway+Series/119429/BySolomonRutzky,2015/03/11TheSeriesThisarticleispartoftheStairwaySeries:StairwaytoSQLCLRThepossibil

大家好,又见面了,我是你们的朋友全栈君。

原文链接:http://www.sqlservercentral.com/articles/Stairway+Series/119429/

Stairway to SQLCLR Level 5: Development (Using .NET within SQL Server)

By Solomon Rutzky, 2015/03/11

The Series

This article is part of the Stairway Series: Stairway to SQLCLR

The possibilities for programming SQL Server platform were greatly enhanced with the addition of the SQLCLR subsystem. This allows code written in any .NET language to be incorporated into your SQL Server instance and called from a stored procedure or function. You can also create your own data types or aggregates for specialized purposes. This Stairway series will teach you how to get started writing your own CLR code and integrating it into SQL Server.

So far in this series we have looked at what SQLCLR is and what it can do. We have also looked at several examples related to the basic mechanics of SQLCLR and the various aspects of security. This time we finish learning the foundational aspects of doing SQLCLR development by exploring how .NET development within the context of SQL Server differs from regular .NET development.

The .NET Environment

Before we start looking at Visual Studio and getting into any code, we need to go over the nuances involved when working in SQL Server’s CLR. Even if you are very familiar with .NET you still need to be aware of environmental differences as there are some limitations that need to be understood. We need to look at features, supported .NET Framework libraries, and what version of .NET is in use, as there were significant changes introduced in SQL Server 2008 and then again in 2012. Between versions 2012 and 2014 nothing has changed so all examples here (and throughout this Stairway series) will work the same between those two versions. If any functionality is used that is not available in either just 2005, or in 2005, 2008, and 2008 R2, it will be noted.

Below is a chart of the changes to SQL Server’s CLR Integration over the ages:

SQL Server —»

Category (below)

2005

2008  / 2008 R2

2012 / 2014

Supported .NET Framework Libraries

  • CustomMarshalers
  • Microsoft.VisualBasic
  • Microsoft.VisualC
  • mscorlib
  • System
  • System.Configuration
  • System.Data
  • System.Data.OracleClient
  • System.Data.SqlXml
  • System.Deployment
  • System.Security
  • System.Transactions
  • System.Web.Services
  • System.Xml
  • CustomMarshalers
  • Microsoft.VisualBasic
  • Microsoft.VisualC
  • mscorlib
  • System
  • System.Configuration
  • System.Data
  • System.Data.OracleClient
  • System.Data.SqlXml
  • System.Deployment
  • System.Security
  • System.Transactions
  • System.Web.Services
  • System.Xml
  • System.Core
  • System.Xml.Linq

UDT: Max size (bytes)

8000

2147483647 (i.e. 2 GB)

UDA: Max size (bytes)

8000

2147483647 (i.e. 2 GB)

UDA: Input Parameters

1

1 or more

TVF: ORDER clause

No

Yes

Memory Location and Amount Available (32-bit systems)

MTL (Mem-to-Leave)

256 MB by default

Max Server Memory

CLR version

2.0

4.0

.NET Framework versions

2.0 / 3.0 / 3.5

4.0 / 4.5 / 4.5.x

.NET Framework version installed with SQL Server  ¹

2.0

3.5 SP1

4.0

Performance Enhancements

n/a

{none}

  • Improvements in streaming TVFs
  • The following CLR objects are now foldable:
    • Deterministic scalar-valued UDFs
    • Deterministic methods of UDTs

¹ Please note that any Microsoft documentation stating that a particular .NET Framework version is “required”, whether or not it is installed with that version of SQL Server, is referring to the requirements of tools that come with SQL Server and is not related to the “CLR Integration” feature.

CLR Version and .NET Framework Versions

While this is not always a practical difference, it should at least be understood that the Common Language Runtime (CLR) that executes the .NET code is not the same thing as the .NET Framework. The reason it is important to note this difference is that a particular version of the CLR can work across several versions of the .NET Framework. SQL Server, starting with version 2005, is bound to a particular version of the CLR, not to a particular version of the .NET Framework. For example, SQL Server 2008 is bound to CLR version 2.0 and yet includes internally the System.Core library that was added in version 3.5 of the .NET Framework.

While .NET applications running on the main Windows CLR can make use of multiple versions of the CLR, the SQL Server-specific CLR host doesnot allow for mixed-mode CLR. Meaning, whatever version of the CLR is listed for a particular version of SQL Server, that is the only version that loaded assemblies will run against. Creating an assembly that uses a feature that started in .NET Framework 4.5 will error when trying to load that assembly in SQL Server 2005, 2008, or 2008 R2 due to those versions only supporting version 2.0 of the CLR while .NET Framework 4.5 requires version 4.0 of the CLR. However, due to functionality being backwards compatible, any assembly that works in SQL Server 2005, 2008, or 2008 R2 (assuming that it does not rely upon any unsupported .NET Framework libraries) should work just the same in SQL Server 2012 or 2014 (or whatever the current version is).

To be clear about the CLR versions and .NET Framework versions, the out-of-the-box functionality that you can expect to use for a particular version of SQL Server depends on the version of the CLR that the target version of SQL Server is bound to, and to the list of supported .NET Framework libraries. For SQL Server 2005, 2008, and 2008 R2—all bound to version 2.0 of the CLR—it is valid to use features that were introduced in .NET Framework versions 2.0, 3.0 and 3.5. Likewise, for SQL Server 2012 and 2014—both bound to version 4.0 of the CLR—it is valid to use functionality that was introduced in .NET Framework versions 4.0, 4.5, 4.5.1, and so on. There are two requirements for using functionality that was introduced in a .NET Framework version that is different than its associated version of the CLR (i.e. 3.0 and 3.5 running on version 2.0 of the CLR, and 4.5, 4.5.1, etc running on version 4.0 of the CLR):

  1. The functionality needs to be provided in one of the supported .NET Framework libraries (more on those in a moment). This means that you can reference theTimeZoneInfo class (introduced in .NET 3.5) if the assembly will be used in SQL Server 2008 or 2008 R2 because that class is found inSystem.Core.  But it will not work, by default, if the assembly will be loaded into SQL Server 2005 asSystem.Core was not introduced until SQL Server 2008 (as shown in the chart above). If you try to load an assembly referencingTimeZoneInfo, and henceSystem.Core, into SQL Server 2005, you will get the following error when executing the CREATE ASSEMBLY statement:

    Msg 6503, Level 16, State 12, Line 1
    Assembly 'system.core, version=3.5.0.0, culture=neutral, publickeytoken=b77a5c561934e089.' was not found in the SQL catalog.

  2. The referenced versions of the .NET Framework need to be installed on any server that has an instance of SQL Server in which your assembly will be loaded. This is only an issue if the installed version of SQL Server did not come with the .NET Framework version that is being referenced. For example, SQL Server 2005 could use a feature that was introduced in .NET 3.0 and is found in one of the SQL Server 2005 supported .NET Framework libraries, but SQL Server 2005 only installs .NET Framework 2.0. If .NET 3.0 or even .NET 3.5 has not been manually installed on that server, then the assembly will not work (most likely the error will occur when executingCREATE ASSEMBLY). This is not an issue for SQL Server 2008 or 2008 R2 as both of those versions automatically install .NET 3.5 SP1. However, this does become an issue again in SQL Server 2012 and 2014 is using features that were introduced in .NET 4.5 or newer as those two versions of SQL Server only install .NET 4.0; using newer features requires manually installing the referenced version of the .NET Framework.

Supported .NET Framework Libraries

It was mentioned in the prior two levels that SQLCLR programming is restricted by Host Protection Attributes (HPAs) and other limitations. Those restrictions are configurable and can be lifted by changing thePERMISSION_SET property of an assembly. But there is a whole other level of restrictions that are not configurable. As opposed to typical .NET programming (i.e. for Desktop and WebApps), SQL Server’s CLR host contains only a subset of the available .NET Framework libraries (and a fairly small subset at that). The libraries in the supported list are guaranteed to work as expected, across upgrades to .NET and/or SQL Server on your system.

The list of supported libraries is shown in the chart above. Starting in SQL Server 2008, two libraries—System.Core andSystem.Xml.Linq—were added and the list has been stable ever since (at least through SQL Server 2014).

Unsupported .NET Framework Libraries

It is possible to reference some .NET Framework libraries that are not in the supported list. However, this requires manually loading those libraries into SQL Server. In most, if not all, cases the assembly will need to be created with the optionWITH PERMISSION_SET = UNSAFE due to the library either not being verifiable itself, or depending on one or more libraries that are not verifiable (dependent libraries in the same folder are automatically imported when usingCREATE ASSEMBLY with a path to the DLL).  And loading them asUNSAFE will require setting the database asTRUSTWORTHY ON.

While incorporating one or more unsupported Framework libraries can prove to be successful for years without any problems, you should at least be aware of the risks prior to making that choice:

  1. They are unsupported for a reason:

    Unsupported Framework DLLs have not been fully tested and verified. There can be internationalization issues. There can be unexpected behavior due to whatever reason(s) required that the DLL to be loaded asUNSAFE. For example, there can be class-level static variables being used to store values. This is not an issue for most .NET programming, but the model in SQL Server’s CLR-integration is to create a single App Domain (per database, per assembly owner) that is shared across all sessions. The top-level classes are not instantiated and all methods exposed to SQL Server must bestatic. Sharing the same memory and objects (i.e. the class(es)) means that the static class variables are also shared across sessions, hence one session can read and overwrite values in use by another session. This is typically not how such code was intended to be used and testing by one person will not expose any issues. But as soon as multiple processes are using that same code at the same time, it is quite possible that your code will appear to have some bugs that are really hard to find. So if you do use unsupported .NET Framework libraries, your regression tests really need to have multiple connections hitting that functionality.

  2. DLLs updated when installing a new version of .NET may need to be reloaded and your code might need to be recompiled and reloaded:

    As stated in Microsoft Knowledge Base article 949080 (see “Additional Reading” section at the end):

    When the CLR loads an assembly, the CLR verifies that the same assembly is in the GAC. If the same assembly is in the GAC, the CLR verifies that the Module Version IDs (MVIDs) of these assemblies match. If the MVIDs of these assemblies do not match, you receive the error message that the “Symptoms” section mentions.

    When an assembly is recompiled, the MVID of the assembly changes. Therefore, if you update the .NET Framework, the .NET Framework assemblies have different MVIDs because those assemblies are recompiled. Additionally, if you update your own assembly, the assembly is recompiled. Therefore, the assembly also has a different MVID.

    This means that if you incorporate an unsupported Framework assembly and that assembly is updated in a later version of the .NET Framework, your SQLCLR code will stop working until you reload that assembly.

  3. DLLs updated when installing a new version of .NET might not be loadable into SQL Server anymore if they switch to mixed format (i.e. not “pure”). Being unsupported means that Microsoft has no obligation to make sure they continue to work inside SQL Server:

    There are two types of .NET assemblies: “pure” and “mixed”. Pure assemblies are only managed code, while mixed assemblies also contain unmanaged C++ code. SQL Server only allows pure assemblies to be loaded via CREATE ASSEMBLY. This is why you can only reference some, but not all, of the unsupported Framework DLLs. And to complicate life just a little bit, it is possible for an assembly to go from pure to mixed in a new version of the .NET Framework. This means that any unsupported assembly that you successfully incorporate today could change in such a way that it can no longer be loaded, and reloading and recompiling will not be an option. If this happens (and it, in fact, has happened) then you will have to find a substitute, or write that method yourself, or something else. Either way, it could amount to a lot of extra work. However, the supported libraries are guaranteed to work across new versions of the Framework.

Framework Versions and Supported Libraries and Unsupported Libraries, oh my!

Let’s look at an example that puts all of this together.  .NET 4.5 introduced some improvements to the System.IO.Compression namespace to allow for easily zipping up files in a folder into a single zip file. We have been able to use the GZipStream class in SQLCLR projects since SQL Server 2005 / .NET 2.0 so there should be no problem using ZipFile.CreateFromDirectory as long as our assembly is loaded into SQL Server 2012, 2014, or anything newer, right? Not so fast. While the GZipStream class and the ZipFile class are both in the System.IO.Compression namespace, GZipStream is located in System.dll (in the supported libraries list) while ZipFile is located inSystem.IO.Compression.FileSystem.dll (a new DLL that isnot in the supported libraries list). In order to get this to work we would need to load System.IO.Compression.FileSystem into SQL Server:

CREATE ASSEMBLY [System.IO.Compression.FileSystem] FROM N'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.IO.Compression.FileSystem.dll' WITH PERMISSION_SET = UNSAFE;

This will automatically import System.IO.Compression.dll as well since System.IO.Compression.FileSystem references it. That wasn’t so bad.

We test it on the development server and everything works as expected. Now we can publish our project to the test server. We create our assemblies from hex codes (i.e. theFROM 0x455A90... option) instead of DLLs as it is easier to just have a SQL script to deploy. But we get an error when attempting to create the two unsupported Framework assemblies: they rely on updated versions of some of thesupported Framework assemblies, but those don’t exist on this server as it was never upgraded to .NET 4.5.

So now we have three additional requirements when we load our assembly into a new system:

  1. Any database that our assembly is loaded into needs to be altered with: SET TRUSTWORTHY ON
  2. Any database that our assembly is loaded into needs to get the System.IO.Compression andSystem.IO.Compression.FileSystem DLLs
  3. Any instance our that our assembly is loaded into needs to have .NET Framework 4.5 installed

Data Types

Data types between systems (databases, languages, etc) are not always the same, even if they have the same name. Mappings are usually provided to translate between the systems. In .NET there is a set of classes in theSystem.Data.SqlTypes namespace that map directly to most of the SQL Server datatypes (see the “SQL Server Data Type Mappings” link in the “Additional Reading” section). As stated on that “SQL Server Data Type Mappings” page:

Each data type in SqlTypes has its equivalent data type in SQL Server, with the same underlying data representation. Many of them also have equivalent data types in the CLR. However, SqlDateTimeSqlDecimal, and SqlString have different underlying data structures with their corresponding .NET Framework data types.

Benefits

As long as there is a mapped SqlType available, you should use those types as input and output parameters and return values instead of the standard .NET types (e.g. use SqlString instead of string). There are several good reasons for using the SqlTypes. The following list of benefits is taken from “Nullability and Three-Value Logic Comparisons” link in “Additional Reading” section:

NULL Values

A primary difference between native common language runtime (CLR) data types and SQL Server data types is that the former do not allow for NULL values, while the latter provide full NULL semantics.

Comparisons are affected by NULL values. When comparing two values x and y, if either x or y is NULL, then some logical comparisons evaluate to an UNKNOWN value rather than true or false.

SqlBoolean Data Type

The 
System.Data.SqlTypes namespace introduces a 
SqlBoolean type to represent this 3-value logic. Comparisons between any 
SqlTypes return a 
SqlBoolean value type. The UNKNOWN value is represented by the null value of the 
SqlBoolean type. The properties 
IsTrue
IsFalse, and 
IsNull are provided to check the value of a 
SqlBoolean type.

Operations, Functions, and NULL Values

All arithmetic operators (+, -, *, /, %), bitwise operators (~, &, and |), and most functions return NULL if any of the operands or arguments of 
SqlTypes are NULL. The 
IsNullproperty always returns a true or false value.

Precision

Decimal data types in the .NET Framework CLR have different maximum values than those of the numeric and decimal data types in SQL Server. In addition, in the .NET Framework CLR decimal data types assume the maximum precision. In the CLR for SQL Server, however, 
SqlDecimal provides the same maximum precision and scale, and the same semantics as the decimal data type in SQL Server.

Overflow Detection

In the .NET Framework CLR, the addition of two very large numbers may not throw an exception. Instead, if no check operator has been used, the returned result may “wrap around” as a negative integer. In 
System.Data.SqlTypes, exceptions are thrown for all overflow and underflow errors, and divide-by-zero errors.

Additional benefits include:

Streamed access to LOB parameters

Streaming values in can be achieved via SqlChars for N[VAR]CHAR, SqlBytes for [VAR]BINARY, and SqlXml.CreateReader() for XML. While SqlChars and SqlBytes work for NCHAR / NVARCHAR and BINARY / VARBINARY, respectively, of any length, they are primarily intended to provide access to large values that is faster and uses less memory than the SqlString and SqlBinary types. This efficiency is achieved by providing access to a stream of the data via theRead() method that can read in chunks of 1 or more characters / bytes at a time instead of copying the entire value into memory like SqlString and SqlBinary do.

The streamed access of SqlChars and SqlBytes has a secondary benefit, in addition to already not requiring the value’s full size of memory: it might not require much of any memory at all if the value is never accessed (well, 8 kb). Unlike SqlString and SqlBinary which transfer the full value of the input param as the SQLCLR method is being called, SqlChars and SqlBytes lazy load their values (i.e. not until requested). This means that if the method exits before requesting any of the value from these two types, then no data was ever sent over, and this saves time (both CPU and Elapsed) as well as memory (for strings of 1980 or more characters).

Insight into the collation

When using SqlString (not string or even SqlChars) you can access the CompareInfo, CultureInfo, LCID, and SqlCompareOptions properties (see the “SqlString properties” link in the “Additional Reading” section for details). Of course, this is the database default collation and
not the collation of a particular field or inline specification. So there is no way to tell if a value passed in is of a collation that is not the same as the database default, but in those instances when you are sure that the collation is the database default, then this can be quite handy.

Exceptions

As previously mentioned, the SqlTypes do not map all of the SQL Server data types. Below is a list of the exceptions. Please note that a “?” at the end of a Type, as inType?, is just short-hand forNullable<Type> (see the “Nullable Types (C# Programming Guide)” link in the “Additional Reading” section for full details).

  • CHAR / VARCHAR
    • No mappings as .NET is entirely UCS-2. All strings are NCHAR / NVARCHAR
  • SQL_VARIANT maps to Object
    • test NULL via object == DbNull.Value
    • Use only when necessary. This is not a magical work-around for not being able to do overloading: there is a noticeable performance penalty for using this datatype.
    • LOB types (NVARCHAR(MAX), VARBINARY(MAX), and XML) cannot be sent through a SQL_VARIANT parameter.
  • DATETIME2
    • Use .NET DateTime or DateTime?
  • DATETIMEOFFSET
    • Use .NET DateTimeOffset or DateTimeOffset?
  • TIME
    • Use .NET TimeSpan or TimeSpan?

Good to Know

The following information are things that one should know about working with the SqlTypes in order to be successful and avoid certain pitfalls.

Each of the SqlTypes classes has various static and instance methods and properties (see the “System.Data.SqlTypes Namespace” link in the “Additional Reading” section for full details). While many of the methods and properties vary between the different SqlTypes, there are a few that are consistent across them and are used quite frequently:

  • Value: This instance property returns the underlying value as the associated .NET type. A SqlInt32 returnsint, a SqlBoolean returns abool, and so on. There is no need to cast a SqlType to a .NET type, nor useToString() on a SqlString.
  • IsNull: This instance property is used to test for the value being “NULL”, as opposed to using either “== null” or “== DBNull.Value“.
  • Null: This static field is used to send a “NULL” back to the database, as opposed to using either “null” or “DBNull.Value“.

And there are a few minor differences between the .NET side and the SQL Server side for some of the types that do have mappings.

  • UNIQUEIDENTIFIER maps to SqlGuid, which is mostly the same as a .NET Guid. The difference is in how they compare to other guids. The MSDN page for “Comparing GUID and uniqueidentifier Values” (see link in the “Additional Reading” section) states:

    Both Guid and SqlGuid have a CompareTo method for comparing different GUID values. However, System.Guid.CompareTo and SqlTypes.SqlGuid.CompareTo are implemented differently. SqlGuid implements CompareTo using SQL Server behavior, in the last six bytes of a value are most significant. Guid evaluates all 16 bytes.

  • DECIMAL (and hence NUMERIC) maps to SqlDecimal, but the datatype on the database side can actually hold values with a greater precision than in .NET (i.e. max of 38 instead of 28). So if you are passing in DECIMAL values and need to do arithmetic then you might need to use the SqlDecimal methods, such as Add and Multiply, etc. Also, when left unspecified, the default Precision and Scale used for input and output parameters generated by Visual Studio / SQL Server Data Tools — both are discussed in the next level — is (18, 0). You can use [SqlFacet(Precision = x, Scale = y)] to set custom values.

  • NVARCHAR(1 – 4000) and NVARCHAR(MAX) map to SqlString andSqlChars respectively when using Visual Studio / SQL Server Data Tools to publish your SQLCLR project. It appears to be commonly thought that you must use SqlString to map to NVARCHAR(1 – 4000) and SqlChars to map to NVARCHAR(MAX). However, this is not a requirement; these mappings just happen to be the default used by those two tools.

    You should decide between SqlString and SqlChars based on several factors:

    • If the value will be frequently sent but not accessed / used, use SqlChars, regardless of size
    • If the value can be accessed in pieces (e.g. IndexOf, LastIndexOf, saving to a FileStream, etc) AND is at least 45,000 characters, use SqlChars and theRead() method
    • If the entire value is required for a particular operation (e.g. RegEx, passing to a method as a String parameter, etc) OR will always be less than 45,000 characters, then use SqlString
    • For return values (scalar functions) and table fields (table-valued functions), use SqlString as it is faster and uses less memory

    You should decide between NVARCHAR(1 – 4000) and NVARCHAR(MAX) based on the potential size of that value. You can control what Visual Studio / SQL Server Data Tools will auto-generate via[SqlFacet(MaxSize = x)] where “x” is either 1 – 4000 or -1 to represent “MAX”. When creating SQLCLR functions (scalar or table-valued), be sure to only useMaxSize = -1 / NVARCHAR(MAX) when the value needs to hold more than 4000 bytes. The reason for this caution is that if a MAX type is used anywhere (input param, return value, or field in a result set), there will be a performance penalty. It will be small enough to not really be noticed on a function running once per minute (or less frequently), but it could easily be noticeable on a function running many times per second.

    There is, unfortunately, a general lack of information available regarding the true differences betweenSqlString andSqlChars with respect to memory usage and performance. However, this situation will soon be corrected as a future article will provide a detailed analysis so that this issue can be fully understood.

  • SMALLDATETIME and SMALLMONEY do not have explicit mappings to any SqlTypes, but can useSqlDateTime andSqlMoney, respectively. If you are using Visual Studio / SQL Server Data Tools to publish your SQLCLR project, then you will have to manually ALTER the T-SQL wrapper object(s) to change the auto-mapped datatypes of DATETIME (forSqlDateTime) and MONEY (forSqlMoney) into SMALLDATETIME and SMALLMONEY, respectively.

Examples

The example code—in a zip file (StairwayToSQLCLR-05-ExampleCode.zip) attached to this article at the bottom in the “Resources” section—consists of:

  • A folder named “Level-05” containing:
  • Six SQL scripts
  • A subfolder named “Assemblies” containing:
  • Two DLLs
  • A subfolder named “SourceCode” containing:
  • Two C# .cs files

Example Setup

If you have not already done so via prior “Stairway to SQLCLR” level examples, please create aC:\TEMP\StairwayToSQLCLR folder either in Windows Explorer or from a command prompt with the following command:
                MKDIR C:\TEMP\StairwayToSQLCLR

Extract the contents of the zip file, starting with the “Level-05” folder itself, intoC:\TEMP\StairwayToSQLCLR such that you now haveC:\TEMP\StairwayToSQLCLR\Level-05\… .

For those who want to play with the C# source code, there are two files in theC:\TEMP\StairwayToSQLCLR\Level-05\SourceCode folder. The Visual Studio solution file and project files that contain the configuration for building and signing the source code are not included; the purpose of this article and the examples contained herein is to gain a better and clearer understanding of how .NET works inside of the SQL Server environment, not how to build SQLCLR code. The focus here needs to stay on studying the behavior and we will start actual development in the next level. Along those lines, the C# source code will only be explained in brief.

Run the first script, StairwayToSQLCLR-05-01-CreateDatabase.sql, even if you already have the test database,[StairwayToSQLCLR], created from prior level examples, and then close it.

Run the second script, StairwayToSQLCLR-05-02-EnableCLR.sql, and then close it. This script will enable “CLR Integration” if it is not already enabled. It will also create a User-Defined Function in the[StairwayToSQLCLR] database which returns a BIT value denoting whether or not this script enabled “CLR Integration” or not.  This function,[DidWeEnableCLR], is used in the cleanup script,StairwayToSQLCLR-05-05-DisableCLR.sql, as a means of determining whether or not to actually disable “CLR Integration”. If “CLR Integration” was enabled prior to running theStairwayToSQLCLR-05-02-EnableCLR.sql script, then the[DidWeEnableCLR] function will return 0 and “CLR Integration” won’t be disabled as it might be in use for something other than these examples.

Supported Library Test

Attempt to use the TimeZoneInfo class, which was introduced in .NET Framework version 3.5. Creating the Assembly should only error on SQL Server 2005 as the class is part ofSystem.Core.dll which was included as a supported library starting in SQL Server 2008. That this works in 2008 but not 2005 proves that a) you are not restricted to .NET Framework 2.0 functionality in SQL Server 2005 / 2008 / 2008 R2, and b) that regardless of any new functionality provided in any updates to the .NET Framework, anything not found in one of the supported libraries is still unavailable (at least without manually importing one or more unsupported libraries).

The code shown below simply converts the local server time to “Tokyo Standard Time”. The important aspect of the code is just the fact that it uses theTimeZoneInfo class, which requires adding a reference toSystem.Core. That reference will be checked when loading the assembly and if System.Core is not found then the CREATE ASSEMBLY will fail.

Stairway to SQLCLR Level 5: Development (Using .NET within SQL Server)

Open the StairwayToSQLCLR-05-03-SupportedLibraryTest.sql script. Hit F5 / Control-E. Unless you are running this on SQL Server 2005, you should see output in the “Messages” tab, ending with:

Time in Tokyo: xxxxxxxxx

Running this on SQL Server 2005 will result in the following error:

Msg 6503, Level 16, State 12, Line 1
Assembly 'system.core, version=3.5.0.0, culture=neutral, publickeytoken=b77a5c561934e089.' was not found in the SQL catalog.

Framework Version Test

Use GZipStream to compress some text. This class is available inSystem.dll and so has been available to use starting in SQL Server 2005. Compress a specified string and it will always have a size of X bytes when using SQL Server 2005 / 2008 / 2008 R2. But when using SQL Server 2012 / 2014, it will be that same size of X bytes only if the system has not yet been updated to .NET 4.5. The GZipStream compression algorithm used in .NET Framework versions 2.0, 3.0, 3.5, and 4.0 was not very good. This was fixed in .NET 4.5, so when using SQL Server 2012 / 2014 on a system that has had its .NET Framework updated, the size of that same string will be much smaller than X bytes. This proves that a) SQL Server 2012 / 2014 will automatically get enhanced .NET functionality through regular updates of the .NET Framework (so long as those updates run on CLR version 4.0) and that b) SQL Server 2005 / 2008 / 2008 R2, regardless of updates to the .NET Framework, cannot access newer functionality beyond Framework version 3.5 (since they are bound to Framework versions that run on CLR version 2.0).

The code shown below writes the incoming binary data (in DataToCompress) to the GZipStream which stores the compressed value in the MemoryStream. The data in the MemoryStream is used to create a new SqlBytes variable to pass back. Please note the use of theIsNull property and the static Null field in that first “if” block.

Stairway to SQLCLR Level 5: Development (Using .NET within SQL Server)

Open the StairwayToSQLCLR-05-04-FrameworkVersionTest.sql script. Hit F5 / Control-E. It installs the assembly and runs:

SELECT dbo.GZip(CONVERT(VARBINARY(4000), N'OMG! This is such a test!'));

If you are running this on SQL Server 2005 / 2008 / 2008 R2, or 2012 / 2014 on a server has not been updated with at least .NET Framework 4.5, you will get the following returned (170 bytes):

0x1F8B0800000000000400EDBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22BEFC35BEF8353EFF357ED75F23FD35DEFC1AF35FA3F8351AFA4DFE6D7E8DF5AF31A5CFD25F23A3FFB7BF464E9FB4BFC6EFFA6BFC3F019BA23D32000000

But, if you are running this on SQL Server 2012 / 2014 IF the server has been updated with at least .NET Framework 4.5, you will get the following returned (59 bytes):

0x1F8B0800000000000400F367F0657067506450600861C860C8642806B220643143294332504C812111884B1852812225409500019BA23D32000000

Example Cleanup

If you want to put the system back to the state it was in prior to running any of the example scripts, run scripts 5 and 6 in order. Doing so will remove all of the artifacts created by the example scripts (Database, Asymmetric Key, and Login), and disable “CLR Integration”, but only if it was enabled in script 2.

Summary

This time we looked at the various factors that influence what we are able to do with SQLCLR and how data types are managed between .NET and SQL Server. We distinguished between the CLR and the .NET Framework and explored the relationship between their various versions and the different versions of SQL Server. In the next level we will start coding with Visual Studio and SQL Server Data Tools.

Additional Reading

Environment:

Changes:

Miscellaneous:

About the Author

Solomon Rutzky has been working with databases for 19 years and specifically with SQL Server for 13 years. He has written several articles forSQL Server Central and Simple-Talk. Notable companies that he has worked for include PLATINUM technology and ChannelAdvisor. Solomon is also the author of the popular SQLCLR library of functions:SQL# (SQLsharp).

Resources:

StairwayToSQLCLR-05-ExampleCode.zip

This article is part of theStairway to SQLCLR Stairway

Sign up to our RSS feed and get notified as soon as we publish a new level in the Stairway!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/162231.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • python爬虫爬图片教程_爬虫爬取图片的代码

    python爬虫爬图片教程_爬虫爬取图片的代码用Python爬虫来爬小姐姐本教程将教你从0开始走进Python爬虫1.我们先要知道Python爬虫的原理基本的Python爬虫原理很简单,分为三步获取网页源码通过分析源码并通过代码来获取其中想要的内容进行下载或其他操作话不多说直接开干注意!本教程只是为了快速入门爬虫并实现一个功能,不考虑代码写的漂不漂亮,规不规范先准备上我们的目标网页开始我用的工具是:JetBrai…

    2022年10月29日
  • opencv——访问图像元素(imagedata widthstep)

    opencv——访问图像元素(imagedata widthstep)http://blog.sina.com.cn/s/blog_759803690101gtpx.html怎么访问图像元素(坐标起点相对于图像原点 image origin 从 0 开始,或者是左上角 (img->origin=IPL_ORIGIN_TL) 或者是左下角 (img->origin=IPL_ORIGIN_BL) 假设有 8-bit 1-通道的图像 I (IplIma

  • Java网络爬虫(七)–实现定时爬取与IP代理池

    Java网络爬虫(七)–实现定时爬取与IP代理池注:对代码及思路进行了改进—Java网络爬虫(十一)–重构定时爬取以及IP代理池(多线程+Redis+代码优化)定点爬取当我们需要对金融行业的股票信息进行爬取的时候,由于股票的价格是一直在变化的,我们不可能手动的去每天定时定点的运行程序,这个时候我们就需要实现定点爬取了,我们引入第三方库quartz的使用:packagetimeutils;importorg.quart

  • 单链表排序java_快速排序链表

    单链表排序java_快速排序链表难易程度:★★重要性:★★★链表的排序相对数组的排序更为复杂些,也是考察求职者是否真正理解了排序算法(而不是“死记硬背”) 链表的插入排序 publicclassLinkedInsertSort{staticclassListNode{intval;ListNodenext;Lis…

    2022年10月10日
  • DatagramSocket类简单使用

    DatagramSocket类简单使用importorg.junit.Test;importjava.io.*;importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetSocketAddress;/***@author*@date2019/7/23*/publicclassTestUd…

  • navicat15 临时激活码(最新序列号破解)

    navicat15 临时激活码(最新序列号破解),https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号