3

SqlDependency and DateTime values

Posted in C#, Sql Server at September 22nd, 2008 by Gerrod / 3 Comments »

I ran into a problem today where I was trying to set up a SqlDependency against a table, with what I thought was a plain vanilla select statement:


private const string SqlCommandText =
    "Select ImportDate From dbo.Imports Where ImportDate = '{0:yyyyMMdd}'";

I was formatting this string with a date/time value at runtime, then passing that into my SqlCommand object, then attempting to register the dependency. Well guess what - it didn’t work! The SqlNotificationEventArgs class kept telling me that my statement was invalid. Checking the special considerations using query notifications, I couldn’t see anything wrong with my query, so I was a bit lost.

Anyway, I decided to axe the “Where” condition from my query, and sure enough, it worked! So, by process of elimination, I figured it was something to do with converting a date from a character string that was throwing it. Not sure why; this works fine when run as T-Sql, or within a normal query…

To get around this problem, I used the “best practice” approach of using a parameter in the SqlCommand object:


private const string SqlCommandText =
    "Select ImportDate From dbo.Imports Where ImportDate = @ImportDate";

protected SqlCommand CreateSqlCommand() {
    var command = new SqlCommand(SqlCommandText);
    command.Parameters.Add(new SqlParameter("ImportDate", SqlDbType.DateTime) {
        Value = PositionDate
    });

    return command;
}

To my surprise, this worked a treat! I guess in the end, it wasn’t all that surprising, however I for some reason thought that using a parameter would surely not work; hence the reason I was pre-formatting the date/time in the first place!

If you want to run sql server scripts through code, you can’t just run a standard ExecuteNonQuery() with a SqlCommand. There’s problems with ExecuteNonQuery() recognizing multiple sql statements.

Don’t fear though, if you reference the Microsoft.SqlServer.Smo and Microsoft.SqlServer.ConnectionInfo assemblies you get access to some goodies that’ll help you on your way!


using System.Configuration;
using System.Data;
using System.Data.SqlClient;

using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DBName"].ConnectionString);
Server server = new Server(new ServerConnection(connection));
server.ConnectionContext.ExecuteNonQuery(File.ReadAllText("SqlScript.sql"));

Recently I showed you how to pull sql query result sets into object instances using the XmlReader and deserialization. This article aims to show you the opposite process - persisting an object instance into a database table using serialization and Sql Server xml parsing.

The same Channel database table and entity class will be used for all examples.

Channel Table Schema


/// Represents a media channel (e.g. web, mobile, iphone, etc.)
public class Channel    {
  private int _id;
  private string _type, _description;

  public Channel(){}

  public Channel(string type, string description)  {
    _type = type;
    _description = description;
  }

  /// Get/Set the channel identifier
  public int Id {
    get { return _id; }
    set { _id = value; }
  }

  /// Get/Set the channel type (e.g. web, mobile, iphone, etc.)
  public string Type {
    get { return _type; }
    set { _type = value; }
  }

  /// Get/Set the description of the channel.
  public string Description {
    get { return _description; }
    set { _description = value; }
  }
}

The Stored Procedure

The sproc takes in an sql server xml datatype parameter. This is provided via a normal string representation of the xml. Within the SPROC you prepare the document for querying, pull out the values you require and perform the insert.

To understand the following SPROC, you need to have have a basic understanding of:

a) how your object instance xml serialization format looks


<?xml version="1.0" encoding="utf-16"?>
<Channel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Id>0</Id>
  <Type>web</Type>
  <Description>800x600 or larger browsers</Description>
</Channel>'

and

b) an understanding of xpath and xquery for pulling the values out from the xml.

Check out the - Resources - Sql Xml - for some links that might help you out.


CREATE PROCEDURE [usp_PersistChannel]
	@xmldoc XML
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE
            @iDoc int,
            @type varchar(50),
            @description varchar(255)

    EXEC sp_xml_preparedocument
           @iDoc OUTPUT,
           @xmldoc,
            '‘

    SELECT
            @Type = [Type],
            @Description = [Description]
    FROM
            OPENXML (@iDoc, ‘/Channel’,3)
	WITH (  [Type] varchar(50) ‘./Type’,
	Description] varchar(255) ‘./Description’ )

    EXEC sp_xml_removedocument @idoc

    INSERT INTO Channel VALUES (@Type, @Description)                

    SELECT @@IDENTITY
END

Data Access Code

The following is a convenience method to help aid the serialization process of an object instance.


        /// Serialize an instance of an object to a string.
        public string Serialize(object instance)
        {
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            XmlSerializer serializer = new XmlSerializer(instance.GetType(), null, new Type[0], null, null);
            serializer.Serialize(sw, instance);
            return sb.ToString();
        }

Usage Examples:



        /// Persist a Channel.
        public void Persist(Channel channel)
        {
            channel.Id = Int32.Parse(
                                Database.ExecuteScalar(
                                   Database.GetStoredProcCommand("usp_PersistChannel", Serialize(channel))
                                ).ToString()
                             );
        }
6

Using XmlReader and object Deserialization

Posted in .Net, C#, Sql Server at February 15th, 2008 by Ben / 6 Comments »

These days there seems to be an infinte number of ways to take data returned from the database and map them into your object model.

This article aims to simple show you how to generically map the sql select result set into a list of object instances in your code.

First things first, the code illustrated here requires Sql Server 2005, and .Net 2.0+ framework.

Sql Server 2005 Queries

It doesn’t matter whether you write you queries in code or by using Sql Server SProcs, simple by adding - ‘FOR XML Path(’ElementName’) - the following to the end of you Select statement, Sql Server will return your result sets in Xml.


Syntax: 

SELECT * FROM [TableName] WITH (nolock) FOR XML PATH('TableName')


Example:

SELECT * FROM [Channel] WITH (nolock) FOR XML PATH('Channel')

Results:

<Channel>
  <Id>1</Id>
  <Type>web</Type>
  <Description>standard internet</Description>
</Channel>
<Channel>
  <Id>3</Id>
  <Type>mobile</Type>
  <Description>mobile</Description>
</Channel>

The parameter passed into the Path() statement determines the top level element name - allowing you to shape the xml.

The Entity POCOs

Using the .Net built in xml to class deserialization process, the returned xml will map the top level element to a class name and each sub-element to a public property/member on the object class.

Let’s create the Plain Old C# Object to enable deserialization.


/// Represents a media channel (e.g. web, mobile, iphone, etc.)
public class Channel    {
  private int _id;
  private string _type, _description;

  public Channel(){}

  public Channel(string type, string description)  {
    _type = type;
    _description = description;
  }

  /// Get/Set the channel identifier
  public int Id {
    get { return _id; }
    set { _id = value; }
  }

  /// Get/Set the channel type (e.g. web, mobile, iphone, etc.)
  public string Type {
    get { return _type; }
    set { _type = value; }
  }

  /// Get/Set the description of the channel.
  public string Description {
    get { return _description; }
    set { _description = value; }
  }
}

NOTE: If there are differences in your database schema naming conventions to how you name things in your classes you will need to shape the xml somewhere to make the deserialization process work.

You have two options: you can decorate your class with appropriate Xml Serialization Attributes OR you can change your Sql Query.

Example: the Channel table has a column name desc but you need to map to the public property name Description.

Scenario 1: Shape the xml using the [XmlAttribute]


  [XmlAttribute("desc")]
  public string Description {
    get { return _description; }
    set { _description = value; }
  }

OR

Scenario 2: Shape the xml in your Sql Query


SELECT [Id], [Type], [Desc] as Description
FROM [Channel]
WITH (nolock)
FOR XML PATH('Channel')

Data Access Code

Ok. You know how to get the data back from the DB in Xml. Now it’s time to read the results into a list of the entity POCOs.


  private static readonly IDictionary xmlSerializers = new Dictionary();

  /// Generic helper method for reading sql server table rows into entities.
  internal IList GetEntities(DbCommand dbCommand)  {
    IList entities = new List();
    using (XmlReader reader = ((SqlDatabase) Database).ExecuteXmlReader(dbCommand))  {
      while (!reader.EOF) {
        if (reader.IsStartElement()) {
          entities.Add(Deserialize(reader.ReadOuterXml()));
        }
      }
    }
    return entities;
  }

  /// Deserialize an xml fragment into the specified Type.
  public T Deserialize(string xml)  {
    using (StringReader sr = new StringReader(xml)) {
      if (!xmlSerializers.ContainsKey(typeof (T))){
        xmlSerializers.Add(typeof (T), new XmlSerializer(typeof (T), null, new Type[0], null, null));
      }
      XmlSerializer serializer = xmlSerializers[typeof (T)];
      return (T) serializer.Deserialize(sr);
    }
  }

NOTE: I cache an XmlSerialzer instance for each Type because the .Net framework generates a class dynamically at runtime when the new XmlSerializer() constructor is called. If you have thousands of results coming back from the DB this is timely and memory consuming.

Usage Examples:


/// Get a specific Channel.
public Channel GetChannel(string name) {
  IList list = GetEntities(Database.GetStoredProcCommand(”usp_GetChannels”, 0, name));
  return (list != null && list.Count == 1) ? list[0] : null;
}

/// Get a list of all Channels.
public IList GetChannels() {
  return GetEntities(Database.GetStoredProcCommand(”usp_GetChannels”, 0, null));
}

That’s it!

Update: 18-Feb-08 I’ve written a followup article that shows how to do the reverse process - Persisting objects into database using xml serialization

1

Sql timeout - a trap for young players!

Posted in Asp.Net, Sql Server at December 22nd, 2006 by Gerrod / 1 Comment »

I was talking to one of the guys at work about Sql timeouts. He had a long running operation - about 40 seconds - so to compensate for this, he set the timeout on his connection string to one minute:


<connectionStrings>
  <add name="cs"
    connectionString="Data Source=localhost;Initial Catalog=MyDB;Connection Timeout=60;"
</connectionStrings>

Sure looks OK, but as it turns out, this wasn’t working for him. The reason is actually quite straightforward - the Connection Timeout property is used to specify how long the application will wait to establish a connection to the server before giving up and throwing an exception. In most cases (where connections are properly managed), the connection will be returned almost instantaneously, hence changing this value is usually not required.

Instead, the timeout that he needed to change was the CommandTimeout on the SqlCommand object which was being passed to the database.


  SqlCommand command = new SqlCommand();
  command.CommandTimeout = 60;

As the MSDN doco states, this property “Gets or sets the wait time before terminating the attempt to execute a command and generating an error”. And sure enough, once he changed this value to 60, everything was dandy.

1

SQL Compare

Posted in Sql Server at December 1st, 2006 by Gerrod / 1 Comment »

Recently I made the unfortunate mistake of developing against an SQL Server 2005 database server, which I mistakenly thought was an SQL Server 2000 server. I know what you’re thinking, “how could you get that wrong?!” - but the reason was that the server was previously running 2000, and I had forgotten that it was upgraded.

Anyway, this wasn’t really a problem (since I had compatibility level set to 8) - until it came to deploying the updated database. Turns out you can’t export a 2005 backup and restore it onto 2000 - no real surprises there! So my next option was to generate scripts to recreate the database on a SQL Server 2000 server, but guess what - the Generate Scripts tool in 2005 does NOT generate scripts compatible with 2000. Now that was a surprise, given that one of the options actually allows you to specify that the scripts are for Sql Server 2000!

After searching around the net for a solution, I concluded that my best option was to restore an old database backup, and then manually go through and make the changes. Clearly not a good option, since it was very likely that I’d be missing things left right and centre, but what choice did I have?

Lucky for me, a colleague happened to pay me a visit just as I was about to start, and told me that he had had my exact same problem only a few weeks ago. Better still, he had a tool which was effectively a diff/merge tool which worked across database versions. Sweet!

So I checked it out - RedGate’s SQL Compare - and found it an absolute delight to use. It was able to update about 95% of the changes without manual intervention. The other 5% was also a simple change - since SQL Compare had generated a script to do the upgrades, all I had to do was make a few minor tweaks.

My only complaint with SQL Compare is that you can’t tell it not to synchronize/compare extended properties of objects. (Extended properties define stuff like layouts of diagrams, etc). If I could have turned this off, it would have been able to do the entire upgrade without any intervention from me. Still, overall, this is an awesome piece of software, and it gets my highest recommendation.

Have you ever wanted to script all the db objects in your database and customize how you format the scripts?

So have I!

Ideally, I wanted to script all the db objects (eg. procs, udfs, grants, defaults, permissions, tables, etc) and have each object output into a separate file and formatted a particular way.

So I’ve created a couple of sprocs that allow you to do this.

Usage:
EXEC dbo.CreateSqlScripts
‘<directory>’, ‘<sqlServerInstance>’, ‘<dbname>’, ‘<object type or null>’, ‘<formattingoptions>’

Example:
EXEC dbo.CreateSqlScripts
‘c:\temp\sql\’, ‘localhost\SQLLOCALMACHINE’, ‘MyDB’, NULL, 73879 — all db objects

Example:
EXEC dbo.CreateSqlScripts
‘c:\temp\sql\’, ‘localhost\SQLLOCALMACHINE’, ‘MyDB’, ‘P’, 73879 — stored procedures only

A bonus feature of having your db scripted out is that you can use a 3rd party program like DBGhost to generate a new db from the scripts directory.

This is really handy if you want to have your db source controlled.