.NET Managed User Defined Types in SQL Server 2005

7 11 2007

Implementing managed user defined types (UDT) for use in SQL Server 2005, as with stored procedures, is again relatively straight forward – you need to create a class library which implements the UDT.

A managed UDT takes the form of a C# class or struct, use of namespaces to enclose your UDTs is optional. For this example I’ll create a Coordinate UDT which has X and Y int properties.

First your class or struct must be serialisable and must implement the INullable interface, further you must decorate the type with the SqlUserDefinedType attribute, found in the Microsoft.SqlServer.Server namespace.

The Coordinate type is therefore declared as follows;

namespace MyProcsAndUDTs
{
 [Serializable]
 [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native, IsByteOrdered = true, ValidationMethodName = "ValidateCoord")]
 public struct MyCoordType : INullable
 {
 	private int _x;
 	private int _y;
 	private bool _isnull;

	public bool IsNull
 	{
 		get { return _isnull; }
 	}

	public static MyCoordType Null
 	{
 		get
 		{
 			MyCoordType coord = new MyCoordType();
 			coord._isnull = true;
 			return coord;
 		}
 	}

You must implement the IsNull property which indicates whether the UDT is NULL, and the static Null property which returns a new “Null” instance of the UDT.You also need to implement a static Parse method which works in tandem with the overriden ToString method. Together these to methods render the UDT’s value to a string readable form, and Parse a string readable form to a native UDT, as shown below;

 	[Microsoft.SqlServer.Server.SqlMethod(OnNullCall = false)]
 	public static MyCoordType Parse(SqlString sqlString)
 	{
 		if (sqlString.IsNull)
 			return Null;

		string[] xy = sqlString.Value.Split(",".ToCharArray());
 		MyCoordType coord = new MyCoordType();
 		coord.X = Int32.Parse(xy[0]);
 		coord.Y = Int32.Parse(xy[1]);

		if (!coord.ValidateCoord())
 			throw new ArgumentException(@"Invalid X & Y Coordinate Value");
 		return coord;
 	}

	public override string ToString()
 	{
 		if (this.IsNull)
 			return "NULL";
 		StringBuilder sb = new StringBuilder();
 		sb.Append(_x);
 		sb.Append(",");
 		sb.Append(_y);
 		return sb.ToString();
 	}

The OnNullCall attribute is used to indicate that the method may be called with “Null” arguments and therefore that the method may return “Null”. The property methods are described below and are pretty standard;

	public int X
	{
 		get { return _x; }
		set { _x = value; _isnull = false; }
	}
	public int Y
	{
 		get { return _y; }
 		set { _y = value; _isnull = false; }
	}

To introduce the UDT into SQL Server, you need to register the assembly with SQL Server and create a UDT which references the managed struct/class.


CREATE ASSEMBLY SqlServerProcs FROM N'C:\dev\SqlServerProcs\....\Debug\SqlServerProcs.dll'
WITH PERMISSION_SET = SAFE

CREATE TYPE XYCoord
EXTERNAL NAME SqlServerProcs.[MyProcsAndUDTs.MyCoordType]

Having done this, you can use the UDT almost as you would any native Sql type;


CREATE TABLE dbo.Coords (ID int IDENTITY(1,1) PRIMARY KEY, XY XYCoord)
INSERT INTO Coords ([XY]) VALUES (N'1,3')
SELECT ID, XY FROM Coords
SELECT ID, XY.ToString() AS XY FROM Coords





ASP.NET Page Life Cycle Overview

7 11 2007




ASP.NET Website Administration Tool Problems

5 11 2007

I’ve had 2 problems with the VS 2005 Website Admiistration Tool (WAT), first the site wouldn’t load in my browser correctly, saying unhelpfully that “an error occurred, please try restarting the tool”.

After much head scratching I went over to my Vista laptop, tried it out there and it worked fine. IT dawns on me then that my laptop has IE as the default browser while my dev box has Avant as the default browser. After making IE the default browser on the dev box, the WAT launches without a problem.

The second problem, on both my dev box and laptop occurs when you click on the Security tab. You get a number of error messages the most obvious one mention that it was unable to connect to the datasource and to check your settings on the Provider tab.

After some googling, it turns out that the culprit is a conection string setting which is configured while installing VS 2005. This setting is called LocalSqlServer and it can be found in your machine.config file in the .NET framework directory according to the version of the framework installed. On my machine the default setting is as follows;

<connectionStrings>
  <add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;
                                   Integrated Security=SSPI;
                                   AttachDBFilename=|DataDirectory|aspnetdb.mdf;
                                   User Instance=true"
                             providerName="System.Data.SqlClient" />
</connectionStrings>

On my laptop I have SQL Server 2005 installed and on the dev box I have SQL Server 2000. The WAT tool uses the LocalSqlServer connection string to connect with a Datastore to use for the Memberships and Roles API, the default instance name for SQL Server 2005 is “SQLEXPRESS” hence the connection string above.The solution is to either change the default in machine.config, or preferably, modify your web.config file to remove the setting inhertited from machine.config and set a new connection string for your web project. After doing the later, my web.config file appears as shown below;

<connectionStrings>
  <remove name="LocalSqlServer" />
  <add name="LocalSqlServer" connectionString="Data Source=HP6715s\MSSMLBIZ;
                       Initial Catalog=aspnetdb;Integrated Security=True"
                       providerName="System.Data.SqlClient" />
</connectionStrings>

After making this change the Security tab for the WAT works correctly. I should point out that before doing this I’d already run the aspnet_regsql utility to create the schema for the Roles and Membership API, although apparently if you’re using SQL Server 2005 you don’t need to do this as the Wizard will do it for you, I never tried this so I don’t know if thats true.





.NET Managed Stored Procedures in SQL Server 2005

1 11 2007

To develop a managed stored procedure is relatively simple, however you first need to configure SQL Server 2005 to enable CLR Integration. You can do this using the SQL Server Surface Area Configuration tool, or entering the following command into SQLServer Management Studio;

EXEC sp_configure 'clr enabled', 1;
RECONFIGURE WITH OVERRIDE;
GO

Next you need to write a C# class library to implement the stored procedure behaviour. Create a standard C# class library as shown below, you’ll need to
import the Microsoft.SqlServer.Server namespace as well as the usual Sql namespaces.

CLR stored procedure are implemented in your code as static class instance methods, which return either an Int32 or void.
The method must be decorated with the [Microsoft.SqlServer.Server.SqlProcedure] attribute.

using System; 

using System.Collections.Generic; 

using System.Text; 

using System.Data; 

using System.Data.Sql; 

using System.Data.SqlClient; 

using System.Data.SqlTypes; 

using Microsoft.SqlServer.Server; 

public class StoredProcs 

{ 

   [Microsoft.SqlServer.Server.SqlProcedure] 

   public static void GetAuthors() 

   { 

      SqlPipe sp = SqlContext.Pipe; 

      using(SqlConnection conn = new SqlConnection("context connection=true")) 

      { 

         SqlCommand cmd = new SqlCommand("Select * from authors", conn); 

         conn.Open(); 

         SqlDataReader rdr = cmd.ExecuteReader(); 

         sp.Send(rdr); 

      } 

   } 

}

Notice that in this example I haven’t used namespaces (see below for namespaces). Build your assembly.

Next, you need to register the assembly in SQL Server 2005 with the following command

CREATE ASSEMBLY SqlServerProcs FROM N'C:\dev\SqlServerProcs\SqlServerProcs\bin\Debug\SqlServerProcs.dll'
WITH PERMISSION_SET = SAFE


Then, you need to create the stored procedure linkage between a Stored Procedure name and your managed stored procedure implementation, as shown below.

CREATE PROCEDURE GetAuthors AS EXTERNAL NAME SqlServerProcs.StoredProcs.GetAuthors

The external name parameter in the above command has the following syntax;

assemblyname.namespace/classname.methodname

  Using Namespaces.

If you defined your stored procedure implementation without using namespaces,
the “namespace/classname” part is simply the class name. If you did use namespaces,
the “namespace/classname” part is written as either "namespace.classname"
or [namespace.classname]. Assuming the above example had used a namespace name of "MyProcs" the CREATE PROCEDURE command would be written as follows;

CREATE PROCEDURE GetAuthors AS EXTERNAL NAME SqlServerProcs.[MyProcs.StoredProcs].GetAuthors

This stored procedure can now be executed just like any other normal stored procedure.

EXEC dbo.GetAuthors