Search

Loading...

Tuesday, 23 October 2012

Gotcha #121008: SELECT @Variable vs SET @Variable in SQL-Server

Introduction

Due to the nature of the work I've been doing lately, it's been a while since I've had to cut much T-SQL above and beyond standard CRUD operations; but whilst fixing a stored procedure the other day, I was bitten by this little gotcha.

The Scenario

Imagine that we have the following table in our database:

IdDescription
1Foo
2Bar
3Baz

Now take a look at the following T-SQL snippet:

DECLARE @MyVar INT

SELECT @MyVar = Id
FROM dbo.MyTable
WHERE Description = 'Foo'

IF @MyVar IS NOT NULL
 PRINT @MyVar
ELSE
 PRINT '[Null]'
 
SELECT @MyVar = Id
FROM dbo.MyTable
WHERE Description = 'Boo'

IF @MyVar IS NOT NULL
 PRINT @MyVar
ELSE
 PRINT '[Null]'

Now what would you expect the output to be? Maybe:

1
[Null]

Wrong! The actual output is:

1
1

So why is this? You will note that the second SELECT statement has been written to deliberately return 0 rows from the database. Now when SELECTing into a variable which already contains data; using a query which does not return any rows, the existing value of the variable is left intact and not overwritten.

In this trivial example the result is hardly critical, but you can imagine that if a stored procedure (or script or whatever) pivoted around testing @MyVar for NULL your execution flow could very easily go off on an unexpected tangent.

If we make the following changes to our code, the result will be as we expected:

DECLARE @MyVar INT

SET @MyVar = ( SELECT Id
    FROM dbo.MyTable
    WHERE Description = 'Foo')

IF @MyVar IS NOT NULL
 PRINT @MyVar
ELSE
 PRINT '[Null]'

SET @MyVar = ( SELECT Id
    FROM dbo.MyTable
    WHERE Description = 'Boo')

IF @MyVar IS NOT NULL
 PRINT @MyVar
ELSE
 PRINT '[Null]'

When using the SET statement, the value of @MyVar will always be overwritten. If the query does not return any data, the value @MyVar will be cleared.

Conclusion

When putting data into a variable which you may later want to test for NULL, it is safer to use the SET statement, rather than the SELECT statement.

Thursday, 1 December 2011

Partial Validation with Data Annotations in ASP.NET MVC

Introduction

This article is a follow-up to Andy West's blog post about performing a conditional validation when using .NET data annotations on a model in MVC.

Now I am not going to go into the arguments about the use of DTOs vs 'real' model objects; or using separate vs shared model objects across different views. As many others have noted (from what I've seen on the Web), if you are working with 'real' model objects using data annotations, there is a clear need to be able to exclude certain validations depending on the specific scenario.

The Scenario

Let's look at a simple product/category model:

// C#
public class Category
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string Description { get; set; }
}

public class Product
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string Description { get; set; }
    [Required]
    public Category Category { get; set; }
    [Required]
    public decimal Price { get; set; }
}
' Visual Basic
Public Class Category

    <Required()>
    Public Property Id As Integer
    <Required()>
    Public Property Name As String
    <Required()>
    Public Property Description As String

End Class

Public Class Product

    <Required()>
    Public Property Id As Integer
    <Required()>
    Public Property Name As String
    <Required()>
    Public Property Description As String
    <Required()>
    Public Property Category As Category
    <Required()>
    Public Property Price As Decimal
End Class

As you can see, this is a very simple model where all properties on the two classes are decorated with the Required attribute.

Now let's take a simple action to create a new product:

// C#
[HttpPost]
public ActionResult Create(Product product)
{
    if (ModelState.IsValid)
    {
        // Do something here, probably put the product in some database.
        return View("SuccessPage");
    }
    else
    {
        // Do something else here, probably return to the view showing the errors.
        return View();
    }
}
' Visual Basic
<HttpPost()>
Public Function Create(ByVal product As Product) As ActionResult
    If ModelState.IsValid
        ' Do something here, probably put the product in some database.
        Return View("SuccessPage")
    Else
        ' Do something else here, probably return to the view showing the errors.
        Return View
    End If
End Function

Now our data annotations specify that a Product must have a Category that, in turn, must have values for its Id, Name, and Description properties. However, when we post back to the above action, do we really need to specify the name and description for the product's category? The answer is probably not. After all it is likely that at the time of product creation, the category already exists in our data store and that the user picked the category from a drop-down list (or similar) of current categories. In that case we are not really interested in the category's name and description. We are only really interested in the category's ID, so we can assigned it to the product and thus satisfy any data integrity constraints (e.g. database foreign keys) we have on our data.

However if we just post back the category ID, the model-state validation will fail because of the Required attributes on the Name and Description properties. However, we do not want to get rid of these attributes because elsewhere on the system, on the category creation view for example, we want to make sure the user specifies a name and description for any new categories they create.

So, what are we to do?

The Solution

This is where the IgnoreModelErrors attribute comes in. It allows us to specify a comma-separated string of model-state keys for which we wish to ignore any validation errors. So, in our example, we could decorate our action like this:

// C#
[HttpPost]
[IgnoreModelErrors("Category.Name, Category.Description")]
public ActionResult Create(Product product)
{
    // Code omitted for brevity.
}
' Visual Basic
<HttpPost()>
<IgnoreModelErrors("Category.Name, Category.Description")>
Public Function Create(ByVal product As Product) As ActionResult
    ' Code omitted for brevity.
End Function

Additional Options

The IgnoreModelErrors attribute has a couple of additional options worth mentioning:

Firstly, the attribute supports the '*' wildcard when specifying model-state keys. So if, for example, we used "Category.*", validation errors for any sub-property of the Category property will be ignored. However, if instead we used "*.Description", validation errors for the Description sub-property of any property will be ignored.

Secondly, the attribute also supports collections: For example, if the Product contained a property Categories which returned a IList<Category>, we could use "Categories[0].Description" to specify validation errors for the Description property of the first Category object in the list. We can use 1, 2, 3 etc. as the indexer to specify the second, third, fourth etc. Category as required. Omitting the indexer, e.g.: "Categories[].Description specifies all validation errors for the Description property of any Category object in the list.

The Code

The code for the IgnoreModelErrors attribute is shown below:

// C#
public class IgnoreModelErrorsAttribute : ActionFilterAttribute
{
    private string keysString;

    public IgnoreModelErrorsAttribute(string keys)
        : base()
    {
        this.keysString = keys;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState;
        string[] keyPatterns = keysString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        for (int i = 0; i < keyPatterns.Length; i++)
        {
            string keyPattern = keyPatterns[i]
                .Trim()
                .Replace(@".", @"\.")
                .Replace(@"[", @"\[")
                .Replace(@"]", @"\]")
                .Replace(@"\[\]", @"\[[0-9]+\]")
                .Replace(@"*", @"[A-Za-z0-9]+");
            IEnumerable<string> matchingKeys = modelState.Keys.Where(x => Regex.IsMatch(x, keyPattern));
            foreach (string matchingKey in matchingKeys)
                modelState[matchingKey].Errors.Clear();
        }
    }
}
' Visual Basic
Public Class IgnoreModelErrorsAttribute
    Inherits ActionFilterAttribute

    Private keysString As String

    Public Sub New(ByVal keys As String)
        MyBase.New()
        Me.keysString = keys
    End Sub

    Public Overrides Sub OnActionExecuting(ByVal filterContext As ActionExecutingContext)
        Dim modelState As ModelStateDictionary = filterContext.Controller.ViewData.ModelState
        Dim keyPatterns As String() = keysString.Split(New Char() {","}, StringSplitOptions.RemoveEmptyEntries)
        For i As Integer = 0 To keyPatterns.Length - 1 Step 1
            Dim keyPattern As String = keyPatterns(i) _
                                       .Replace(".", "\.") _
                                       .Replace("[", "\[") _
                                       .Replace("]", "\]") _
                                       .Replace("\[\]", "\[[0-9]+\]") _
                                       .Replace("*", "[A-Za-z0-9]+")
            Dim matchingKeys As IEnumerable(Of String) = modelState.Keys.Where(Function(x) Regex.IsMatch(x, keyPattern))
            For Each matchingKey As String In matchingKeys
                modelState(matchingKey).Errors.Clear()
            Next
        Next
    End Sub

End Class

As you can see the code is very straightforward. Firstly we split the comma-separated string into its component keys. We then transform each key into a regular expression which we then use to query the model-state for any keys which match. For any matches which are found, we clear any validation errors which may have been raised.

Summary

The IgnoreModelErrors attribute provides another alternative, and more declarative, method for performing partial or selective validation when posting model data back to an action in MVC. At present it provides only a basic syntax for matching keys in the model-state dictionary, but it could easily be expanded upon to handle more complex queries.

Wednesday, 5 October 2011

Serialization 101 - Part III: XML Serialization

Introduction

In the final part in this series on serialization, we are going to take a look at XML serialization.

Now unlike binary and SOAP serialization, which we looked at in parts I and II respectively, XML serialization requires the use of a completely different framework.

The Math Game

For this article, we are going to use the example of a simple math game. The user is presented with a random sum and they have to provide the answer, gaining two points for each correct answer and losing 1 point for each incorrect answer. The application keeps the score and allows the user to save a game in progress to continue it at a later point. The class diagram is shown below (click to zoom):

Making Objects Serializable

Now, unlike binary and SOAP serialization where objects are non-serializable unless explicitly stated otherwise through use of the Serializable attribute; with XML serialization all objects are implicitly serializable and do not require the use of such an attribute. That said, as you will see below, we still end up liberally decorating our classes with various different attributes to further refine the serialization process.

Firstly, let's take a look at the Question class:

// C#
[XmlRoot(ElementName = "question")]
public class Question
{
    [XmlAttribute(AttributeName = "left")]
    public int LeftOperand { get; set; }

    [XmlAttribute(AttributeName = "right")]
    public int RightOperand { get; set; }

    [XmlAttribute(AttributeName = "operator")]
    public Operator Operator { get; set; }
}
' Visual Basic
<XmlRoot(ElementName:="question")>
Public Class Question

    <XmlAttribute(AttributeName:="left")>
    Public Property LeftOperand As Integer

    <XmlAttribute(AttributeName:="right")>
    Public Property RightOperand As Integer

    <XmlAttribute(AttributeName:="operator")>
    Public Property [Operator] As [Operator]

End Class

Note how the class is decorated with an XmlRoot attribute. This attribute controls how an object of this class should be serialized if it is the root element of the XML document. In this case we use it to make sure the element is rendered in lower case.

Next, notice the three properties of the class are decorated with XmlAttribute attributes. By default the XML serializer serializes all properties as XML elements. By using this attribute, we override this behaviour, serializing the properties as attributes instead. At the same time, we are also changing the attribute name to something more concise.

Next we will take a look at the UserAnswer class:

// C#
[XmlRoot(ElementName = "answer")]
public class UserAnswer
{
    [XmlElement(ElementName = "question")]
    public Question Question { get; set; }

    [XmlElement(ElementName = "value")]
    public int Answer { get; set; }

    public bool IsCorrect
    {
        get { return Answer == Question.CorrectAnswer; }
    }
}
' Visual Basic
<XmlRoot(ElementName:="answer")>
Public Class UserAnswer

    <XmlElement(ElementName:="question")>
    Public Property Question As Question

    <XmlElement(ElementName:="value")>
    Public Property Answer As Integer

    Public ReadOnly Property IsCorrect As Boolean
        Get
            Return Answer = Question.CorrectAnswer
        End Get
    End Property
End Class

Here we are using the XmlElement attribute to override the default names given to the XML elements upon serialization. By default, the serializer will name any XML elements or attributes exactly as the corresponding property is named, however in our example, we want the elements in lower case.

It is also worth noting that because the IsCorrect property is read-only, it will not be serialized. However, if the property was not read-only and we didn't want it serialized, we would simply decorated it with the XmlIgnore attribute.

Now finally, the Game class:

// C#
[XmlRoot(ElementName = "game")]
public class Game
{
    [XmlArray(ElementName = "answers")]
    [XmlArrayItem(ElementName = "answer")]
    public UserAnswersCollection Answers { get; set; }

    public int Score
    {
        get { return (Answers.Count(x => x.IsCorrect) * 2) - Answers.Count(x => !x.IsCorrect); }
    }
}
' Visual Basic
<XmlRoot(ElementName:="game")>
Public Class Game

    <XmlArray(ElementName:="answers")>
    <XmlArrayItem(ElementName:="answer")>
    Public Property Answers As UserAnswersCollection

    Public ReadOnly Property Score As Integer
        Get
            Return (Answers.Where(Function(x) x.IsCorrect).Count() * 2) - Answers.Where(Function(x) Not x.IsCorrect).Count()
        End Get
    End Property
End Class

Note how the Answers property is decorated with both an XmlArray and XmlArrayItem attribute. This specifies that the Answers collection is to be serialized as an array with an element name of "answers". Each answer in the collection will be serialized as an individual element named "answer".

OK, Let's Start Serializing

In order to serialize and de-serialize our objects, we need to use an XmlSerializer object. The example below shows how to use the XmlSerializer to save the current game:

// C#
public void SaveGame(Game game, string fileName)
{
    using (Stream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Game));
        serializer.Serialize(fileStream, game);
    }
}
' Visual Basic
Public Sub SaveGame(ByVal game As Game, ByVal fileName As String)
    Using fileStream As Stream = New FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)
        Dim serializer As XmlSerializer = New XmlSerializer(GetType(Game))
        serializer.Serialize(fileStream, game)
    End Using
End Sub

As with binary and SOAP serialization, we can serialize to any object which inherits from the Stream class. In our example, we use a FileStream object, but this could just as easily have been a NetworkStream object.

Finally, for completeness, the code for loading a previously-saved game from disk

// C#
public Game LoadGame(string fileName)
{
    using (Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        XmlSerializer deserializer = new XmlSerializer(typeof(Game));
        return (Game)deserializer.Deserialize(fileStream);
    }
}
' Visual Basic
Public Function LoadGame(ByVal fileName As String) As Game
    Using fileStream As Stream = New FileStream(fileName, FileMode.Create, FileAccess.Read, FileShare.Read)
        Dim deserializer As XmlSerializer = New XmlSerializer(GetType(Game))
        Return DirectCast(deserializer.Deserialize(fileStream), Game)
    End Using
End Function

Below is an example of the XML produced when serializing a game:

<?xml version="1.0"?>
<game xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <answers>
    <answer>
      <question left="6" right="6" operator="Addition" />
      <value>12</value>
    </answer>
    <answer>
      <question left="8" right="2" operator="Multiplication" />
      <value>16</value>
    </answer>
    <answer>
      <question left="8" right="8" operator="Division" />
      <value>1</value>
    </answer>
    <answer>
      <question left="1" right="3" operator="Multiplication" />
      <value>4</value>
    </answer>
  </answers>
</game>

A Quick Note on SOAP

As the SOAP message format is based on XML, you can use XML serialization for serializing and de-serializing objects to and from SOAP messages. Indeed, this method is now preferred over using the SoapFormatter discussed in the previous article. You can find further details on this in the MSDN documentation.

Summary

XML serialization provides the means to serialize objects in a human-readable form and uses a completely separate framework from binary and SOAP serialization. It is also the preferred method for serializing objects to SOAP messages.

You can download the source code for the math game here.

Friday, 16 September 2011

Serialization 101 - Part II: SOAP Serialization

Introduction

In Part I of this series, we took a brief look at how to perform binary serialization to persist the state of objects in our application to some form of permanent storage medium and then retrieve them again at a later point in time

In this article we are going to take a quick look at SOAP serialization. This article is going to be quite brief as I don't intend going into a massive amount of detail, mainly because SOAP serialization is now generally considered to be superseded by XML serialization, which we will look at in Part III.

Upgrading the Farmyard

Continuing with the sample farmyard application we looked at in Part I, how do we go about the process of upgrading our software to support saving in SOAP format, as well as binary? Well, as I hinted at in Part I, SOAP serialization uses the same mechanism as binary serialization.

Therefore, as all the relevant classes have already been decorated with the appropriate attributes to support serialization, all that should be required is to substitute the BinaryFormatter object for a SoapFormatter object when saving the data:

// C#
public enum FileFormat { Binary = 1, Soap = 2 }

using (Stream fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
{
    IFormatter formatter;
    switch ((FileFormat)saveFarmyardDialog.FilterIndex)
    {
        case FileFormat.Binary: formatter = new BinaryFormatter(); break;
        case FileFormat.Soap: formatter = new SoapFormatter(); break;
        default: formatter = new BinaryFormatter(); break;
    }
    formatter.Serialize(fileStream, this.farmyard);
}
' Visual Basic
Public Enum FileFormat
    Binary = 1
    Soap = 2
End Enum

Using fileStream As Stream = New FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)
    Dim formatter As IFormatter
    Select Case DirectCast(saveFarmyardDialog.FilterIndex, FileFormat)
        Case FileFormat.Binary
            formatter = New BinaryFormatter()
        Case FileFormat.Soap
            formatter = New SoapFormatter()
        Case Else
            formatter = New BinaryFormatter()
    End Select
    formatter.Serialize(fileStream, farmyard)
End Using

...and for loading the data:

// C#
using (Stream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    IFormatter formatter;
    switch ((FileFormat)loadFarmyardDialog.FilterIndex)
    {
        case FileFormat.Binary: formatter = new BinaryFormatter(); break;
        case FileFormat.Soap: formatter = new SoapFormatter(); break;
        default: formatter = new BinaryFormatter(); break;
    }
    farmyard = (Farmyard)formatter.Deserialize(fileStream);
}
' Visual Basic
Using FileStream As Stream = New FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)
    Dim formatter As IFormatter
    Select Case DirectCast(loadFarmyardDialog.FilterIndex, FileFormat)
        Case FileFormat.Binary
            formatter = New BinaryFormatter()
        Case FileFormat.Soap
            formatter = New SoapFormatter()
        Case Else
            formatter = New BinaryFormatter()
    End Select
    farmyard = DirectCast(formatter.Deserialize(FileStream), Farmyard)
End Using

As you can see, we use a simple switch...case statement, based on the selected file format from the load/save dialog, to determine whether to instantiate a BinaryFormatter or a SoapFormatter object. As before, we are serializing our objects to disk; however if we were using SOAP serialization in a real-world application, we would more likely be serializing to a NetworkStream.

And that it is it, that should be all that is needed to for our application to work. Well, actually no. Try and run the application as it is, and you will get a dirty great exception when you try to serialize using the SoapFormatter. The reason? Apparently the SoapFormatter does not support the serialization of generics, and is therefore throwing its toys out of the pram when it encounters our List<Animal> object which we have used to underpin our farmyard.

As a (not too elegant) workaround, I have used the following code to shuttle the data to and from a non-generic collection when serializing and de-serializing:

// C#
[NonSerialized]
private List<Animal> animals;

private ArrayList animalsForSerialization;

[OnSerializing]
private void OnSerializing(StreamingContext context)
{
    animalsForSerialization = new ArrayList();
    foreach (Animal animal in animals)
        animalsForSerialization.Add(animal);
}

[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
    NewDay();
    animals = new List<Animal>();
    foreach (Animal animal in animalsForSerialization)
        animals.Add(animal);
    animalsForSerialization = null;
}
' Visual Basic
<NonSerialized()>
Private animals As List(Of Animal)

Private animalsForSerialization As ArrayList

<OnSerializing()>
Private Sub OnSerializing(ByVal context As StreamingContext)
    animalsForSerialization = New ArrayList
    For Each animal As Animal In animals
        animalsForSerialization.Add(animal)
    Next
End Sub


<OnDeserialized()>
Private Sub OnDeserialized(ByVal context As StreamingContext)
    NewDay()
    For Each animal As Animal In animalsForSerialization
        animals.Add(animal)
    Next
    animalsForSerialization = Nothing
End Sub

Note the use of the OnSerializing and OnDeserialized attributes we discussed in Part I to control the serialization process. Also, as a result of our change, we won't be able to load any farmyards which were saved with the previous version of the application. A more elegant solution would, of course, allow for backward compatibility

Summary

SOAP serialization uses the same mechanism as binary serialization; however one major limitation is the inability to serialize generics.

The source code for the farmyard application can be downloaded here.

Next: XML Serialization

Wednesday, 14 September 2011

Serialization 101 - Part I: Binary Serialization


Foreword

It's been several weeks since I last posted on my blog, due to various other commitments over the summer. Now that I have a bit more time again, I thought now was a good time to get going again.
When deciding what topic to kick-start with, I decided upon another 101 mini-series this time looking at serialization using the .NET framework. For those of you new to .NET, hopefully this should serve as good primer for getting into serialization; and the more experienced developers amongst you perhaps this will serve as a useful refresher (as indeed it did for me, whilst I was preparing the sample code!).

Introduction

Serialization is to process of persisting the state of an object to some form of permanent storage medium. This is achieved by converting public and private fields of a objects, as well as its class name and assembly name, to a stream of bytes which is then persisted onto the selected storage medium. De-serialization, as the name suggests, reverses the process. As an aside, serialization and de-serialization are sometimes referred to as 'dehydrating' and 'rehydrating' an object respectively.

There are three main mechanisms for serialization in .NET:

  • Binary serialization: Persistence of objects to a binary storage format. We will look at this in this article.
  • SOAP serialization: Persistence of objects in SOAP (Simple Object Access Protocol) format.
  • XML serialization. Persistence of objects as XML.

The Serialization Farmyard

Continuing our farmyard theme from previous articles, the Serialization Farmyard is a simple Windows application in which the user can create a new farmyard, populate it with animals and then save it to disk for later use. The user can also reload a previous farmyard from disk and and edit it as they see fit. The application uses binary serialization/de-serialization for saving and loading farmyards, and the object model is shown below (click to zoom):

Making Objects Serializable

Now lets take a quick look at the Animal class:

// C#
[Serializable]
public abstract class Animal
{
    public virtual string Name { get; set; }
    public virtual int Arrived { get; set; }

    public abstract int SpaceRequired { get; }
}
' Visual Basic
<Serializable()>
Public MustInherit Class Animal

    Public Property Name As String
    Public Property Arrived As Integer

    Public MustOverride ReadOnly Property SpaceRequired As Integer

End Class

Notice how the class has been decorated with a Serializable attribute? This is how we mark a class as being binary serializable (and SOAP serializable too. XML serialization uses to totally different mechanism). It is important to note however, that for an object to be fully serializable, not only must it be decorated with Serializable attribute, but the types of all of its members must too be either serializable or explicitly marked as non-serialized (we will look at an example of this later). If not, then an exception will be thrown at run-time. In the above example, all the members are of primitive types which are always serializable.

Now you could quite easily assume that if a base class is marked as serializable, any sub-classes would therefore also be serializable. This however is not the case, as the Serializable attribute is not inheritable. Therefore any sub-classes of our abstract Animal class must therefore also be explicitly decorated with the Serializable, for example:

// C#
[Serializable]
public class Sheep : Animal
{
    public override int SpaceRequired
    {
        get { return 3; }
    }
}
' Visual Basic
<Serializable()>
Public Class Sheep
    Inherits Animal

    Public Overrides ReadOnly Property SpaceRequired As Integer
        Get
            Return 3
        End Get
    End Property
End Class

Also, don't think you can get away with just decorating your sub-classes with the Serializable attribute! All classes up the inheritance hierarchy must also be suitably decorated otherwise an exception will be thrown at run-time.

Now let's look at the Farmyard class. Firstly, the class definition:

// C#
[Serializable]
public class Farmyard
{
    private List<Animal> animals;
    
    public string Name { get; set; }
    public int Day { get; set; }
    public int Capacity { get; set; }
}
' Visual Basic
<Serializable()>
Public Class Farmyard

    Private animals As List(Of Animal)

    Public Property Name As String
    Public Property Day As Integer
    Public Property Capacity As Integer
End Class

As before, the class is decorated with the Serializable attribute. The properties Name, Day and Capacity are all primitive types and therefore can be serialized. If you look at the MSDN documentation, you will see that the List<T> class is serializable, providing that the type of T is also serializable. In this case it will be, as our Animal class has been decorated with the Serializable attribute.

Now, take a look at the weather property:

// C#
[NonSerialized]
private Weather weather;

public Weather Weather
{
    get { return weather; }
    set { weather = value; }
}
' Visual Basic
<NonSerialized()>
Private _weather As Weather

Public Property Weather As Weather
    Get
        Return _weather
    End Get
    Set(value As Weather)
        _weather = value
    End Set
End Property

This shows how to prevent a member from being serialized at run-time: simply decorate it with the NonSerialized attribute. A common reason for doing this is when a member is of a type that is not serializable, however in our example we don't persist the Weather property because we will be assigning it a new value, when it is loaded from disk (see below).

It's also worth noting that the NonSerialized attribute can only be applied to fields and not properties. Therefore we cannot use the "auto-properties" feature of C#/VB, and have to return to the pattern of declaring a private member variable and exposing it through a separate public property.

Now for a real gotcha: As we know, when serializing an object, the .NET serializer will attempt to serialize all members of that object which are not explicitly marked as being non-serializable. This also includes any delegates or events, or more specifically, any handlers that are currently wired up to them. In an application such as this, the objects wired up to any events are often UI objects which are generally not serializable in the first place; and even if they were, we wouldn't want to persist them to disk. Therefore the delegates which handle any events must also be marked as non-serialized:

// C#
[NonSerialized]
private FarmyardEventHandler animalAdded;

public event FarmyardEventHandler AnimalAdded
{
    add { animalAdded = (FarmyardEventHandler)Delegate.Combine(animalAdded, value); }
    remove { animalAdded = (FarmyardEventHandler)Delegate.Remove(animalAdded, value); }
}
' Visual Basic
<NonSerialized()>
Private _animalAdded As FarmyardEventHandler

Public Custom Event AnimalAdded As FarmyardEventHandler
    AddHandler(value As FarmyardEventHandler)
        _animalAdded = DirectCast([Delegate].Combine(_animalAdded, value), FarmyardEventHandler)
    End AddHandler

    RemoveHandler(value As FarmyardEventHandler)
        _animalAdded = DirectCast([Delegate].Remove(_animalAdded, value), FarmyardEventHandler)
    End RemoveHandler

    RaiseEvent(sender As Object, eventArgs As System.EventArgs)
        _animalAdded(sender, eventArgs)
    End RaiseEvent
End Event

In similarity to non-serialized properties we have to declare a private delegate, decorated with the NonSerialized attribute, and expose it through a public event.

Controlling and Customising Serialization

There are two main approaches to controlling and customising binary serialization in the .NET framework. One is to implement ISerializable interface (which we won't look at in this article, but you can find more information in the MSDN documentation); but the Microsoft-recommended approach is to use the following attributes to decorate certain methods in your class which will be executed at certain points during the serialization/de-serialization process:

  • OnSerializing
  • OnSerialized
  • OnDeserializing
  • OnDeserialized

We can see an example of this in the Farmyard class of our application:

// C#
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
    NewDay();
} 

public void NewDay()
{
    Day++;
    Random random = new Random();
    Weather = (Weather)random.Next(0, 4);
    OnNewDay(new FarmyardEventArgs());
}
' Visual Basic
<OnDeserialized()>
Private Sub OnDeserialized(ByVal context as StreamingContext)
    NewDay()
End Sub

Public Sub NewDay()
    Day = Day + 1
    Dim random As Random = New Random()
    Weather = DirectCast(random.Next(0, 4), Weather)
    OnNewDay(New FarmyardEventArgs())
End Sub

As you can see, the method is decorated with the OnDeserialized attribute which means it will be executed once de-serialization is complete. This method calls the NewDay() method, which increments the day and sets the Weather property to a random value. Remember, we are explicitly not serializing the weather when we save the farmyard to disk.

Note also, that the method takes an object of type StreamingContext, and although we don't use it in our example, it provides information about the source and destination of the current serialization stream, as well as any caller-defined data. As always, you can read more about this in the MSDN documentation.

Performing Serialization

OK, so we've looked at how we declare which classes and members are to be serialized (or not, as the case may be!), but how do we actually go about serializing our data to storage and retrieving it later? Well, the answer is we need to use a BinaryFormatter object. This object has two methods: Serialize() and Deserialize() for serialization and de-serialization respectively. Here is the code for saving a farmyard to disk:

// C#
using (Stream fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
{
    IFormatter formatter = new BinaryFormatter();
    formatter.Serialize(fileStream, farmyard);
}
' Visual Basic
Using fileStream As Stream = New FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)
    Dim formatter As IFormatter = New BinaryFormatter
    formatter.Serialize(fileStream, farmyard)
End Using

And for loading a saved farmyard from disk:

// C#
using (Stream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    IFormatter formatter = new BinaryFormatter();
    farmyard = (Farmyard)formatter.Deserialize(fileStream);
}
' Visual Basic
Using FileStream As Stream = New FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)
    Dim formatter As IFormatter = New BinaryFormatter
    farmyard = DirectCast(formatter.Deserialize(FileStream), Farmyard)
End Using

Note how the Serialize() and Deserialize() methods of the BinaryFormatter object accept an object of type Stream. In our example, we are using a FileStream object as we are serializing our farmyard to disk, but we could just as easily have used a MemoryStream or NetworkStream for serializing to memory or over a network respectively.

Note also that, when de-serializing an object, a cast is required as the Deserialize() method simply returns a method of type Object.

Summary

Binary serialization provides an easy mechanism for persisting objects to permanent storage. By the use of various attributes we can control and refine the serialization process to suit our particular needs.

The source code for the farmyard application can be downloaded here. Unfortunately, due to time constraints, the source code is in C# only.

Next: SOAP Serialization

Thursday, 30 June 2011

A D.I.Y. Lightbox


Introduction

OK, so JavaScript/jQuery lightboxes are ten-a-penny. Just go to the jQuery plugin repository and take your pick! Indeed for long time, I did just that. After all, there's no point re-inventing the wheel is there??

However, I recently had to write my own JavaScript lightbox from scratch. So I thought now was a good opportunity to share my experiences with you, as well as the code, which you are free to use should you want or need to develop your own lightbox or maybe want to develop my lightbox further.

LiteBox

The fruits of my labours I have (unimaginatively) named 'LiteBox', mainly to try and emphasise the fact that it makes no pretences to doing anything fancy! Also, as I am a shameless jQuery whore, I am also assuming prior experience of jQuery. If you are unfamiliar with jQuery, you can find more information here.

The Basics

LiteBox is implemented as a jQuery plugin. The signature is as follows:

jQuery.lightbox( url, [options] )

The url parameter is mandatory and specifies the URL of the image to be shown in the lightbox. The options parameter is a set of optional key/value pairs for configuring the lightbox:

KeyDescriptionDefault Value
titleThe text to be used for the tooltip and alt attribute of the image.The URL of the image.
showCloseButtonWhether or not to show the 'close' button on the lightbox.True.
closeButtonPositionThe position of the 'close' button.
Can be one of either 'top-left', 'top-right', 'bottom-left' or 'bottom-right'.
Ignored if showCloseButton is false.
'top-right'.
animationSpeedThe speed of the animation.
Can be one of either 'slow', 'medium' or 'fast'; or the length of the animation in milliseconds.
'medium' (≡ 500ms)

Getting into the Code

Before we get delve too deeply into the JavaScript, it's helpful to have a look at the CSS:

.jquery-litebox-lightbox 
{
 position: absolute;
 background-color: Transparent;
 z-index: 1001;
 margin: 0px;
 padding: 0px;
 border: 10px solid white;
}

.jquery-litebox-img
{
    margin: 0px;
    padding: 0px;
}

.jquery-litebox-close
{
    height: 25px;
    width: 25px;
    position: absolute;
    cursor: pointer;
    background-image: url(images/jquery-litebox_close.png);
    background-repeat: no-repeat;
    z-index: 1002;
}

.jquery-litebox-shadow 
{
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
 background-image: url(images/jquery-litebox_shadow.png);
 background-repeat: repeat;
}

As is customary with jQuery plugins, the first thing we need to do is merge any user options into a settings object:

var settings = {
    title: url,
    showCloseButton: true,
    closeButtonPosition: 'top-right',
    animationSpeed: 'medium'
};
$.extend(settings, options);

The next thing to do is create a <img> tag for the full-sized image, and add it to the DOM. It is important that we do this early on as we need the browser to have loaded the image in order to work with its properties, such as height and width etc. However, we make the image invisible so that it doesn't actually appear to the user yet:

var img = $('<img src="' + url + '" alt="' + settings.title + '" title="' + settings.title + '" class="jquery-litebox-img" style="display: none;" />');
$('body').append(img);

Once the browser has loaded them image, then the interesting stuff can start. By hooking into the image's load event we can create the lightbox and append the image to it:

var imgWidth = $(this).width();
var imgHeight = $(this).height();
$(this).detach().height('0px').width('0px');
var lightbox = $('<div class="jquery-litebox-lightbox"></div>').css('top', $(window).scrollTop() + 100 + 'px').css('left', ($(window).width() / 2) - (imgWidth / 2) + 'px');
var shadow = $('<div class="jquery-litebox-shadow"></div>');
$('body').append(shadow);
$('body').append(lightbox);
lightbox.append($(this));

As you can see, we firstly get the height and width of the image. We then detach the image from the DOM and set its height and width to 0px.

Next, we create the lightbox itself. Now that we know the height and width of the image, we can calculate and set the position of the top-left-hand corner of the lightbox such that it will be centred horizontally on the page and 100px from the top of the window.

We then create the lightbox shadow and append it to the DOM. We append the lightbox itself to the DOM and then append the image to the lightbox.

Now the time has come to animate our lightbox. The animation is very simple: we simply grow the image from top-left to bottom-right until the image is at its full size. This is achieved using jQuery's animate() function:

var animationTime = getAnimationTime(settings.animationSpeed);
$(this).show().animate({
    width: imgWidth,
    height: imgHeight
}, animationTime, function () {
    if (settings.showCloseButton) {
        showCloseButton(settings.closeButtonPosition);
        img.mouseover(function () {
            showCloseButton(settings.closeButtonPosition);
        });
        img.mouseout(function (e) {
            hideCloseButton(e);
        });
    }
});

When the animation is complete, we hook into the mouseover and mouseout events of the image to show and hide the close button respectively. The code for showing and hiding the button is as follows:

function showCloseButton(position) {
    var img = $("img.jquery-litebox-img");
    var imgPositionY = img.offset().top;
    var imgPositionX = img.offset().left;
    var imgHeight = img.height();
    var imgWidth = img.width();
    if ($("div.jquery-litebox-close").length == 0) {
        var close = $('<div class="jquery-litebox-close" title="Close the lightbox." style="display: none;"></div>');
        $('body').append(close);
        switch (position) {
            case 'top-left':
                close.css('top', imgPositionY).css('left', imgPositionX);
                break;
            case 'top-right':
                close.css('top', imgPositionY).css('left', (imgPositionX + imgWidth) - close.width());
                break;
            case 'bottom-left':
                close.css('top', (imgPositionY + imgHeight) - close.height()).css('left', imgPositionX);
                break;
            case 'bottom-right':
                close.css('top', (imgPositionY + imgHeight) - close.height()).css('left', (imgPositionX + imgWidth) - close.width());
                break;
            default:
                throw new Error("Buttom position must be one of either: 'top-left', 'top-right', 'bottom-left' or 'bottom-right'.");
        }
        close.click(function (e) {
            $(this).remove();
            closeLightBox();
        });
        close.show();
    }
}

function hideCloseButton(mouseEvent) {
    if (!isIn($("div.jquery-litebox-close"), mouseEvent))
        $("div.jquery-litebox-close").remove();
}

function isIn(obj, mouseEvent) {
    if (obj.length > 0) {
        var x = mouseEvent.pageX;
        var y = mouseEvent.pageY;
        var posX = obj.position().left;
        var posY = obj.position().top;
        var objX = obj.width();
        var objY = obj.height();
        return x > posX && x < posX + objX && y > posY && y < posY + objY;
    }
    else
        return false;
}

The animation time is determined by calling the getAnimationTime() function:

function getAnimationTime(speed) {
    if (typeof speed === 'string') {
        switch (speed) {
            case 'slow': return 1000;
            case 'medium': return 500;
            case 'fast': return 250;
            default:
                var parsedSpeed = parseInt(speed);
                if (!isNaN(parsedSpeed))
                    return parsedSpeed;
                else
                    throw new Error("Animation speed must be a number or one of: 'slow', 'medium' or 'fast'.");
        }
    }
    else if (typeof speed === 'number')
        return speed;
    else
        throw new Error("Animation speed must be a number or one of: 'slow', 'medium' or 'fast'.");
}

Summary

LiteBox is a very simple, lightweight jQuery lightbox, which can serve as an example for anyone wishing to develop their own solution; or as a base for anyone wishing to extend it further.

You can download the source code, along with a sample web page from here.

So can see a demo of LiteBox in action here.

Thursday, 16 June 2011

Gotcha #1167: Quoted Identifiers in Oracle


Introduction

It's been many years since I last worked with Oracle in anger, and having had to recently work with it again it's amazing how many of its "features" (I use the term guardedly) have either appeared in the intervening years, or (more likely) I had forgotten about altogether

However this one had me (and a few others in the office, I might add) puzzled for an afternoon, and once again the documentation for this is scant (or at the very least, hard to find).

The Scenario

Consider the following table:

CREATE TABLE A_TABLE 
(
   A_COLUMN NUMBER(5,0) NOT NULL ENABLE
)

Now take a look at he following queries:

SELECT A_COLUMN FROM A_TABLE -- Returns the data
SELECT A_Column FROM A_TABLE -- Returns the data

Nothing magical there. The query returns the data correctly regardless of the case used in the query, exactly as we would expect.

Now consider this table:

CREATE TABLE ANOTHER_TABLE
(
   "A_Column" NUMBER(5,0) NOT NULL ENABLE
)

When we query this table in the same way, the result is very different:

SELECT A_COLUMN FROM ANOTHER_TABLE -- ORA-00904: "A_COLUMN": invalid identifier
SELECT A_Column FROM ANOTHER_TABLE -- ORA-00904: "A_COLUMN": invalid identifier

As you can see Oracle now complains that it can't find the specified column, even when we have specified the column name with the correct case.

The Problem

The problem is caused by the fact that, not only is Oracle case-sensitive, but it also implicitly converts all identifiers to UPPER CASE. This is why, when querying A_TABLE, the case of the query doesn't matter. However, when querying ANOTHER_TABLE, the query fails as the identifier is always converted to A_COLUMN, whereas the column is actually named A_Column.

To prevent Oracle converting identifiers to upper case, they must be enclosed in double-quotes ('"'), just as when the table was created. Therefore, the following query will work:

SELECT "A_Column" FROM ANOTHER_TABLE

OK, fair enough, it's not ideal but we can live with that. However, this in itself presents another interesting scenario. Consider this table:

CREATE TABLE YET_ANOTHER_TABLE
(
   "A_COLUMN" NUMBER(5,0) NOT NULL ENABLE, 
   "A_Column" VARCHAR2(100) NOT NULL ENABLE
)

Yes, in Oracle this is a perfectly valid (if not recommended) table definition! So assuming the following data, what happens when we query it:

A_COLUMNA_Column
1050Foo
2060Bar
3070Baz
SELECT A_COLUMN FROM YET_ANOTHER_TABLE -- Returns 1050, 2060 and 3070
SELECT "A_COLUMN" FROM YET_ANOTHER_TABLE -- Returns 1050, 2060 and 3070
SELECT A_Column FROM YET_ANOTHER_TABLE -- Returns 1050, 2060 and 3070
SELECT "A_Column" FROM YET_ANOTHER_TABLE -- Returns Foo, Bar and Baz

The Moral of the Story

This clearly highlights the need for robust database standards, part of which must include whether or not to use quoted identifiers and follow that decision rigidly throughout your database. My personal recommendation would be against the use quoted identifiers as they appear to cause more problems and confusion than they are worth; not least the potential creation of "duplicate" column names.