Bookmark and Share

WCF from CLR Functions

by KodefuGuru 13. July 2009 18:15

I don’t approve of calling a WCF server from SQL Server, but there was a business requirement that had to be met. It concerned regulations regarding the safeguarding of certain data elements. Due to performance issues and the application’s infrastructure, calling the service from the application itself wasn’t an option.

Coding the CLR functions were the easy part. To do this, reference the Microsoft.SqlServer.Server and System.Data.SqlTypes namespaces (contained in System.Data.dll) and set the appropriate attributes on static methods contained within a static class. Also, it is probably best if you use the SqlTypes (SqlString, SqlDateTime, etc) for parameters and return values rather than rely on Sql Server to convert the values for you. Be sure to enable CLR on your SQL Server.

using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public static class SqlFunctions
{
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true)]
    public static SqlString HelloWorld()
    {
        return new SqlString("Hello World!");
    }

}

I wanted to avoid pulling too many assemblies into SQL Server, so I limited my references to the bare essentials for working with WCF and Sql Server: System, System.Data, System.Runtime.Serialization, System.ServiceModel, and System.XML.

Here’s the script to drop the assemblies if they exist and add them back (double slashes are intentional to allow copy/paste with formatting to work). Because of dependencies, it is necessary to add more assemblies than directly referenced by the project. If you created this within a database project, you can deploy by right-clicking the project. I have previously posted instructions on how to convert a class library to a database project.

IF  EXISTS (SELECT * FROM sys.assemblies asms 
WHERE asms.name = N'Microsoft.Transactions.Bridge') 
    DROP ASSEMBLY [Microsoft.Transactions.Bridge]
GO

IF  EXISTS (SELECT * FROM sys.assemblies asms 
WHERE asms.name = N'System.IdentityModel.Selectors') 
    DROP ASSEMBLY [System.IdentityModel.Selectors]
GO

IF  EXISTS (SELECT * FROM sys.assemblies asms 
WHERE asms.name = N'System.IdentityModel') 
    DROP ASSEMBLY [System.IdentityModel]
GO

IF  EXISTS (SELECT * FROM sys.assemblies asms 
WHERE asms.name = N'System.Messaging') 
    DROP ASSEMBLY [System.Messaging]
GO

IF  EXISTS (SELECT * FROM sys.assemblies asms 
WHERE asms.name = N'System.Web') 
    DROP ASSEMBLY [System.Web]
GO

IF  EXISTS (SELECT * FROM sys.assemblies asms 
WHERE asms.name = N'SMDiagnostics')
    DROP ASSEMBLY [SMDiagnostics]
GO

CREATE ASSEMBLY 
SMDiagnostics from
'C:\Windows\Microsoft.NET\Framework\v3.0\
\Windows Communication Foundation\SMDiagnostics.dll'
with permission_set = UNSAFE
GO

CREATE ASSEMBLY 
[System.Web] from
'C:\Windows\Microsoft.NET\Framework\v2.0.50727\
\System.Web.dll'
with permission_set = UNSAFE
GO

CREATE ASSEMBLY 
[System.Messaging] from
'C:\Windows\Microsoft.NET\Framework\v2.0.50727\
\System.Messaging.dll'
with permission_set = UNSAFE
GO

CREATE ASSEMBLY  
[System.IdentityModel] from
'C:\Program Files\Reference Assemblies\Microsoft\
\Framework\v3.0\System.IdentityModel.dll'
with permission_set = UNSAFE
GO

CREATE ASSEMBLY  
[System.IdentityModel.Selectors] from
'C:\Program Files\Reference Assemblies\Microsoft\
\Framework\v3.0\System.IdentityModel.Selectors.dll'
with permission_set = UNSAFE
GO

CREATE ASSEMBLY  
[Microsoft.Transactions.Bridge] from
'C:\Windows\Microsoft.NET\Framework\v3.0\
\Windows Communication Foundation\Microsoft.Transactions.Bridge.dll'
with permission_set = UNSAFE
GO

You will need to add your own assembly to the list in a similar manner as the .NET assemblies. Notice that they are marked UNSAFE. This is a necessary evil if you wish to use WCF with SQL Server. Because of this, if you have not done so you will need to reconfigure your database to allow it.

ALTER DATABASE KodefuGuru SET TRUSTWORTHY ON
reconfigure

The interesting part about this is that more assemblies than those specified are pulled in. When you remove the ones you specifically added, these are removed as well. Here is the list of assemblies:

Accessibility
Microsoft.Transactions.Bridge
SMDiagnostics
System.Configuration.Install
System.Design
System.DirectoryServices
System.DirectoryServices.Protocols
System.Drawing
System.Drawing.Design
System.EnterpriseServices
System.IdentityModel
System.IdentityModel.Selectors
System.Messaging
System.Runtime.Remoting
System.Runtime.Serialization
System.Runtime.Serialization.Formatters.Soap
System.ServiceModel
System.ServiceProcess
System.Web
System.Web.RegularExpressions
System.Windows.Forms

System.Windows.Forms? What?! Some of the dependencies are truly mind boggling.

This should be just enough to allow you to put your service calls inside of CLR functions. I attempted to be as lightweight as possible and avoided pulling in extra libraries, though you certainly could pull in a massive programmatic infrastructure with this.

I only agreed to this because the service call was directly related to the integrity of the data and further restrictions prevented the use of a separate physical tier to handle it. If you’re in the same situation, I hope this helps.

Bookmark and Share

Convert Class Library to Database Project

by KodefuGuru 2. July 2009 16:40

I’ve had this tendency to deploy CLR Functions manually by providing the dll and scripts to the data management team. This has generally worked out great, and I’ve always thought I’d have a dba available to perform the database duties. Imagine my surprise when I could not contact anyone today. Apparently database administrators take their holidays and vacations seriously (it’s Independence Day weekend for my non-American readers).

Another developer asked me why I couldn’t deploy my clr functions through the IDE. Having never done this, I asked him how. He showed me the deploy button in his project.

Deploy

I went back to my desk, heartened that I could still get my work done. I opened the Solution, right-clicked the project… but there was no Deploy. Thinking back on it, I know that I tend to make class libraries rather than database projects. I looked at my coworker’s project and sure enough, he was using a database project.

It seemed rather heavy-handed to create a new database project and move files over, so I dug into the csproj file to figure out how to convert it.

There are two key things that need to be done to convert a class library to a database project. SqlServer.targets needs to be imported, and the ProjectTypeGuids property needs to be added.

  <Import Project="$(MSBuildToolsPath)\SqlServer.targets" />
  <PropertyGroup>
    <ProjectTypeGuids>{c252feb5-a946-4202-b1d4-9916a0590387};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
  </PropertyGroup>

It doesn’t matter where these entries are added, but I place the property in the top property group below the Platform property and paste the import near the CSharp.targets import. The first guid in ProjectTypeGuids specifies that this is a SqlClrProject. The second guid specifies this is a C# project. Visual Basic developers will need to replace the C# guid with F184B08F-C81C-45F6-A57F-5ABD9991F28F.

The deploy button now appears but doesn’t work. When clicked, it fails with no more information than “Deploy failed.” Deducing that it probably needs a database connection, I used MSDN to find out where that configuration is located. This is one place the MSDN documentation is misleading.

Select Deploy from the Build menu. The assembly will then be registered in the SQL Server instance and database specified when the SQL Server project was first created in Visual Studio.

This is true only if one has never changed that configuration for the Database project. Since this was a class library converted to a database project, the server and database wasn’t specified when creating the project. Luckily, this can be configured in the Properties window on the Database tab. Be sure to add the connection to Server Explorer (Ctrl+W, L) first.

DatabaseProperties

At this point, the project has been converted and we’re almost ready to deploy. Before doing so, the assembly must be dropped from the database. Visual Studio is smart enough to drop it, but only if the assembly was deployed from Visual Studio in the first place. After the first deployment, it will work automatically in the future.

Bookmark and Share

Enable CLR in SQL 2008

by KodefuGuru 16. June 2009 12:58

I added an assembly and created clr functions on my SQL server only to receive the following error when I tried to excute them.

Msg 6263, Level 16, State 1, Line 1
Execution of user code in the .NET Framework is disabled. Enable "clr enabled" configuration option.

To enable clr functions to execute, run the following script:

sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO
After executing that script, calling clr functions work like a charm.

Tags: ,

Kodefu

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
C# MVP, INETA Community Champion


MVP - Visual C#

 

INETA Community Champions
Friend of RedGate
Telerik .NET Ninja
Community blogs & blog posts

I am a #52er

I have joined Anti-IF Campaign


World Map

Tag cloud

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010