tag:blogger.com,1999:blog-52106934370120931372024-03-13T15:17:33.487+00:00MrB's BlogMrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-5210693437012093137.post-59789596407601390582012-10-23T13:55:00.002+01:002012-10-23T13:56:23.908+01:00Gotcha #121008: SELECT @Variable vs SET @Variable in SQL-Server<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a>
<h2>Introduction</h2>
<p>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.</p>
<h2>The Scenario</h2>
<p>Imagine that we have the following table in our database:<p>
<table class="datatable"><thead>
<tr><th>Id</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>Foo</td></tr>
<tr><td>2</td><td>Bar</td></tr>
<tr><td>3</td><td>Baz</td></tr>
</tbody>
</table>
<p>Now take a look at the following T-SQL snippet:</p>
<pre lang="sql" class="brush: sql">
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]'
</pre>
<p>Now what would you expect the output to be? Maybe:</p>
<pre>
1
[Null]
</pre>
<p>Wrong! The actual output is:</p>
<pre>
1
1
</pre>
<p>So why is this? You will note that the second <code>SELECT</code> 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.</p>
<p>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 <code>@MyVar</code> for <code>NULL</code> your execution flow could very easily go off on an unexpected tangent.<p>
<p>If we make the following changes to our code, the result will be as we expected:</p>
<pre lang="sql" class="brush: sql">
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]'
</pre>
<p>When using the <code>SET</code> statement, the value of <code>@MyVar</code> will <strong>always</strong> be overwritten. If the query does not return any data, the value <code>@MyVar</code> will be cleared.</p>
<h2>Conclusion</h2>
<p>When putting data into a variable which you may later want to test for <code>NULL</code>, it is safer to use the <code>SET</code> statement, rather than the <code>SELECT</code> statement.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-2443450665483265802011-12-01T14:24:00.001+00:002011-12-01T20:11:00.641+00:00Partial Validation with Data Annotations in ASP.NET MVC<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a>
<h2>Introduction</h2>
<p>This article is a follow-up to <a href="http://andrewtwest.com/2011/01/10/conditional-validation-with-data-annotations-in-asp-net-mvc/">Andy West's blog post</a> about performing a conditional validation when using .NET data annotations on a model in MVC.</p>
<p>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.</p>
<h2>The Scenario</h2>
<p>Let's look at a simple product/category model:</p>
<pre lang="cs" class="brush: csharp">
// 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; }
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>As you can see, this is a very simple model where all properties on the two classes are decorated with the <code>Required</code> attribute.</p>
<p>Now let's take a simple action to create a new product:</p>
<pre lang="cs" class="brush: csharp">
// 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();
}
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Now our data annotations specify that a <code>Product</code> must have a <code>Category</code> that, in turn, must have values for its <code>Id</code>, <code>Name</code>, and <code>Description</code> 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.<p>
<p>However if we just post back the category ID, the model-state validation will fail because of the <code>Required</code> attributes on the <code>Name</code> and <code>Description</code> 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.</p>
<p>So, what are we to do?</p>
<h2>The Solution</h2>
<p>This is where the <code>IgnoreModelErrors</code> 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:</p>
<pre lang="cs" class="brush: csharp">
// C#
[HttpPost]
[IgnoreModelErrors("Category.Name, Category.Description")]
public ActionResult Create(Product product)
{
// Code omitted for brevity.
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' Visual Basic
<HttpPost()>
<IgnoreModelErrors("Category.Name, Category.Description")>
Public Function Create(ByVal product As Product) As ActionResult
' Code omitted for brevity.
End Function
</pre>
<h4>Additional Options</h4>
<p>The <code>IgnoreModelErrors</code> attribute has a couple of additional options worth mentioning:</p>
<p>Firstly, the attribute supports the '*' wildcard when specifying model-state keys. So if, for example, we used <code>"Category.*"</code>, validation errors for any sub-property of the <code>Category</code> property will be ignored. However, if instead we used <code>"*.Description"</code>, validation errors for the <code>Description</code> sub-property of any property will be ignored.</p>
<p>Secondly, the attribute also supports collections: For example, if the <code>Product</code> contained a property <code>Categories</code> which returned a <code>IList<Category></code>, we could use <code>"Categories[0].Description"</code> to specify validation errors for the <code>Description</code> property of the first <code>Category</code> object in the list. We can use 1, 2, 3 etc. as the indexer to specify the second, third, fourth etc. <code>Category</code> as required. Omitting the indexer, e.g.: <code>"Categories[].Description</code> specifies all validation errors for the <code>Description</code> property of <em>any</em> <code>Category</code> object in the list.</p>
<h4>The Code</h4>
<p>The code for the <code>IgnoreModelErrors</code> attribute is shown below:</p>
<pre lang="cs" class="brush: csharp">
// 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();
}
}
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>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.</p>
<h2>Summary</h2>
<p>The <code>IgnoreModelErrors</code> 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.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-74765180667735697982011-10-05T12:46:00.001+01:002011-10-05T12:48:21.930+01:00Serialization 101 - Part III: XML Serialization<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a>
<h2>Introduction</h2>
<p>In the final part in this series on serialization, we are going to take a look at XML serialization.</p>
<p>Now unlike binary and SOAP serialization, which we looked at in parts <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-i-binary.html">I</a> and <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-ii-soap.html">II</a> respectively, XML serialization requires the use of a completely different framework.</p>
<h2>The Math Game</h2>
<p>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):</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUqSbYw4QYgMDaghK_d_KKKAa5MfCLJxsTbw4gCd5KZ5GiUMI3J504prafwU8cAMrPl9dyod8FBi31U9oae_YEtbkVUUYmmM7OvTrDhUJHYVTPy0zFUN4VxbjT87UHA0-M-cjbBCN_MJE/s1600/GameObjects.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="151" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUqSbYw4QYgMDaghK_d_KKKAa5MfCLJxsTbw4gCd5KZ5GiUMI3J504prafwU8cAMrPl9dyod8FBi31U9oae_YEtbkVUUYmmM7OvTrDhUJHYVTPy0zFUN4VxbjT87UHA0-M-cjbBCN_MJE/s400/GameObjects.png" /></a></div>
<h4>Making Objects Serializable</h4>
<p>Now, unlike binary and SOAP serialization where objects are non-serializable unless explicitly stated otherwise through use of the <code>Serializable</code> attribute; with XML serialization <em>all</em> 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.</p>
<p>Firstly, let's take a look at the <code>Question</code> class:</p>
<pre lang="cs" class="brush: csharp">
// 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; }
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Note how the class is decorated with an <code>XmlRoot</code> 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.</p>
<p>Next, notice the three properties of the class are decorated with <code>XmlAttribute</code> 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.</p>
<p>Next we will take a look at the <code>UserAnswer</code> class:</p>
<pre lang="cs" class="brush: csharp">
// 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; }
}
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Here we are using the <code>XmlElement</code> 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.</p>
<p>It is also worth noting that because the <code>IsCorrect</code> property is read-only, it will <strong>not</strong> be serialized. However, if the property was <em>not</em> read-only and we didn't want it serialized, we would simply decorated it with the <code>XmlIgnore</code> attribute.</p>
<p>Now finally, the <code>Game</code> class:</p>
<pre lang="cs" class="brush: csharp">
// 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); }
}
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Note how the <code>Answers</code> property is decorated with both an <code>XmlArray</code> and <code>XmlArrayItem</code> attribute. This specifies that the <code>Answers</code> 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".</p>
<h4>OK, Let's Start Serializing</h4>
<p>In order to serialize and de-serialize our objects, we need to use an <code>XmlSerializer</code> object. The example below shows how to use the <code>XmlSerializer</code> to save the current game:</p>
<pre lang="cs" class="brush: csharp">
// 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);
}
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>As with binary and SOAP serialization, we can serialize to any object which inherits from the <code>Stream</code> class. In our example, we use a <code>FileStream</code> object, but this could just as easily have been a <code>NetworkStream</code> object.</p>
<p>Finally, for completeness, the code for loading a previously-saved game from disk</p>
<pre lang="cs" class="brush: csharp">
// 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);
}
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Below is an example of the XML produced when serializing a game:</p>
<pre lang="xml" class="brush: xml">
<?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>
</pre>
<h2>A Quick Note on SOAP</h2>
<p>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 <code>SoapFormatter</code> discussed in the <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-ii-soap.html">previous article</a>. You can find further details on this in the MSDN documentation.</p>
<h2>Summary</h2>
<p>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.<p>
<p>You can download the source code for the math game <a href="http://www.jameshallsweb.co.uk/codesamples/MathGame.zip">here</a>.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-19879948067799581462011-09-16T09:44:00.000+01:002011-10-05T12:56:18.597+01:00Serialization 101 - Part II: SOAP Serialization<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a>
<h2>Introduction</h2>
<p>In <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-i-binary.html">Part I</a> 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</p>
<p>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.</p>
<h2>Upgrading the Farmyard</h2>
<p>Continuing with the sample farmyard application we looked at in <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-i-binary.html">Part I</a>, 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 <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-i-binary.html">Part I</a>, SOAP serialization uses the same mechanism as binary serialization.</p>
<p>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 <code>BinaryFormatter</code> object for a <code>SoapFormatter</code> object when saving the data:</p>
<pre lang="cs" class="brush: csharp">
// 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);
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>...and for loading the data:</p>
<pre lang="cs" class="brush: csharp">
// 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);
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>As you can see, we use a simple <code>switch...case</code> statement, based on the selected file format from the load/save dialog, to determine whether to instantiate a <code>BinaryFormatter</code> or a <code>SoapFormatter</code> 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 <code>NetworkStream</code>.</p>
<p>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 <code>SoapFormatter</code>. The reason? Apparently the <code>SoapFormatter</code> does not support the serialization of generics, and is therefore throwing its toys out of the pram when it encounters our <code>List<Animal></code> object which we have used to underpin our farmyard.</p>
<p>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:</p>
<pre lang="cs" class="brush: csharp">
// 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;
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Note the use of the <code>OnSerializing</code> and <code>OnDeserialized</code> attributes we discussed in <a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-i-binary.html">Part I</a> 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</p>
<h2>Summary</h2>
<p>SOAP serialization uses the same mechanism as binary serialization; however one major limitation is the inability to serialize generics.<p>
<p>The source code for the farmyard application can be downloaded <a href="http://www.jameshallsweb.co.uk/codesamples/SerializationFarmyard_PartII.zip">here</a>.</p>
<p><strong>Next: </strong><em><a href="http://mrbigglesworth79.blogspot.com/2011/10/serialization-101-part-iii-xml.html">XML Serialization</a></em></p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com2tag:blogger.com,1999:blog-5210693437012093137.post-12150893750163364112011-09-14T13:14:00.002+01:002011-09-16T09:51:06.889+01:00Serialization 101 - Part I: Binary Serialization<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a><br />
<h2>Foreword</h2>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.<br />
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!).<br />
<h2>Introduction</h2><p>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.</p><p>There are three main mechanisms for serialization in .NET:</p><ul><li>Binary serialization: Persistence of objects to a binary storage format. We will look at this in this article.</li>
<li>SOAP serialization: Persistence of objects in SOAP (Simple Object Access Protocol) format.</li>
<li>XML serialization. Persistence of objects as XML.</li>
</ul><h2>The Serialization Farmyard</h2><p>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):</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijCXJMiwRwGbwsEku-jRQ9VgBqC4_ldRfEcxaGkVMrhYNlZnGzI4dhS5IjNN1vQsLUTUVMPKuMGXxsWOXuIE4croVEUobeCgil1JWSsVJdGiW7GPkY2ucbr70Itp-nlnoJLfEXoWfWLko/s1600/FarmClasses.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="324" width="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijCXJMiwRwGbwsEku-jRQ9VgBqC4_ldRfEcxaGkVMrhYNlZnGzI4dhS5IjNN1vQsLUTUVMPKuMGXxsWOXuIE4croVEUobeCgil1JWSsVJdGiW7GPkY2ucbr70Itp-nlnoJLfEXoWfWLko/s320/FarmClasses.png" /></a></div>
<h4>Making Objects Serializable</h4>
<p>Now lets take a quick look at the <code>Animal</code> class:</p><pre lang="cs" class="brush: csharp">// C#
[Serializable]
public abstract class Animal
{
public virtual string Name { get; set; }
public virtual int Arrived { get; set; }
public abstract int SpaceRequired { get; }
}
</pre><pre lang="vb.net" class="brush: vbnet">' 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
</pre><p>Notice how the class has been decorated with a <code>Serializable</code> 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 <code>Serializable</code> 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.</p><p>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 <code>Serializable</code> attribute is not inheritable. Therefore any sub-classes of our abstract <code>Animal</code> class must therefore also be explicitly decorated with the <code>Serializable</code>, for example: <pre lang="cs" class="brush: csharp">// C#
[Serializable]
public class Sheep : Animal
{
public override int SpaceRequired
{
get { return 3; }
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
<Serializable()>
Public Class Sheep
Inherits Animal
Public Overrides ReadOnly Property SpaceRequired As Integer
Get
Return 3
End Get
End Property
End Class
</pre><p>Also, don't think you can get away with just decorating your sub-classes with the <code>Serializable</code> attribute! All classes up the inheritance hierarchy must also be suitably decorated otherwise an exception will be thrown at run-time.</p><p>Now let's look at the <code>Farmyard</code> class. Firstly, the class definition:</p><pre lang="cs" class="brush: csharp">// C#
[Serializable]
public class Farmyard
{
private List<Animal> animals;
public string Name { get; set; }
public int Day { get; set; }
public int Capacity { get; set; }
}
</pre><pre lang="vb.net" class="brush: vbnet">' 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
</pre><p>As before, the class is decorated with the <code>Serializable</code> attribute. The properties <code>Name</code>, <code>Day</code> and <code>Capacity</code> are all primitive types and therefore can be serialized. If you look at the MSDN documentation, you will see that the <code>List<T></code> class is serializable, providing that the type of <code>T</code> is also serializable. In this case it will be, as our <code>Animal</code> class has been decorated with the <code>Serializable</code> attribute.</p><p>Now, take a look at the weather property:</p><pre lang="cs" class="brush: csharp">// C#
[NonSerialized]
private Weather weather;
public Weather Weather
{
get { return weather; }
set { weather = value; }
}
</pre><pre lang="vb.net" class="brush: vbnet">' 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
</pre><p>This shows how to prevent a member from being serialized at run-time: simply decorate it with the <code>NonSerialized</code> 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 <code>Weather</code> property because we will be assigning it a new value, when it is loaded from disk (see below).</p><p>It's also worth noting that the <code>NonSerialized</code> attribute can only be applied to <strong>fields</strong> 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.</p><p>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:</p><pre lang="cs" class="brush: csharp">// C#
[NonSerialized]
private FarmyardEventHandler animalAdded;
public event FarmyardEventHandler AnimalAdded
{
add { animalAdded = (FarmyardEventHandler)Delegate.Combine(animalAdded, value); }
remove { animalAdded = (FarmyardEventHandler)Delegate.Remove(animalAdded, value); }
}
</pre><pre lang="vb.net" class="brush: vbnet">' 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
</pre><p>In similarity to non-serialized properties we have to declare a private delegate, decorated with the <code>NonSerialized</code> attribute, and expose it through a public event.</p>
<h4>Controlling and Customising Serialization</h4>
<p>There are two main approaches to controlling and customising binary serialization in the .NET framework. One is to implement <code>ISerializable</code> 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:</p>
<ul>
<li><code>OnSerializing</code></li>
<li><code>OnSerialized</code></li>
<li><code>OnDeserializing</code></li>
<li><code>OnDeserialized</code></li>
</ul>
<p>We can see an example of this in the <code>Farmyard</code> class of our application:</p>
<pre lang="cs" class="brush: csharp">
// 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());
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>As you can see, the method is decorated with the <code>OnDeserialized</code> attribute which means it will be executed once de-serialization is complete. This method calls the <code>NewDay()</code> method, which increments the day and sets the <code>Weather</code> property to a random value. Remember, we are explicitly not serializing the weather when we save the farmyard to disk.</p>
<p>Note also, that the method takes an object of type <code>StreamingContext</code>, 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.</p>
<h4>Performing Serialization</h4>
<p>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 <code>BinaryFormatter</code> object. This object has two methods: <code>Serialize()</code> and <code>Deserialize()</code> for serialization and de-serialization respectively. Here is the code for saving a farmyard to disk:<p>
<pre lang="cs" class="brush: csharp">
// C#
using (Stream fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(fileStream, farmyard);
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>And for loading a saved farmyard from disk:</p>
<pre lang="cs" class="brush: csharp">
// C#
using (Stream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
IFormatter formatter = new BinaryFormatter();
farmyard = (Farmyard)formatter.Deserialize(fileStream);
}
</pre>
<pre lang="vb.net" class="brush: vbnet">
' 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
</pre>
<p>Note how the <code>Serialize()</code> and <code>Deserialize()</code> methods of the <code>BinaryFormatter</code> object accept an object of type <code>Stream</code>. In our example, we are using a <code>FileStream</code> object as we are serializing our farmyard to disk, but we could just as easily have used a <code>MemoryStream</code> or <code>NetworkStream</code> for serializing to memory or over a network respectively.</p>
<p>Note also that, when de-serializing an object, a cast is required as the <code>Deserialize()</code> method simply returns a method of type <code>Object</code>.
<h2>Summary</h2>
<p>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.</p>
<p>The source code for the farmyard application can be downloaded <a href="http://www.jameshallsweb.co.uk/codesamples/SerializationFarmyard.zip">here</a>. Unfortunately, due to time constraints, the source code is in C# only.</p>
<p><strong>Next: </strong><a href="http://mrbigglesworth79.blogspot.com/2011/09/serialization-101-part-ii-soap.html"><em>SOAP Serialization</em></a></p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com1tag:blogger.com,1999:blog-5210693437012093137.post-56594189614865500082011-06-30T13:52:00.004+01:002011-07-01T13:50:26.083+01:00A D.I.Y. Lightbox<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a><br />
<h2>Introduction</h2><p>OK, so JavaScript/jQuery lightboxes are ten-a-penny. Just go to the <a href="http://plugins.jquery.com/search/node/lightbox%20type%3Aproject_project">jQuery plugin repository</a> and take your pick! Indeed for long time, I did just that. After all, there's no point re-inventing the wheel is there??</p><p>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.</p><h2>LiteBox</h2></p>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 <a href="http://jquery.com">jQuery</a> whore, I am also assuming prior experience of jQuery. If you are unfamiliar with jQuery, you can find more information <a href="http://docs.jquery.com/Main_Page">here</a>.</p><h4>The Basics</h4><p>LiteBox is implemented as a jQuery plugin. The signature is as follows:</p><pre lang="jscript" class="brush: js">jQuery.lightbox( url, [options] )
</pre><p>The <code>url</code> parameter is mandatory and specifies the URL of the image to be shown in the lightbox. The <code>options</code> parameter is a set of optional key/value pairs for configuring the lightbox:</p><table class="paramtable"><thead>
<tr><th>Key</th><th>Description</th><th>Default Value</th></tr>
</thead> <tbody>
<tr><td><code>title</code></td><td>The text to be used for the tooltip and <code>alt</code> attribute of the image.</td><td>The URL of the image.</td></tr>
<tr><td><code>showCloseButton</code></td><td>Whether or not to show the 'close' button on the lightbox.</td><td>True.</td></tr>
<tr><td><code>closeButtonPosition</code></td><td>The position of the 'close' button.<br />
Can be one of either 'top-left', 'top-right', 'bottom-left' or 'bottom-right'.<br />
Ignored if <code>showCloseButton</code> is <code>false</code>.</td><td>'top-right'.</td></tr>
<tr><td><code>animationSpeed</code></td><td>The speed of the animation.<br />
Can be one of either 'slow', 'medium' or 'fast'; or the length of the animation in milliseconds.</td><td>'medium' (≡ 500ms)</td></tr>
</tbody> </table><h4>Getting into the Code</h4><p>Before we get delve too deeply into the JavaScript, it's helpful to have a look at the CSS:</p><pre lang="css" class="brush: 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;
}
</pre><p>As is customary with jQuery plugins, the first thing we need to do is merge any user options into a settings object:</p><pre lang="jscript" class="brush: js">var settings = {
title: url,
showCloseButton: true,
closeButtonPosition: 'top-right',
animationSpeed: 'medium'
};
$.extend(settings, options);
</pre><p>The next thing to do is create a <code><img></code> 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:</p><pre lang="jscript" class="brush: js">var img = $('<img src="' + url + '" alt="' + settings.title + '" title="' + settings.title + '" class="jquery-litebox-img" style="display: none;" />');
$('body').append(img);
</pre><p>Once the browser has loaded them image, then the interesting stuff can start. By hooking into the image's <code>load</code> event we can create the lightbox and append the image to it:</p><pre lang="jscript" class="brush: js">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));
</pre><p>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.</p><p>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.</p><p>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.<p><p>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 <code>animate()</code> function:</p><pre lang="jscript" class="brush: js">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);
});
}
});
</pre><p>When the animation is complete, we hook into the <code>mouseover</code> and <code>mouseout</code> events of the image to show and hide the close button respectively. The code for showing and hiding the button is as follows:</p><pre lang="jscript" class="brush: js">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;
}
</pre><p>The animation time is determined by calling the <code>getAnimationTime()</code> function:</p><pre lang="jscript" class="brush: js">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'.");
}
</pre><h2>Summary</h2><p>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.</p><p>You can download the source code, along with a sample web page from <a href="http://www.jameshallsweb.co.uk/codesamples/LiteBox.zip">here</a>.</p><p>So can see a demo of LiteBox in action <a href="http://www.jameshallsweb.co.uk/samplesites/LiteBox/Default.htm">here</a>.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com2tag:blogger.com,1999:blog-5210693437012093137.post-88198359670770223962011-06-16T09:54:00.000+01:002011-06-16T12:43:32.684+01:00Gotcha #1167: Quoted Identifiers in Oracle<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a><br />
<h2>Introduction</h2><p>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</p><p>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).</p><h2>The Scenario</h2><p>Consider the following table:</p><pre lang="sql" class="brush: sql">CREATE TABLE A_TABLE
(
A_COLUMN NUMBER(5,0) NOT NULL ENABLE
)
</pre><p>Now take a look at he following queries:</p><pre lang="sql" class="brush: sql">SELECT A_COLUMN FROM A_TABLE -- Returns the data
SELECT A_Column FROM A_TABLE -- Returns the data
</pre><p>Nothing magical there. The query returns the data correctly regardless of the case used in the query, exactly as we would expect.</p><p>Now consider this table:</p><pre lang="sql" class="brush: sql">CREATE TABLE ANOTHER_TABLE
(
"A_Column" NUMBER(5,0) NOT NULL ENABLE
)
</pre><p>When we query this table in the same way, the result is very different:</p><pre lang="sql" class="brush: sql">SELECT A_COLUMN FROM ANOTHER_TABLE -- ORA-00904: "A_COLUMN": invalid identifier
SELECT A_Column FROM ANOTHER_TABLE -- ORA-00904: "A_COLUMN": invalid identifier
</pre><p>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.</p><h2>The Problem</h2><p>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 <code>A_TABLE</code>, the case of the query doesn't matter. However, when querying <code>ANOTHER_TABLE</code>, the query fails as the identifier is always converted to <code>A_COLUMN</code>, whereas the column is actually named <code>A_Column</code>.</p><p>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:</p><pre lang="sql" class="brush: sql">SELECT "A_Column" FROM ANOTHER_TABLE
</pre><p>OK, fair enough, it's not ideal but we can live with that. However, this in itself presents another interesting scenario. Consider this table:</p><pre lang="sql" class="brush: sql">CREATE TABLE YET_ANOTHER_TABLE
(
"A_COLUMN" NUMBER(5,0) NOT NULL ENABLE,
"A_Column" VARCHAR2(100) NOT NULL ENABLE
)
</pre><p>Yes, in Oracle this <em>is</em> a perfectly valid (if not recommended) table definition! So assuming the following data, what happens when we query it:</p><table class="datatable"><thead>
<tr><th>A_COLUMN</th><th>A_Column</th></tr>
</thead>
<tbody>
<tr><td>1050</td><td>Foo</td></tr>
<tr><td>2060</td><td>Bar</td></tr>
<tr><td>3070</td><td>Baz</td></tr>
</tbody>
</table><pre lang="sql" class="brush: sql">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
</pre><h2>The Moral of the Story</h2><p>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.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-14943846045674108472011-06-14T10:26:00.134+01:002011-06-22T08:32:53.418+01:00A Look at Dapper.NET<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a><br />
<h2>Introduction</h2><p>Over the years, I've seen several object-relational-mappers (ORMs) come along for .NET and always ultimately end up being somewhat disappointed with the end result. I remember seeing a preview of LINQ-to-SQL being absolutely blown away by the speed and ease with which you could generate code which would manage the movement of data between your relational database and your business object model. And all without having to hand-roll any SQL! A little later, it was Entity Framework which, at the time at least, seemed to be like LINQ-to-SQL but <em>even better!</em></p><p>However, the time eventually came when I had to use some of these technologies in real-world development projects and it was then that some of their limitations became apparent. From cumbersome XML definition files to sub-optimal performance, I have spent so much time implementing 'fixes' and 'workarounds' to try and shoe-horn a framework into a project's architecture and get it to perform in the way I want, that now (unless it is a very simple 1-tier application that needs to be developed rapidly), I prefer to go back to using the stalwart ADO.NET classes of yesteryear, as they offer the flexibility and control over my data access layer, that I often find is taken away from me when using some of these ORMs.</p><p>That was until a colleague told me about <a href="http://code.google.com/p/dapper-dot-net">Dapper.NET</a>...</p><p><a href="http://code.google.com/p/dapper-dot-net">Dapper.NET</a> is an open-source, lightweight ORM written by the developers behind <a href="http://www.stackoverflow.com">Stack Overflow</a>. It is simple to use, and is compatible with any database which implements a provider for .NET (i.e.: provides an implementation of the <code>IDbConnection</code> interface). For more information, check out the project's <a href="http://code.google.com/p/dapper-dot-net">website</a>.</p><p>The aim of this article, is to give a brief introduction to <a href="http://code.google.com/p/dapper-dot-net">Dapper.NET</a> along with some examples, and hopefully demonstrate why I like this product.</p><h2>Getting Dapper</h2><p>At the time of writing there are no pre-compiled binaries available for Dapper, so you have to download the source code from the <a href="http://code.google.com/p/dapper-dot-net/source/checkout">website</a> and compile it yourself. This is no big deal as the project is quite small and has no other dependencies. When you open the solution, the project you are interested in is the one simply called "Dapper" (or "Dapper NET35" if you are using .NET 3.5).<br />
<h2>Using Dapper</h2><p>Dapper is implemented as series of extension methods, which can be called on any object which implements the <code>IDbConnection</code> interface. In the following examples I am going to use SQL-Server, specifically the <code>AdventureWorks</code> sample database.</p><h4>The <code>Query()</code> Method</h4><p>The <code>Query()</code> extension method and its overloads are used, as the name suggests, is used for extracting information from the database and using it to populate our business object model.</p><p>In this example, we are going to populate a collection of <code>SubCategory</code> objects from the database. Here is our POCO <code>SubCategory</code> class:</p><pre lang="cs" class="brush: csharp">// C#
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime ModifiedOn { get; set; }
}
public class SubCategory : Category
{
public int CategoryId { get; set; }
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class Category
Public Property Id As Integer
Public Property Name As String
Public Property ModifiedOn As DateTime
End Class
Public Class SubCategory
Inherits Category
Public Property CategoryId As Integer
End Class
</pre><p>And here is the code to populate a collection of <code>SubCategory</code> objects from the database:</p><pre lang="cs" class="brush: csharp">// C#
public IEnumerable<SubCategory> SelectSubCategories()
{
using (IDbConnection connection = OpenConnection())
{
const string query = "SELECT ProductSubcategoryId AS Id, ProductCategoryID AS CategoryId, [Name], ModifiedDate AS ModifiedOn " +
"FROM Production.ProductSubcategory";
return connection.Query<SubCategory>(query);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function SelectSubCategories() As IEnumerable(Of SubCategory)
Using connection As IDbConnection = OpenConnection()
Const query As String = "SELECT ProductSubcategoryId AS Id, ProductCategoryID AS CategoryId, [Name], ModifiedDate AS ModifiedOn " + _
"FROM Production.ProductSubcategory"
Return connection.Query(Of SubCategory)(query)
End Using
End Function
</pre><p>Yes, it really <em>is</em> as simple as that! Note that I have used embedded SQL in this example, but I could have just as easily used a stored procedure. I have used aliases in the SQL to ensure the columns of the result-set match the properties of the <code>SubCategory</code> class. Dapper does the rest.</p><p>Now for getting a single <code>SubCategory</code> out of the database:</p><pre lang="cs" class="brush: csharp">// C#
public SubCategory SelectSubCategory(int subCategoryId)
{
using (IDbConnection connection = OpenConnection())
{
const string query = "SELECT ProductSubcategoryId AS Id, ProductCategoryID AS CategoryId, [Name], ModifiedDate AS ModifiedOn " +
"FROM Production.ProductSubcategory " +
"WHERE ProductSubcategoryId = @SubCategoryId";
return connection.Query<SubCategory>(query, new { SubCategoryId = subCategoryId }).SingleOrDefault();
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function SelectSubCategory(ByVal subCategoryId As Integer) As SubCategory
Using connection As IDbConnection = OpenConnection()
Const query As String = "SELECT ProductSubcategoryId AS Id, ProductCategoryID AS CategoryId, [Name], ModifiedDate AS ModifiedOn " + _
"FROM Production.ProductSubcategory " + _
"WHERE ProductSubcategoryId = @SubCategoryId"
Return connection.Query(Of SubCategory)(query, New With {.SubCategoryId = subCategoryId}).SingleOrDefault()
End Using
End Function
</pre><p>Here, we pass in a parameter object to the <code>Query()</code> method. The parameter object can be any object whose properties match the SQL parameters used in the query. As the <code>Query()</code> method always returns collection of objects, we simply call the LINQ <code>SingleOrDefault()</code> method as we know the query should only return 1 or 0 rows.</p><p>Dapper also has the ability to populate nested objects using <em>true</em> eager-loading. Consider the <code>Product</code> class which has a property, <code>SubCategory</code>, which returns a <code>SubCategory</code> object:</p><pre lang="cs" class="brush: csharp">// C#
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string ProductNumber { get; set; }
// NOTE: Some properties omitted for brevity.
public DateTime ModifiedDate { get; set; }
public SubCategory SubCategory { get; set; }
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class Product
Public Property ProductID As Integer
Public Property Name As String
Public Property ProductNumber As String
' NOTE: Some properties omitted for brevity.
Public Property ModifiedDate As DateTime
Public Property SubCategory As SubCategory
End Class
</pre><p>Here is the code to populate our business objects:</p><pre lang="cs" class="brush: csharp">// C#
public IEnumerable<Product> SelectProductsWithSubCategories()
{
using (IDbConnection connection = OpenConnection())
{
const string query = "SELECT p.ProductID, p.Name, p.ProductNumber, p.MakeFlag, p.FinishedGoodsFlag, p.Color, p.SafetyStockLevel, p.ReorderPoint, p.StandardCost, p.ListPrice, p.Size, p.SizeUnitMeasureCode, p.WeightUnitMeasureCode, p.Weight, p.DaysToManufacture, p.ProductLine, p.Class, p.Style, p.ProductSubcategoryID, p.ProductModelID, p.SellStartDate, p.SellEndDate, p.DiscontinuedDate, p.ModifiedDate, " +
"s.ProductSubcategoryId AS Id, s.ProductCategoryID AS CategoryId, s.[Name], s.ModifiedDate AS ModifiedOn " +
"FROM Production.Product p " +
"LEFT OUTER JOIN Production.ProductSubcategory s ON s.ProductSubcategoryId = p.ProductSubcategoryID";
return connection.Query<Product, SubCategory, Product>(query, (product, subCategory) => { product.SubCategory = subCategory; return product; });
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function SelectProductsWithSubCategories() As IEnumerable(Of Product)
Using connection As IDbConnection = OpenConnection()
Const query As String = "SELECT p.ProductID, p.Name, p.ProductNumber, p.MakeFlag, p.FinishedGoodsFlag, p.Color, p.SafetyStockLevel, p.ReorderPoint, p.StandardCost, p.ListPrice, p.Size, p.SizeUnitMeasureCode, p.WeightUnitMeasureCode, p.Weight, p.DaysToManufacture, p.ProductLine, p.Class, p.Style, p.ProductSubcategoryID, p.ProductModelID, p.SellStartDate, p.SellEndDate, p.DiscontinuedDate, p.ModifiedDate, " + _
"s.ProductSubcategoryId AS Id, s.ProductCategoryID AS CategoryId, s.[Name], s.ModifiedDate AS ModifiedOn " + _
"FROM Production.Product p " + _
"LEFT OUTER JOIN Production.ProductSubcategory s ON s.ProductSubcategoryId = p.ProductSubcategoryID"
Return connection.Query(Of Product, SubCategory, Product)(query, Function(product, subCategory)
product.SubCategory = subCategory
Return product
End Function)
End Using
End Function
</pre><p>Here, the <code>Query()</code> takes type-parameters of the business objects involved as well as the type of object to return. As in previous examples, the first parameter passed into the method is the SQL query. The second is a mapping function which describes how the two objects should be nested (i.e.: setting the <code>SubCategory</code> property of the <code>Product</code> object to the <code>SubCategory</code> object).</p><p>If the type-parameter is omitted from the <code>Query()</code> method, a collection of dynamic objects is returned, whose properties match the columns in the result-set. Take a look at this example, which is used to get the thumbnail photo for a single product:</p><pre lang="cs" class="brush: csharp">// C#
public byte[] SelectThumbnail(int productId)
{
using (IDbConnection connection = OpenConnection())
{
const string query = "SELECT pp.ThumbNailPhoto " +
"FROM Production.ProductPhoto pp " +
"INNER JOIN Production.ProductProductPhoto ppp ON ppp.ProductPhotoID = pp.ProductPhotoID " +
"WHERE ppp.ProductID = @ProductId";
dynamic result = connection.Query(query, new { ProductId = productId }).SingleOrDefault();
return result != null ? result.ThumbNailPhoto : null;
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function SelectThumbnail(ByVal productId As Integer) As Byte()
Using connection As IDbConnection = OpenConnection()
Const query As String = "SELECT pp.ThumbNailPhoto " + _
"FROM Production.ProductPhoto pp " + _
"INNER JOIN Production.ProductProductPhoto ppp ON ppp.ProductPhotoID = pp.ProductPhotoID " + _
"WHERE ppp.ProductID = @ProductId"
Dim result As Object = connection.Query(query, New With {.ProductId = productId}).SingleOrDefault()
Return If(Not result Is Nothing, result.ThumbNailPhoto, Nothing)
End Using
End Function
</pre><h4>The <code>Execute()</code> Method</h4><p>Just as the <code>Query()</code> method is used to get data out of the database, the <code>Execute()</code> method is used in situations where we are <strong>not</strong> retrieving data (e.g.: INSERTing, UPDATEing and DELETEing data). Its use, however, is very similar to the <code>Query()</code> method, except that it always returns an integer (the number of rows affected) instead of a collection of objects.</p><p>In this example, we are going to insert a new <code>SubCategory</code> into the database:</p><pre lang="cs" class="brush: csharp">// C#
public int InsertSubCategory(SubCategory subCategory)
{
using (IDbConnection connection = OpenConnection())
{
const string query = "INSERT INTO Production.ProductSubcategory(ProductCategoryID, [Name]) " +
"VALUES (@CategoryId, @Name)";
int rowsAffectd = connection.Execute(query, subCategory);
SetIdentity<int>(connection, id => subCategory.Id = id);
return rowsAffectd;
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function InsertSubCategory(ByVal subCategory As SubCategory) As Integer
Using connection As IDbConnection = OpenConnection()
Const query As String = "INSERT INTO Production.ProductSubcategory(ProductCategoryID, [Name]) " + _
"VALUES (@CategoryId, @Name)"
Dim rowsAffected As Integer = connection.Execute(query, subCategory)
SetIdentity(Of Integer)(connection, Sub(id) subCategory.Id = id)
Return rowsAffected
End Using
End Function
</pre><p>As with the <code>Query()</code> method, the <code>Execute()</code> method takes a parameter object. As the names of the SQL parameters match the properties of the <code>SubCategory</code> object itself, I simply use that as the parameter object.</p><p>I have also created a convenient method for assigning the identity value, generated by the database, to our POCO object:</p><pre lang="cs" class="brush: csharp">// C#
protected static void SetIdentity<T>(IDbConnection connection, Action<T> setId)
{
dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single();
T newId = (T)identity.Id;
setId(newId);
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Protected Shared Sub SetIdentity(Of T)(ByVal connection As IDbConnection, ByVal setId As Action(Of T))
Dim identity As Object = connection.Query("SELECT @@IDENTITY AS Id").Single()
Dim newId As T = CType(identity.Id, T)
setId(newId)
End Sub
</pre><p>For the sake of completeness, here is the code for updating a <code>SubCategory</code>:</p><pre lang="cs" class="brush: csharp">// C#
public int UpdateSubCategory(SubCategory subCategory)
{
using (IDbConnection connection = OpenConnection())
{
const string query = "UPDATE Production.ProductSubcategory " +
"SET ProductCategoryID = @CategoryId, " +
"[Name] = @Name, " +
"ModifiedDate = @ModifiedOn " +
"WHERE ProductSubcategoryID = @Id";
return connection.Execute(query, subCategory);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function UpdateSubCategory(ByVal subCategory As SubCategory) As Integer
Using connection As IDbConnection = OpenConnection()
Const query As String = "UPDATE Production.ProductSubcategory " + _
"SET ProductCategoryID = @CategoryId, " + _
"[Name] = @Name, " + _
"ModifiedDate = @ModifiedOn " + _
"WHERE ProductSubcategoryID = @Id"
Return connection.Execute(query, subCategory)
End Using
End Function
</pre><p>And deleting a <code>SubCategory</code>:</p><pre lang="cs" class="brush: csharp">// C#
public int DeleteSubCategory(SubCategory subCategory)
{
using (IDbConnection connection = OpenConnection())
{
const string query = "DELETE FROM Production.ProductSubcategory " +
"WHERE ProductSubcategoryID = @Id";
return connection.Execute(query, subCategory);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function DeleteSubCategory(ByVal subCategory As SubCategory) As Integer
Using connection As IDbConnection = OpenConnection()
Const query As String = "DELETE FROM Production.ProductSubcategory " + _
"WHERE ProductSubcategoryID = @Id"
Return connection.Execute(query, subCategory)
End Using
End Function
</pre><h4>Transaction Support</h4><p>Dapper also supports transactional operations. For example, the following code deletes a product and any related images from the database:</p><pre lang="cs" class="brush: csharp">// C#
public int DeleteProduct(Product product)
{
using (IDbConnection connection = OpenConnection())
{
const string deleteImageQuery = "DELETE FROM Production.ProductProductPhoto " +
"WHERE ProductID = @ProductID";
const string deleteProductQuery = "DELETE FROM Production.Product " +
"WHERE ProductID = @ProductID";
IDbTransaction transaction = connection.BeginTransaction();
int rowsAffected = connection.Execute(deleteImageQuery, new { ProductID = product.ProductID }, transaction);
rowsAffected += connection.Execute(deleteProductQuery, new { ProductID = product.ProductID }, transaction);
transaction.Commit();
return rowsAffected;
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function DeleteProduct(ByVal product As Product) As Integer
Using connection As IDbConnection = OpenConnection()
Const deleteImageQuery As String = "DELETE FROM Production.ProductProductPhoto " + _
"WHERE ProductID = @ProductID"
Const deleteProductQuery As String = "DELETE FROM Production.Product " + _
"WHERE ProductID = @ProductID"
Dim transaction As IDbTransaction = connection.BeginTransaction()
Dim rowsAffected As Integer = connection.Execute(deleteImageQuery, New With {.ProductID = product.ProductID}, transaction)
rowsAffected += connection.Execute(deleteProductQuery, New With {.ProductID = product.ProductID}, transaction)
transaction.Commit()
Return rowsAffected
End Using
End Function
</pre><h4>Stored Procedure Support</h4><p>As I mentioned earlier, Dapper also supports stored procedures. The example below uses a stored procedure to get a list of managers for a given employee:</p><pre lang="cs" class="brush: csharp">// C#
public IEnumerable<Manager> SelectManagers(int employeeId)
{
using (IDbConnection connection = OpenConnection())
{
const string storedProcedure = "dbo.uspGetEmployeeManagers";
return connection.Query<Manager>(storedProcedure, new { EmployeeID = employeeId }, commandType: CommandType.StoredProcedure);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function SelectManagers(ByVal employeeId As Integer) As IEnumerable(Of Manager)
Using connection As IDbConnection = OpenConnection()
Const storedProcedure As String = "dbo.uspGetEmployeeManagers"
Return connection.Query(Of Manager)(storedProcedure, New With {.EmployeeID = employeeId}, commandType:=CommandType.StoredProcedure)
End Using
End Function
</pre><h2>Summary</h2><p>To summarise, Dapper.NET is extremely easy to use and offers a high degree of flexibility with regard to how data is accessed and mapped to any business objects. It also has the advantage of not requiring a cumbersome XML (or similar) definition file to set it up</p><p>For further information on Dapper, take a look at the <a href="http://code.google.com/p/dapper-dot-net/">official project homepage</a>.</p><p>My code examples are taken from a very simple MVC application which is available <a href="http://www.jameshallsweb.co.uk/codesamples/DapperDemo.zip">here</a>.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com3tag:blogger.com,1999:blog-5210693437012093137.post-45301366435298412582011-06-06T13:47:00.000+01:002011-06-22T08:32:53.419+01:00Gotcha #1161: Using Named Parameters with Oracle ODP.NET<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" rel="tag" style="display: none;">CodeProject</a><br />
<h2>Introduction</h2>After being legged-up for a good couple of hours by this the other day, I'm writing this quick article to try and prevent other people from nearly throwing their PC out of the window in frustration. It concerns using named query parameters with the Oracle Data Provider for .NET (ODP.NET).<br />
<h2>The Problem</h2>Consider the following simple data-retrieval operation:<br />
<pre class="brush: csharp" lang="cs">// C#
using (OracleConnection connection = new OracleConnection(ConfigurationManager.ConnectionStrings["OracleExpress"].ConnectionString))
{
string query = "SELECT SOME_COLUMN, ANOTHER_COLUMN, THIRD_COLUMN FROM SOME_TABLE WHERE ANOTHER_COLUMN = :SomeParam AND THIRD_COLUMN = :AnotherParam";
OracleCommand command = new OracleCommand(query, connection) { CommandType = CommandType.Text };
command.Parameters.Add(":AnotherParam", OracleDbType.Varchar2).Value = "Ping";
command.Parameters.Add(":SomeParam", OracleDbType.Varchar2).Value = "Foo";
connection.Open();
IDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int someColumn = reader.GetInt32(reader.GetOrdinal("SOME_COLUMN"));
string anotherColumn = reader.GetString(reader.GetOrdinal("ANOTHER_COLUMN"));
string thirdColumn = reader.GetString(reader.GetOrdinal("THIRD_COLUMN"));
Console.WriteLine(String.Format("{0}: {1}, {2}", someColumn, anotherColumn, thirdColumn));
}
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
Using connection As OracleConnection = New OracleConnection(ConfigurationManager.ConnectionStrings("OracleExpress").ConnectionString)
Dim query As String = "SELECT SOME_COLUMN, ANOTHER_COLUMN, THIRD_COLUMN FROM SOME_TABLE WHERE ANOTHER_COLUMN = :SomeParam AND THIRD_COLUMN = :AnotherParam"
Dim command As OracleCommand = New OracleCommand(query, connection) With {.CommandType = CommandType.Text}
command.Parameters.Add(":AnotherParam", OracleDbType.Varchar2).Value = "Ping"
command.Parameters.Add(":SomeParam", OracleDbType.Varchar2).Value = "Foo"
connection.Open()
Dim reader As IDataReader = command.ExecuteReader()
While reader.Read()
Dim someColumn As Integer = reader.GetInt32(reader.GetOrdinal("SOME_COLUMN"))
Dim anotherColumn As String = reader.GetString(reader.GetOrdinal("ANOTHER_COLUMN"))
Dim thirdColumn As String = reader.GetString(reader.GetOrdinal("THIRD_COLUMN"))
Console.WriteLine(String.Format("{0}: {1}, {2}", someColumn, anotherColumn, thirdColumn))
End While
End Using
</pre><p>Now, assuming that the data in our table is as follows:</p><table class="datatable"><thead>
<tr><th>SOME_COLUMN</th><th>ANOTHER_COLUMN</th><th>THIRD_COLUMN</th></tr>
</thead> <tbody>
<tr><td>1</td><td>Foo</td><td>Ping</td></tr>
<tr><td>2</td><td>Bar</td><td>Pong</td></tr>
<tr><td>3</td><td>Baz</td><td>Ping</td></tr>
</tbody> </table><p>We should get back a single row from the database, right? Wrong! Actually what we get back is nothing. Why is this? If we run this query in another tool of our choice (SQL+, TOAD etc.) we find it works as expected.<p><h2>The Solution</h2><p>After some scouring of Google, I eventually tracked down what was causing the problem. It turns out that unlike, for example the SQL-Server data provider, the Oracle data provider <em>always</em> binds parameters by position unless told otherwise. So, even though we have named parameters in our query, <strong>and</strong> we have added named parameters to the <code>OracleCommand</code> object, the data provider still binds the parameters by position; and as we have added the parameters in the 'wrong' order (albeit deliberately in this example) the query doesn't throw an exception, it merely returns an empty result set as the query received by the database is the equivalent of:</p><pre lang="sql" class="brush: sql">SELECT SOME_COLUMN, ANOTHER_COLUMN, THIRD_COLUMN
FROM SOME_TABLE
WHERE ANOTHER_COLUMN = 'Ping'
AND THIRD_COLUMN = 'Foo'
</pre><p>The solution: This isn't made entirely clear on the Oracle documentation, but there is an additional property, <code>BindByName</code>, on the <code>OracleCommand</code> object, which must be set to <strong>true</strong> in order to bind parameters by name:</p><pre lang="cs" class="brush: csharp">// C#
OracleCommand command = new OracleCommand(query, connection) { CommandType = CommandType.Text, BindByName = true };
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Dim command As OracleCommand = New OracleCommand(query, connection) With {.CommandType = CommandType.Text, .BindByName = True}
</pre><p>The above query should now work as expected.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com2tag:blogger.com,1999:blog-5210693437012093137.post-68692047068517504982011-06-03T15:05:00.000+01:002011-06-22T08:32:53.420+01:00Reflection 101: An Introduction to Reflection in .NET<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h2>Introduction</h2><p>This article is in response to a request I've received, to give an introduction to reflection in .NET as well as some examples of how it can be used.</p><p>So what is reflection? Personally, I think of reflection as the use of meta-data which describes our code; or more specifically, describes objects in our code.</p><p>Most database systems provide "system tables" hold information about all the objects (tables, views, stored procedures etc. etc.) in the database. As a exercise, run the following on SQL-Server and see what you get back:</p><pre lang="sql" class="brush: sql">SELECT *
FROM sys.objects
</pre><p>In much the same way, the .NET framework provides a series of classes which, collectively, can be thought of as performing much the same function as system tables in a relational database. They provide data about the different objects (classes, structs, enums, delegates etc.) used in our code as well as functionality to perform actions on those objects (e.g.: instantiating objects, invoking methods etc.). Together these classes form the <code>System.Reflection</code> namespace.<p><h2>Reflection Fundamentals</h2><p>In the <code>System.Reflection</code> namespace, there are two classes which are key to any reflection operations in .NET. They are the <code>Assembly</code> and <code>Type</code> classes and you are likely to end up using either or both of these in any reflection code you write</p><h4>The <code>Assembly</code> Class</h4><p>The <code>Assembly</code> class represents a single .NET assembly. In most .NET solutions, this can be thought of as the equivalent of a single .dll or a single Visual Studio project. The are several ways of obtaining an <code>Assembly</code> object:</p><pre lang="cs" class="brush: csharp">// C#
// Loads the assembly System.Core.dll for execution.
Assembly theAssembly = Assembly.Load("System.Core, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089");
// Loads the assembly System.Core.dll for reflection only.
Assembly theAssembly = Assembly.ReflectionOnlyLoad("System.Core, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089");
// Gets the currently executing assembly.
Assembly theAssembly = Assembly.GetExecutingAssembly();
// Gets the assembly which called the current method.
Assembly theAssembly = Assembly.GetCallingAssembly();
// Gets the entry or startup assembly (i.e.: The assembly for the web app, console app or Winforms app project)
Assembly theAssembly = Assembly.GetEntryAssembly();
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
' Loads the assembly System.Core.dll for execution.
Dim theAssembly As Assembly = Assembly.Load("System.Core, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")
' Loads the assembly System.Core.dll for reflection only.
Dim theAssembly As Assembly = Assembly.ReflectionOnlyLoad("System.Core, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")
' Gets the currently executing assembly.
Dim theAssembly As Assembly = Assembly.GetExecutingAssembly()
' Gets the assembly which called the current method.
Dim theAssembly As Assembly = Assembly.GetCallingAssembly()
' Gets the entry or startup assembly (i.e.: The assembly for the web app, console app or Winforms app project)
Dim theAssembly As Assembly = Assembly.GetEntryAssembly()
</pre><p>As you can see, when using the <code>Load()</code> and <code>ReflectionOnlyLoad()</code> method we can optionally include the version, culture and public-key token to pinpoint the assembly we are interested in more precisely.<p><h4>The <code>Type</code> class</h4><p>The <code>Type</code> class represents the actual type declarations (classes, structs, interfaces, enums etc.) in a .NET assembly. There are three common ways to obtain a <code>Type</code> object for a type:</p><pre lang="cs" class="brush: csharp">// C#
// When we have an instance of an object.
String theString = "Foo";
Type theType = theString.GetType();
// When we know the type we want, but don't have an instance of it.
Type theType = typeof(string);
// When we want to get a type from a specific assembly.
Assembly theAssembly = Assembly.Load("mscorlib");
Type theType = theAssembly.GetType("System.String");
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
' When we have an instance of an object.
Dim theString As String = "Foo"
Dim theType As Type = theString.GetType()
' When we know the type we want, but don't have an instance of it.
Dim theType As Type = GetType(String)
' When we want to get a type from a specific assembly.
Dim theAssembly As Assembly = Assembly.Load("mscorlib")
Dim theType As Type = theAssembly.GetType("System.String")
</pre><p>Once we have our <code>Type</code> object, we then have access to any of its constituent members (properties, methods, constructors etc.) via the methods of the <code>Type</code> class. The list is quite extensive, so I am not going to go through it here, but you can find the complete reference on <a href="http://msdn.microsoft.com/en-us/library/system.type.aspx">MSDN</a>.</p><p>Instead, we are going to look at a simple example which, I hope, will act as a good primer for getting into reflection with .NET.</p><h2>The Reflection Farmyard</h2><p>The Reflection Farmyard is a one-page web application. The user selects an animal from the drop-down, clicks the 'Make Sound' button and a textual representation of that animal's sound appears on the page. Here is the ASP.NET markup for the page:</p><pre lang="aspnet" class="brush: xml"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>My Farm Yard</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>
My Farm Yard</h1>
<div>
<asp:DropDownList ID="animalDropDown" runat="server" AppendDataBoundItems="true" />
<asp:Button ID="makeSoundButton" runat="server" Text="Make Sound" OnClick="makeSoundButton_Click" />
</div>
<div>
<asp:Label ID="soundLabel" runat="server" ForeColor="Red" />
</div>
</div>
</form>
</body>
</html>
</pre><p>In a separate assembly (class library project), we have a series of classes (one for each animal) each exposing a single <code>MakeSound()</code> method which returns the animal's sound as a string, for example:</p><pre lang="cs" class="brush: csharp">// C#
public class Cat
{
public string MakeSound()
{
return "Meow!";
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class Cat
Public Function MakeSound() As String
Return "Meow!"
End Function
End Class
</pre><p>Note that I am <em>deliberately</em> not following good OO practice by not having an abstract <code>Animal</code> from which my concrete animal classes will inherit. The reason for this will become obvious later on.</p><p>Now let's look at the code-behind for the web-page is this is where the interesting reflection stuff happens. Firstly, we want to populate the drop down list with the name of each animal. To do this we are going to use the name of each of the animal classes in our animal assembly. The code for this is as follows:</p><pre lang="cs" class="brush: csharp">// C#
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
animalDropDown.DataSource = GetAnimals();
animalDropDown.DataBind();
}
}
private string[] GetAnimals()
{
Assembly assembly = Assembly.ReflectionOnlyLoad("Ovineware.CodeSamples.ReflectionFarmyard.Animals.CSharp");
IEnumerable<Type> animalTypes = assembly.GetTypes().Where(x => x.Namespace == "Ovineware.CodeSamples.ReflectionFarmyard.Animals.CSharp").OrderBy(x => x.Name);
string[] animals = new string [animalTypes.Count()];
for (int i = 0; i < animalTypes.Count(); i++)
{
Type animalType = animalTypes.ElementAt(i);
animals[i] = animalType.Name;
}
return animals;
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Not IsPostBack Then
animalDropDown.DataSource = GetAnimals()
animalDropDown.DataBind()
End If
End Sub
Private Function GetAnimals() As String()
Dim _assembly As Assembly = Assembly.ReflectionOnlyLoad("Ovineware.CodeSamples.ReflectionFarmyard.Animals.VisualBasic")
Dim animalTypes As IEnumerable(Of Type) = _assembly.GetTypes().Where(Function(x) x.Namespace = "Ovineware.CodeSamples.ReflectionFarmyard.Animals.VisualBasic").OrderBy(Function(x) x.Name)
Dim animals(animalTypes.Count() - 1) As String
For i As Integer = 0 To animalTypes.Count() - 1 Step 1
Dim animalType As Type = animalTypes.ElementAt(i)
animals(i) = animalType.Name
Next
Return animals
End Function
</pre><p>Firstly, we we load the assembly which contains our animal classes. At this stage we can do a reflection-only load as we are not actually going to execute any code in this assembly yet. We then call the <code>GetTypes()</code> method of the <code>Assembly</code> class to return all the types in the assembly. We use a LINQ query to filter the types from the namespace we are interested in and sort them in alphabetical order. For each of our <code>Type</code> objects we then call its <code>Name</code> property to get its name which is put into an array, which in turn is used to populate the drop-down list.</p><p>When the user clicks the 'Make Sound' button, we use reflection once again to initialize the correct animal object and call its <code>MakeSound()</code> method:</p><pre lang="cs" class="brush: csharp">// C#
protected void makeSoundButton_Click(object sender, EventArgs e)
{
Assembly assembly = Assembly.Load("Ovineware.CodeSamples.ReflectionFarmyard.Animals.CSharp");
string typeName = String.Format("Ovineware.CodeSamples.ReflectionFarmyard.Animals.CSharp.{0}", animalDropDown.SelectedValue);
Type animalType = assembly.GetType(typeName);
ConstructorInfo constructor = animalType.GetConstructor(Type.EmptyTypes);
object animal = constructor.Invoke(null);
MethodInfo makeSoundMethod = animalType.GetMethod("MakeSound", BindingFlags.Public | BindingFlags.Instance);
string sound = (string)makeSoundMethod.Invoke(animal, null);
soundLabel.Text = sound;
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Protected Sub makeSoundButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles makeSoundButton.Click
Dim _assembly As Assembly = Assembly.Load("Ovineware.CodeSamples.ReflectionFarmyard.Animals.VisualBasic")
Dim typeName As String = String.Format("Ovineware.CodeSamples.ReflectionFarmyard.Animals.VisualBasic.{0}", animalDropDown.SelectedValue)
Dim animalType As Type = _assembly.GetType(typeName)
Dim constructor As ConstructorInfo = animalType.GetConstructor(Type.EmptyTypes)
Dim animal As Object = constructor.Invoke(Nothing)
Dim makeSoundMethod = animalType.GetMethod("MakeSound", BindingFlags.Public Or BindingFlags.Instance)
Dim sound As String = DirectCast(makeSoundMethod.Invoke(animal, Nothing), String)
soundLabel.Text = sound
End Sub
</pre><p>Here, we load the assembly again but this time for execution. We then call the <code>GetType()</code> method on our <code>Assembly</code> object to get the relevant <code>Type</code> object as specified by the user's selection in the drop-down list. Once we have our <code>Type</code> object we get its default constructor by calling its <code>GetConstructor()</code> method. This method takes an array of <code>Type</code> objects which match the parameter types of the constructor overload we are interested in. In this example, we want the default constructor so we pass in an empty array. The method returns a <code>ConstructorInfo</code> object.</p><p>Next, we instantiate an instance of our animal class by calling the <code>Invoke()</code> method on the <code>ConstructorInfo</code> object. This method takes an array of objects which are passed in as parameters to the constructor. As we are invoking the default constructor, we can just pass in a <code>null</code> reference.<p><p>We then need to get the <code>MakeSound()</code> method of our animal class. We do this by calling the <code>GetMethod()</code> method on our <code>Type</code> object. This method has several overloads, but in this instance the one we are using simply takes the name of the method and a combination of <code>BindingFlags</code> to specify the method, i.e.: we are asking for the method with the name "MakeSound" which is a public, instance method. The <code>GetMethod()</code> method returns a <code>MethodInfo</code> object.</p><p>Once we have our <code>MethodInfo</code> object, we invoke it by calling its <code>Invoke()</code> method. This method takes as parameters the object we want to invoke the method on, in this case the object stored in the <code>animal</code> variable; and an array of objects which are passed as parameters to the method. As our <code>MakeSound()</code> method is parameterless, we again pass in a <code>null</code> reference.</p><p>We cast the result as a <code>String</code> and use it to set the <code>Text</code> property of our label.</p><h4>So why didn't I use an Abstract Class?</h4><p>As I said earlier, I <em>deliberately</em> didn't use an abstract class in my design. If I had, once we had invoked the constructor to get in instance of the object, we could simply cast it to the base class and call the <code>MakeSound()</code> method at compile time:</p><pre lang="cs" class="brush: csharp">// C#
Animal animal = (Animal)constructor.Invoke(null);
string sound = animal.MakeSound();
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Dim animal As Animal = DirectCast(constructor.Invoke(Nothing), Animal)
Dim sound As String = animal.MakeSound()
</pre><p>However, as this is an article about reflection and I wanted to demonstrate calling a method dynamically at run-time, using an abstract class would have somewhat defeated the object of exercise!</p><h2>Gotcha: Getting the Correct Type</h2><p>A common gotcha when using reflection is getting an unexpected <code>Type</code> object. This is often as a result of the differences between the <code>GetType()</code> method of <code>System.Object</code> and the <code>typeof/GetType</code> functions.</p><p>The <code>GetType()</code> method will always return the concrete type of an object, regardless of the type it is declared as; whereas the <code>typeof/GetType</code> functions will return the type that has been declared. It is especially easy to get caught out when using reflection with generics.</p><p>Consider a scenario where the <code>Cat</code> class is a sub-class of <code>Animal</code> and we have a generic method as follows:</p><pre lang="cs" class="brush: csharp">// C#
public static void SomeGenericMethod<T>(T obj)
{
// Always the concrete type of obj.
Type v = obj.GetType();
// Varies depending on the type of T, which may be inferred by the compiler.
Type w = typeof(T);
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Shared Sub SomeGenericMethod(Of T)(ByVal obj As T)
' Always the concrete type of obj.
Dim v As Type = obj.GetType()
' Varies depending on the type of T, which may be inferred by the compiler.
Dim w As Type = GetType(T)
End Sub
</pre><p>Now take a look at the following examples:</p><pre lang="cs" class="brush: csharp">// C#
Cat cat = new Cat();
Animal animal = new Cat();
Type t = cat.GetType(); // t is Cat.
Type u = animal.GetType(); // u is Cat.
SomeGenericMethod(cat); // v is Cat. w is Cat.
SomeGenericMethod(animal); // v is Cat. w is Animal.
SomeGenericMethod<Animal>(cat); // v is Cat. w is Animal.
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Dim _cat As Cat = New Cat()
Dim _animal As Animal = New Cat()
Dim t As Type = _cat.GetType() ' t is Cat.
Dim u As Type = _animal.GetType() ' u is Cat.
SomeGenericMethod(_cat) ' v is Cat. w is Cat.
SomeGenericMethod(_animal) ' v is Cat. w is Animal.
SomeGenericMethod(Of Animal)(_cat) ' v is Cat. w is Animal.
</pre><h2>Summary</h2><p>That concludes this whistle-stop introduction to reflection. For more information, take a look at the <a href="http://msdn.microsoft.com/en-us/library/system.reflection.aspx">MSDN documentation</a> for the <code>System.Reflection</code> namespace.</p><p>The example source code is available <a href="http://www.jameshallsweb.co.uk/codesamples/ReflectionFarmyard.zip">here</a>.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-46634442171489510042011-05-27T11:57:00.000+01:002011-06-22T08:32:53.421+01:00Data Access using Dynamics - Part II: The DynamicDataSet<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h2>Introduction</h2><p>In my <a href="http://mrbigglesworth79.blogspot.com/2011/05/data-access-using-dynamics-part-i.html">previous article</a>, I demonstrated how to leverage the Dynamic Language Runtime (DLR) to create a flexible, dynamic wrapper for the ADO.NET <code>DbDataReader</code> class and its subclasses.</p><p>In this article, we are going to look at creating a dynamic version of another staple component of the ADO.NET framework, namely the <code>DataSet</code>. Again, I'm sure many seasoned .NET developers will recognise the following hypothetical example:</p><pre lang="cs" class="brush: csharp">// C#
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString))
{
string sql = "SELECT SomeColumn, AnotherColumn FROM SomeTable";
SqlCommand command = new SqlCommand(sql, connection);
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet, "SomeTable");
foreach (DataRow row in dataSet.Tables["SomeTable"].Rows)
{
int foo = (int)row["SomeColumn"];
string bar = (string)row["AnotherColumn"];
// Do some stuff with the data.
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Using connection As SqlConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("MyDatabase").ConnectionString)
Dim sql As String = "SELECT SomeColumn, AnotherColumn FROM SomeTable"
Dim command As SqlCommand = New SqlCommand(sql, connection)
Dim dataAdapter As SqlDataAdapter = New SqlDataAdapter(command)
Dim dataSet As DataSet = New DataSet()
dataAdapter.Fill(dataSet, "SomeTable")
For Each row As DataRow In dataSet.Tables("SomeTable").Rows
Dim foo As Integer = DirectCast(row("SomeColumn"), Integer)
Dim bar As String = DirectCast(row("AnotherColumn"), Integer)
' Do some stuff with the data.
Next
End Using
</pre><h2>The Dynamic DataSet and its Components</h2><p>Before we look at the code for the <code>DynamicDataSet</code> class and all its component classes, we first need to extend the <code>DynamicDataObjectWrapper</code> class we looked at in the <a href="http://mrbigglesworth79.blogspot.com/2011/05/data-access-using-dynamics-part-i.html">previous article</a>:</p><pre lang="cs" class="brush: csharp">// C#
public abstract class DynamicListSourceDataObjectWrapper<T> : DynamicDataObjectWrapper<T>, IListSource
where T : IListSource
{
public DynamicListSourceDataObjectWrapper(T obj)
: base(obj)
{
}
public virtual bool ContainsListCollection
{
get { return Obj.ContainsListCollection; }
}
public virtual IList GetList()
{
return Obj.GetList();
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public MustInherit Class DynamicListSourceDataObjectWrapper(Of T As IListSource)
Inherits DynamicDataObjectWrapper(Of T)
Implements IListSource
Public Sub New(ByVal obj As T)
MyBase.New(obj)
End Sub
Public ReadOnly Property ContainsListCollection As Boolean Implements IListSource.ContainsListCollection
Get
Return Obj.ContainsListCollection
End Get
End Property
Public Function GetList() As System.Collections.IList Implements IListSource.GetList
Return Obj.GetList()
End Function
End Class
</pre><p>As you can see, this class implements the <code>IListSource</code>, which will enable us to use our <code>DynamicDataSet</code> and <code>DynamicDataTable</code> classes with the standard ASP.NET data controls.</p><p>Now, let's look a the code for the <code>DynamicDataSet</code> class:</p><pre lang="cs" class="brush: csharp">// C#
public class DynamicDataSet : DynamicListSourceDataObjectWrapper<DataSet>
{
public DynamicDataSet()
: this(new DataSet())
{
}
public DynamicDataSet(DataSet dataSet)
: base(dataSet)
{
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (base.TryGetMember(binder, out result))
return true;
else
{
try
{
if (Obj.Tables.Contains(binder.Name))
result = (DynamicDataTable)Obj.Tables[binder.Name];
else
result = (DynamicDataTable)Obj.Tables.Add(binder.Name);
return true;
}
catch (Exception)
{
result = null;
return false;
}
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (base.TrySetMember(binder, value))
return true;
else
{
try
{
dynamic table = value;
table.TableName = binder.Name;
Obj.Tables.Add(table);
return true;
}
catch (Exception)
{
return false;
}
}
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
try
{
object index = indexes[0];
if (index is int)
{
result = (DynamicDataTable)Obj.Tables[(int)index];
return true;
}
else if (index is string)
{
result = (DynamicDataTable)Obj.Tables[(string)index];
return true;
}
else
{
result = null;
return false;
}
}
catch (Exception)
{
result = null;
return false;
}
}
public static implicit operator DataSet(DynamicDataSet dataSet)
{
return dataSet.Obj;
}
public static explicit operator DynamicDataSet(DataSet dataSet)
{
return new DynamicDataSet(dataSet);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class DynamicDataSet
Inherits DynamicListSourceDataObjectWrapper(Of DataSet)
Public Sub New()
Me.New(New DataSet())
End Sub
Public Sub New(ByVal dataSet As DataSet)
MyBase.New(dataSet)
End Sub
Public Overrides Function TryGetMember(binder As GetMemberBinder, ByRef result As Object) As Boolean
If MyBase.TryGetMember(binder, result) Then
Return True
Else
Try
If Obj.Tables.Contains(binder.Name) Then
result = CType(Obj.Tables(binder.Name), DynamicDataTable)
Else
result = CType(Obj.Tables.Add(binder.Name), DynamicDataTable)
End If
Return True
Catch ex As Exception
result = Nothing
Return False
End Try
End If
End Function
Public Overrides Function TrySetMember(binder As SetMemberBinder, value As Object) As Boolean
If MyBase.TrySetMember(binder, value) Then
Return True
Else
Try
Dim table As Object = value
table.TableName = binder.Name
Obj.Tables.Add(table)
Return True
Catch ex As Exception
Return False
End Try
End If
End Function
Public Overrides Function TryGetIndex(binder As GetIndexBinder, indexes() As Object, ByRef result As Object) As Boolean
If MyBase.TryGetIndex(binder, indexes, result) Then
Return True
Else
Try
Dim index As Object = indexes(0)
If TypeOf (index) Is Integer Then
result = CType(Obj.Tables(DirectCast(index, Integer)), DynamicDataTable)
Return True
ElseIf TypeOf (index) Is String Then
result = CType(Obj.Tables(DirectCast(index, String)), DynamicDataTable)
Return True
Else
result = Nothing
Return False
End If
Catch ex As Exception
result = Nothing
Return False
End Try
End If
End Function
Public Shared Widening Operator CType(ByVal dataSet As DynamicDataSet) As DataSet
Return dataSet.Obj
End Operator
Public Shared Narrowing Operator CType(ByVal dataSet As DataSet) As DynamicDataSet
Return New DynamicDataSet(dataSet)
End Operator
End Class
</pre><p>Notice how we provide overrides for both the <code>TryGetMember()</code> and <code>TrySetMember()</code> methods which will allow us not only to select a table from our data set via dynamic properties, but also to create a new table. The <code>TryGetIndex()</code> and <code>TrySetIndex()</code> overrides allow us to do the same thing using indexers.</p><p>As with the <code>DynamicDataReader</code> class from the <a href="http://mrbigglesworth79.blogspot.com/2011/05/data-access-using-dynamics-part-i.html">previous article</a>, we also provide a pair of conversion operators for easy conversion between the static object and its dynamic wrapper. This is a pattern we will follow throughout this exercise.</p><p>Now it is time to take a look at the <code>DynamicDataTable</code> class. Like the <code>DynamicDataSet</code>, it inherits from <code>DynamicListSourceDataObjectWrapper</code>, allowing it to be used with ASP.NET data controls. We override the <code>TryGetMember()</code> and <code>TrySetMember()</code> methods in such a way that accessing a dynamic property will return the appropriate column from the table; and setting a dynamic property will create a new column in the table:</p><pre lang="cs" class="brush: csharp">// C#
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (base.TryGetMember(binder, out result))
return true;
else
{
try
{
result = Obj.Columns[binder.Name];
return true;
}
catch
{
result = null;
return false;
}
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (base.TrySetMember(binder, value))
return true;
else
{
try
{
Type columnType = (Type)value;
Obj.Columns.Add(binder.Name, columnType);
return true;
}
catch(Exception)
{
return false;
}
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Overrides Function TryGetMember(binder As GetMemberBinder, ByRef result As Object) As Boolean
If MyBase.TryGetMember(binder, result) Then
Return True
Else
Try
result = Obj.Columns(binder.Name)
Return True
Catch ex As Exception
result = Nothing
Return False
End Try
End If
End Function
Public Overrides Function TrySetMember(binder As SetMemberBinder, value As Object) As Boolean
If MyBase.TrySetMember(binder, value) Then
Return True
Else
Try
Dim columnType As Type = DirectCast(value, Type)
Obj.Columns.Add(binder.Name, columnType)
Return True
Catch ex As Exception
Return False
End Try
End If
End Function
</pre><p>With the <code>TryGetIndex()</code>, however, we are going to be slightly more creative. If the indexer is accessed via an <code>integer</code>, then we return the respective <em>row</em> from the table. On the other hand, if the indexer is accessed via a <code>string</code>, then we return the respective <em>column</em> from the table:</p><pre lang="cs" class="brush: csharp">// C#
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
try
{
object index = indexes[0];
if (index is int)
{
result = (DynamicDataRow)Obj.Rows[(int)index];
return true;
}
else if (index is string)
{
result = Obj.Columns[(string)index];
return true;
}
else
{
result = null;
return false;
}
}
catch (Exception)
{
result = null;
return false;
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Overrides Function TryGetIndex(binder As GetIndexBinder, indexes() As Object, ByRef result As Object) As Boolean
If MyBase.TryGetIndex(binder, indexes, result) Then
Return True
Else
Try
Dim index As Object = indexes(0)
If TypeOf (index) Is Integer Then
result = CType(Obj.Rows(DirectCast(index, Integer)), DynamicDataRow)
Return True
ElseIf TypeOf (index) Is String Then
result = Obj.Columns(DirectCast(index, String))
Return True
Else
result = Nothing
Return False
End If
Catch ex As Exception
result = Nothing
Return False
End Try
End If
End Function
</pre><p>The <code>DynamicDataTable</code> also implements the <code>IEnumerable</code> interface so that we can easily iterate through all the rows in the table without having to explicitly call the <code>Rows</code> property:</p><pre lang="cs" class="brush: csharp">// C#
public IEnumerator GetEnumerator()
{
return new DynamicDataTableEnumerator(Obj.Rows.GetEnumerator());
}
private class DynamicDataTableEnumerator : IEnumerator
{
private IEnumerator enumerator;
public DynamicDataTableEnumerator(IEnumerator enumerator)
{
this.enumerator = enumerator;
}
public object Current
{
get { return (DynamicDataRow)(DataRow)enumerator.Current; }
}
public bool MoveNext()
{
return enumerator.MoveNext();
}
public void Reset()
{
enumerator.Reset();
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return New DynamicDataTableEnumerator(Obj.Rows.GetEnumerator())
End Function
Private Class DynamicDataTableEnumerator
Implements IEnumerator
Private _enumerator As IEnumerator
Public Sub New(ByVal enumerator As IEnumerator)
_enumerator = enumerator
End Sub
Public ReadOnly Property Current As Object Implements IEnumerator.Current
Get
Return CType(DirectCast(_enumerator.Current, DataRow), DynamicDataRow)
End Get
End Property
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
Return _enumerator.MoveNext()
End Function
Public Sub Reset() Implements IEnumerator.Reset
_enumerator.Reset()
End Sub
End Class
</pre><p>This finally leaves the <code>DynamicDataRow</code> class. Here we override the <code>TryGetMember()</code> and <code>TrySetMember()</code> methods to get and set the appropriate column value respectively, using the indexers of the wrapped <code>DataRow</code> object. We also override the <code>TryGetIndex()</code> and <code>TrySetIndex()</code> methods so we still have the option of using the indexers if we wish. For the sake of brevity, I've omitted the code, as it is very similar to what we've seen already.</p><h2>Now for some Examples</h2><p>Here are some examples of our <code>DynamicDataSet</code> in action. As in the <a href="http://mrbigglesworth79.blogspot.com/2011/05/data-access-using-dynamics-part-i.html">previous article</a>, they all use the Northwind database are not intended to serve as examples of good data-access practice. Firstly, selecting from a database table and populating a data object:</p><pre lang="cs" class="brush: csharp">// C#
public static Employee[] GetEmployees()
{
List<Employee> employees = new List<Employee>();
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string query = "SELECT EmployeeID, LastName, FirstName, Title, TitleOfCourtesy, BirthDate, HireDate, Address, City, Region, PostalCode, Country, HomePhone, Extension, Photo, Notes, ReportsTo, PhotoPath " +
"FROM dbo.Employees";
SqlCommand command = new SqlCommand(query, connection);
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
dynamic dataSet = new DynamicDataSet();
dataAdapter.Fill(dataSet, "Staff");
foreach (dynamic row in dataSet.Staff)
{
Employee employee = new Employee()
{
Id = row.EmployeeID,
Surname = row.LastName,
FirstName = row.FirstName,
Title = row.Title,
CourtesyTitle = row.TitleOfCourtesy,
DateOfBirth = row.BirthDate,
DateHired = row.HireDate,
Address = row.Address,
City = row.City,
Region = row.Region,
PostCode = row.PostalCode,
Country = row.Country,
HomePhone = row.HomePhone,
Extension = row.Extension,
Photo = row.Photo,
Notes = row.Notes,
};
employees.Add(employee);
}
}
return employees.ToArray();
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Shared Function GetEmployees() As Employee()
Dim employees As List(Of Employee) = New List(Of Employee)
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim query As String = "SELECT EmployeeID, LastName, FirstName, Title, TitleOfCourtesy, BirthDate, HireDate, Address, City, Region, PostalCode, Country, HomePhone, Extension, Photo, Notes, ReportsTo, PhotoPath " + _
"FROM dbo.Employees"
Dim command As SqlCommand = New SqlCommand(query, connection)
Dim dataAdapter As SqlDataAdapter = New SqlDataAdapter(command)
Dim dataSet As DynamicDataSet = New DynamicDataSet
dataAdapter.Fill(dataSet, "Staff")
For Each row As Object In DirectCast(dataSet, Object).Staff
Dim employee As Employee = New Employee() With _
{ _
.Id = row.EmployeeID, _
.Surname = row.LastName, _
.FirstName = row.FirstName, _
.Title = row.Title, _
.CourtesyTitle = row.TitleOfCourtesy, _
.DateOfBirth = row.BirthDate, _
.DateHired = row.HireDate, _
.Address = row.Address, _
.City = row.City, _
.Region = row.Region, _
.PostCode = row.PostalCode, _
.Country = row.Country, _
.HomePhone = row.HomePhone, _
.Extension = row.Extension, _
.Photo = row.Photo, _
.Notes = row.Notes _
}
employees.Add(employee)
Next
End Using
Return employees.ToArray()
End Function
</pre><p>The following example shows how to create a new table in a data set and use it to insert the its values into a table in the database:</p><pre lang="aspnet" class="brush: xml"><!-- ASP.NET -->
<form id="form1" runat="server">
<div>
<h2>
Insert Example</h2>
<div>
<table>
<tbody>
<tr>
<th>
First Name:
</th>
<td>
<asp:TextBox ID="firstNameTextBox" runat="server" />
</td>
</tr>
<tr>
<th>
Surname:
</th>
<td>
<asp:TextBox ID="surnameTextBox" runat="server" />
</td>
</tr>
<tr>
<th>
Home Phone:
</th>
<td>
<asp:TextBox ID="homePhoneTextBox" runat="server" />
</td>
</tr>
<tr>
<th>
Extension:
</th>
<td>
<asp:TextBox ID="extensionTextBox" runat="server" />
</td>
</tr>
</tbody>
</table>
</div>
<div>
<asp:Button ID="createButton" runat="server" Text="Create" OnClick="createButton_Click" />
<asp:Label ID="resultLabel" runat="server" ForeColor="Red" />
</div>
</div>
</form>
</pre><pre lang="cs" class="brush: csharp">// C#
protected void createButton_Click(object sender, EventArgs e)
{
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string insertQuery = "INSERT INTO dbo.Employees(FirstName, LastName, HomePhone, Extension) " +
"VALUES(@FirstName, @LastName, @HomePhone, @Extension)";
SqlCommand insertCommand = new SqlCommand(insertQuery, connection);
insertCommand.Parameters.Add("@FirstName", SqlDbType.NVarChar, 10).SourceColumn = "FirstName";
insertCommand.Parameters.Add("@LastName", SqlDbType.NVarChar, 20).SourceColumn = "LastName";
insertCommand.Parameters.Add("@HomePhone", SqlDbType.NVarChar, 24).SourceColumn = "HomePhone";
insertCommand.Parameters.Add("@Extension", SqlDbType.NVarChar, 4).SourceColumn = "Extension";
SqlDataAdapter dataAdapter = new SqlDataAdapter() { InsertCommand = insertCommand };
dynamic dataSet = new DynamicDataSet();
dataSet.Employees.EmployeeID = typeof(int);
dataSet.Employees.FirstName = typeof(string);
dataSet.Employees.LastName = typeof(string);
dataSet.Employees.HomePhone = typeof(string);
dataSet.Employees.Extension = typeof(string);
dynamic newRow = dataSet.Employees.NewRow();
newRow.FirstName = firstNameTextBox.Text;
newRow.LastName = surnameTextBox.Text;
newRow.HomePhone = homePhoneTextBox.Text;
newRow.Extension = extensionTextBox.Text;
dataSet.Employees.Rows.Add(newRow);
dataAdapter.Update(dataSet.Employees);
}
resultLabel.Text = "Item created!";
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Protected Sub createButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles createButton.Click
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim insertQuery As String = "INSERT INTO dbo.Employees(FirstName, LastName, HomePhone, Extension) " + _
"VALUES(@FirstName, @LastName, @HomePhone, @Extension)"
Dim insertCommand As SqlCommand = New SqlCommand(insertQuery, connection)
insertCommand.Parameters.Add("@FirstName", SqlDbType.NVarChar, 10).SourceColumn = "FirstName"
insertCommand.Parameters.Add("@LastName", SqlDbType.NVarChar, 20).SourceColumn = "LastName"
insertCommand.Parameters.Add("@HomePhone", SqlDbType.NVarChar, 24).SourceColumn = "HomePhone"
insertCommand.Parameters.Add("@Extension", SqlDbType.NVarChar, 4).SourceColumn = "Extension"
Dim dataAdapter As SqlDataAdapter = New SqlDataAdapter() With {.InsertCommand = insertCommand}
Dim dataSet As Object = New DynamicDataSet()
dataSet.Employees.EmployeeID = GetType(Integer)
dataSet.Employees.FirstName = GetType(String)
dataSet.Employees.LastName = GetType(String)
dataSet.Employees.HomePhone = GetType(String)
dataSet.Employees.Extension = GetType(String)
Dim newRow As Object = dataSet.Employees.NewRow()
newRow.FirstName = firstNameTextBox.Text
newRow.LastName = surnameTextBox.Text
newRow.HomePhone = homePhoneTextBox.Text
newRow.Extension = extensionTextBox.Text
dataSet.Employees.Rows.Add(newRow)
dataAdapter.Update(dataSet.Employees)
End Using
resultLabel.Text = "Item created!"
End Sub
</pre><p>The following example shows how to use the <code>DynamicDataSet</code> to update a database table</code>:<p><pre lang="aspnet" class="brush: xml"><!-- ASP.NET-->
<form id="form1" runat="server">
<div>
<h2>
Update Example</h2>
<div>
<asp:DropDownList ID="employeeDropDown" runat="server" AppendDataBoundItems="True"
DataTextField="LastName" DataValueField="EmployeeId" AutoPostBack="true" OnSelectedIndexChanged="employeeDropDown_SelectedIndexChanged">
<asp:ListItem Text="Please select..." />
</asp:DropDownList>
</div>
<div>
<table>
<tbody>
<tr>
<th>
Home Phone:
</th>
<td>
<asp:TextBox ID="homePhoneTextBox" runat="server" />
</td>
</tr>
<tr>
<th>
Extension:
</th>
<td>
<asp:TextBox ID="extensionTextBox" runat="server" />
</td>
</tr>
</tbody>
</table>
<asp:Button ID="submitButton" runat="server" Text="Change" OnClick="submitButton_Click" />
</div>
<asp:Label ID="resultLabel" runat="server" ForeColor="Red" />
</div>
</form>
</pre><pre lang="cs" class="brush: csharp">// C#
protected void submitButton_Click(object sender, EventArgs e)
{
int employeeId = int.Parse(employeeDropDown.SelectedValue);
string homePhone = homePhoneTextBox.Text;
string extension = extensionTextBox.Text;
ChangeContactNumbers(employeeId, homePhone, extension);
resultLabel.Text = "Contact Details Changed";
}
private void ChangeContactNumbers(int employeeId, string homePhone, string extension)
{
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string selectQuery = "SELECT EmployeeID, HomePhone, Extension " +
"FROM dbo.Employees " +
"WHERE EmployeeID = @EmployeeID";
string updateQuery = "UPDATE dbo.Employees " +
"SET HomePhone = @HomePhone, " +
"Extension = @Extension " +
"WHERE EmployeeID = @EmployeeID";
SqlCommand selectCommand = new SqlCommand(selectQuery, connection);
selectCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = employeeId;
SqlCommand updateCommand = new SqlCommand(updateQuery, connection);
updateCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).SourceColumn = "EmployeeID";
updateCommand.Parameters.Add("@HomePhone", SqlDbType.NVarChar, 24).SourceColumn = "HomePhone";
updateCommand.Parameters.Add("@Extension", SqlDbType.NVarChar, 4).SourceColumn = "Extension";
SqlDataAdapter dataAdapter = new SqlDataAdapter() { SelectCommand = selectCommand, UpdateCommand = updateCommand };
dynamic dataSet = new DynamicDataSet();
dataAdapter.Fill(dataSet, "Employees");
dataSet.Employees[0].HomePhone = homePhone;
dataSet.Employees[0].Extension = extension;
dataAdapter.Update(dataSet.Employees);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Protected Sub submitButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles submitButton.Click
Dim employeeId As Integer = Integer.Parse(employeeDropDown.SelectedValue)
Dim homePhone As String = homePhoneTextBox.Text
Dim extension As String = extensionTextBox.Text
ChangeContactNumbers(employeeId, homePhone, extension)
resultLabel.Text = "Contact Details Changed"
End Sub
Private Sub ChangeContactNumbers(ByVal employeeId As Integer, homePhone As String, ByVal extension As String)
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim selectQuery As String = "SELECT EmployeeID, HomePhone, Extension " + _
"FROM dbo.Employees " + _
"WHERE EmployeeID = @EmployeeID"
Dim updateQuery As String = "UPDATE dbo.Employees " + _
"SET HomePhone = @HomePhone, " + _
"Extension = @Extension " + _
"WHERE EmployeeID = @EmployeeID"
Dim selectCommand As SqlCommand = New SqlCommand(selectQuery, connection)
selectCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = employeeId
Dim updateCommand As SqlCommand = New SqlCommand(updateQuery, connection)
updateCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).SourceColumn = "EmployeeID"
updateCommand.Parameters.Add("@HomePhone", SqlDbType.NVarChar, 24).SourceColumn = "HomePhone"
updateCommand.Parameters.Add("@Extension", SqlDbType.NVarChar, 4).SourceColumn = "Extension"
Dim dataAdapter As SqlDataAdapter = New SqlDataAdapter() With {.SelectCommand = selectCommand, .UpdateCommand = updateCommand}
Dim dataSet As Object = New DynamicDataSet()
dataAdapter.Fill(DirectCast(dataSet, DynamicDataSet), "Employees")
dataSet.Employees(0).HomePhone = homePhone
dataSet.Employees(0).Extension = extension
dataAdapter.Update(dataSet.Employees)
End Using
End Sub
</pre><p>Finally, here is an example of how to delete a row from our <code>DynamicDataTable</code>:</p><pre lang="aspnet" class="brush: xml"><!-- ASP.NET -->
<form id="form1" runat="server">
<div>
<h2>
Delete Example</h2>
<div>
<asp:DropDownList ID="employeeDropDown" runat="server" AppendDataBoundItems="True"
DataTextField="LastName" DataValueField="EmployeeId">
</asp:DropDownList>
&nbsp;<asp:Button ID="deleteButton" runat="server" OnClick="deleteButton_Click" Text="Delete" />
</div>
<div>
<asp:Label ID="resultLabel" runat="server" ForeColor="Red" />
</div>
</div>
</form>
</pre><pre lang="cs" class="brush: csharp">// C#
protected void deleteButton_Click(object sender, EventArgs e)
{
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string selectQuery = "SELECT EmployeeID, LastName " +
"FROM dbo.Employees " +
"WHERE EmployeeID = @EmployeeID";
string deleteQuery = "DELETE FROM dbo.Employees " +
"WHERE EmployeeID = @EmployeeID";
SqlCommand selectCommand = new SqlCommand(selectQuery, connection);
selectCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = int.Parse(employeeDropDown.SelectedValue);
SqlCommand deleteCommand = new SqlCommand(deleteQuery, connection);
deleteCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).SourceColumn = "EmployeeID";
SqlDataAdapter dataAdapter = new SqlDataAdapter() { SelectCommand = selectCommand, DeleteCommand = deleteCommand };
dynamic dataSet = new DynamicDataSet();
dataAdapter.Fill(dataSet, "Employees");
dataSet.Employees[0].Delete();
dataAdapter.Update(dataSet.Employees);
}
resultLabel.Text = "Item deleted.";
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Protected Sub deleteButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles deleteButton.Click
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim selectQuery As String = "SELECT EmployeeID, LastName " + _
"FROM dbo.Employees " + _
"WHERE EmployeeID = @EmployeeID"
Dim deleteQuery As String = "DELETE FROM dbo.Employees " + _
"WHERE EmployeeID = @EmployeeID"
Dim selectCommand As SqlCommand = New SqlCommand(selectQuery, connection)
selectCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = Integer.Parse(employeeDropDown.SelectedValue)
Dim deleteCommand As SqlCommand = New SqlCommand(deleteQuery, connection)
deleteCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).SourceColumn = "EmployeeID"
Dim dataAdapter As SqlDataAdapter = New SqlDataAdapter() With {.SelectCommand = selectCommand, .DeleteCommand = deleteCommand}
Dim dataSet As Object = New DynamicDataSet()
dataAdapter.Fill(DirectCast(dataSet, DynamicDataSet), "Employees")
dataSet.Employees(0).Delete()
dataAdapter.Update(dataSet.Employees)
End Using
resultLabel.Text = "Item deleted."
End Sub
</pre><h2>Summary</h2><p>The <code>DynamicDataSet</code> and <code>DynamicDataTable</code> classes provide a loosely-typed means of data access which improves code readability and can be used with syntax which is an approximation to that found with strongly-typed datasets, but without the need to auto-generate the code beforehand.</p><p>The source code for this and the <a href="http://mrbigglesworth79.blogspot.com/2011/05/data-access-using-dynamics-part-i.html">previous</a> article, can be found <a href="http://www.jameshallsweb.co.uk/codesamples/DynamicDataAccess.zip">here</a>.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-76318387966293409152011-05-24T14:14:00.001+01:002011-06-22T08:32:53.423+01:00Data Access using Dynamics - Part I: The DynamicDataReader<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h2>A Trip Down Memory Lane</h2><p>Way back in the mists of time, before the rise ORMs such as Entity Framework, LINQ-to-SQL or NHibernate; data access was typically performed using the ADO.NET <code>DataReader</code> and <code>DataSet</code> classes. To most .NET developers, I am sure the following scenario will be familiar:</p><pre lang="cs" class="brush: csharp">// C#
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString))
{
string sql = "SELECT SomeColumn, AnotherColumn FROM SomeTable";
SqlCommand command = new SqlCommand(sql, connection);
connection.Open();
IDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int foo = reader.GetInt32(reader.GetOrdinal("SomeColumn"));
string bar = reader.GetString(reader.GetOrdinal("AnotherColumn"));
// Do some stuff with the data.
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Using connection As SqlConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("MyDatabase").ConnectionString)
Dim sql As String = "SELECT SomeColumn, AnotherColumn FROM SomeTable"
Dim command As SqlCommand = New SqlCommand(sql, connection)
connection.Open()
Dim reader As IDataReader = command.ExecuteReader()
While reader.Read()
Dim foo As Integer = reader.GetInt32(reader.GetOrdinal("SomeColumn"))
Dim bar As String = reader.GetString(reader.GetOrdinal("AnotherColumn"))
' Do some stuff with the data.
End While
End Using
</pre><p>Now recently, whilst having to work with directly with the ADO.NET classes for the first time in a very long time, I decided to experiment with trying to leverage the Dynamic Language Runtime (DLR) to create a set of loosely-typed data-access classes which can be used in a more object-oriented manner and hopefully aid code readability to boot.</p><h2>The Dynamic DataReader</h2><p>In this article, we are going to look at the <code>DynamicDataReader</code> class, which acts as a dynamic wrapper around the <code>System.Data.Common.DbDataReader</code> class. However, before we start looking at the code for this class in great detail, let's take a look at one of its parent classes: the <code>DynamicDataWrapper</code> class:</p><pre lang="cs" class="brush: csharp">// C#
public abstract class DynamicDataObjectWrapper<T> : DynamicObject
{
protected T Obj { get; private set; }
protected Type ObjType { get; private set; }
public DynamicDataObjectWrapper(T obj)
{
this.Obj = obj;
this.ObjType = obj.GetType();
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
try
{
result = ObjType.InvokeMember(binder.Name, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, Obj, args);
return true;
}
catch (Exception)
{
result = null;
return false;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
PropertyInfo property = ObjType.GetProperty(binder.Name, BindingFlags.Instance | BindingFlags.Public);
if (property != null)
{
result = property.GetValue(Obj, null);
return true;
}
else
{
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
PropertyInfo property = ObjType.GetProperty(binder.Name, BindingFlags.Instance | BindingFlags.Public);
if (property != null)
{
property.SetValue(Obj, value, null);
return true;
}
else
return false;
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public MustInherit Class DynamicDataObjectWrapper(Of T)
Inherits DynamicObject
Private _obj As T
Private _objType As Type
Public Sub New(ByVal obj As T)
_obj = obj
_objType = obj.GetType()
End Sub
Protected ReadOnly Property Obj As T
Get
Return _obj
End Get
End Property
Protected ReadOnly Property ObjType As Type
Get
Return _objType
End Get
End Property
Public Overrides Function TryInvokeMember(binder As System.Dynamic.InvokeMemberBinder, args() As Object, ByRef result As Object) As Boolean
Try
result = ObjType.InvokeMember(binder.Name, BindingFlags.InvokeMethod Or BindingFlags.Instance Or BindingFlags.Public, Nothing, Obj, args)
Return True
Catch ex As Exception
result = Nothing
Return False
End Try
End Function
Public Overrides Function TryGetMember(binder As System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean
Dim _property As PropertyInfo = ObjType.GetProperty(binder.Name, BindingFlags.Instance Or BindingFlags.Public)
If Not _property Is Nothing Then
result = _property.GetValue(Obj, Nothing)
Return True
Else
result = Nothing
Return False
End If
End Function
Public Overrides Function TrySetMember(binder As System.Dynamic.SetMemberBinder, value As Object) As Boolean
Dim _propety As PropertyInfo = ObjType.GetProperty(binder.Name, BindingFlags.Instance Or BindingFlags.Public)
If Not _propety Is Nothing Then
_propety.SetValue(Obj, value, Nothing)
Return True
Else
Return False
End If
End Function
End Class
</pre><p>This class acts as dynamic wrapper for any non-dynamic object and, as such, inherits from <code>DynamicObject</code>. The wrapper overrides the <code>TryGetMember()</code>, <code>TrySetMember()</code> and <code>TryInvokeMember</code> methods such that any calls made on the dynamic object are passed to the underlying wrapped object. For example, if a call is made to a method called <code>Foo()</code> on the dynamic object, the <code>Foo()</code> method (if it exists) is called on the wrapped object.</p><p>Extending this is the <code>DynamicEnumerableDataObjectWrapper</code>, which is a subclass of <code>DynamicDataObjectWrapper</code> which implements the <code>IEnumerable</code> interface by passing calls to the <code>GetEnumerator()</code> method of the wrapped object:</p><pre lang="cs" class="brush: csharp">// C#
public abstract class DynamicEnumerableDataObjectWrapper<T> : DynamicDataObjectWrapper<T>, IEnumerable
where T : IEnumerable
{
public DynamicEnumerableDataObjectWrapper(T obj)
: base(obj)
{
}
public virtual IEnumerator GetEnumerator()
{
return Obj.GetEnumerator();
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public MustInherit Class DynamicEnumerableDataObjectWrapper(Of T As IEnumerable)
Inherits DynamicDataObjectWrapper(Of T)
Implements IEnumerable
Public Sub New(ByVal obj As T)
MyBase.New(obj)
End Sub
Public Overridable Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return Obj.GetEnumerator()
End Function
End Class
</pre><p>The <code>DynamicDataReader</code> class is, in turn, a concrete implementation of <code>DynamicEnumerableDataObjectWrapper</code> which wraps a <code>DbDataReader</code> object:</p><pre lang="cs" class="brush: csharp">// C#
public class DynamicDataReader : DynamicEnumerableDataObjectWrapper<DbDataReader>
{
public DynamicDataReader(DbDataReader reader)
: base(reader)
{
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (base.TryGetMember(binder, out result))
return true;
else
{
try
{
if (!Obj.IsDBNull(Obj.GetOrdinal(binder.Name)))
result = Obj.GetValue(Obj.GetOrdinal(binder.Name));
else
result = null;
return true;
}
catch (Exception)
{
result = null;
return false;
}
}
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
try
{
object index = indexes[0];
if (index is int)
{
int intIndex = (int)index;
if (!Obj.IsDBNull(intIndex))
result = Obj.GetValue(intIndex);
else
result = null;
return true;
}
else if (index is string)
{
string strIndex = (string)index;
if (!Obj.IsDBNull(Obj.GetOrdinal(strIndex)))
result = Obj.GetValue(Obj.GetOrdinal(strIndex));
else
result = null;
return true;
}
else
{
result = null;
return false;
}
}
catch(Exception)
{
result = null;
return false;
}
}
public static implicit operator DbDataReader(DynamicDataReader reader)
{
return reader.Obj;
}
public static explicit operator DynamicDataReader(DbDataReader reader)
{
return new DynamicDataReader(reader);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class DynamicDataReader
Inherits DynamicEnumerableDataObjectWrapper(Of DbDataReader)
Public Sub New(ByVal reader As DbDataReader)
MyBase.New(reader)
End Sub
Public Overrides Function TryGetMember(binder As GetMemberBinder, ByRef result As Object) As Boolean
If MyBase.TryGetMember(binder, result) Then
Return True
Else
Try
If Not Obj.IsDBNull(Obj.GetOrdinal(binder.Name)) Then
result = Obj.GetValue(Obj.GetOrdinal(binder.Name))
Else
result = Nothing
End If
Return True
Catch ex As Exception
result = Nothing
Return False
End Try
End If
End Function
Public Overrides Function TryGetIndex(binder As GetIndexBinder, indexes() As Object, ByRef result As Object) As Boolean
Try
Dim index As Object = indexes(0)
If TypeOf (index) Is Integer Then
Dim intIndex As Integer = DirectCast(index, Integer)
If Not Obj.IsDBNull(intIndex) Then
result = Obj.GetValue(intIndex)
Else
result = Nothing
End If
Return True
ElseIf TypeOf (index) Is String Then
Dim strIndex As String = DirectCast(index, String)
If Not Obj.IsDBNull(Obj.GetOrdinal(strIndex)) Then
result = Obj.GetValue(Obj.GetOrdinal(strIndex))
Else
result = Nothing
End If
Return True
Else
result = Nothing
Return False
End If
Catch ex As Exception
result = Nothing
Return False
End Try
End Function
Public Shared Widening Operator CType(ByVal reader As DynamicDataReader) As DbDataReader
Return reader.Obj
End Operator
Public Shared Narrowing Operator CType(ByVal reader As DbDataReader) As DynamicDataReader
Return New DynamicDataReader(reader)
End Operator
End Class
</pre><p>Firstly, note how we override the <code>TryGetMember()</code> method again. In this override we first call the base method to test whether the member exists on the wrapped object. If it does, we return its value; otherwise, we attempt to read the appropriate column from the wrapped data reader. If this fails (i.e.: the named column does not exist) the method returns <strong>false</strong>, causing a <code>RuntimeBinderException</code> to be thrown at run-time.</p><p>Secondly, we also provide an override for the <code>TryGetIndex()</code> method, which allows us to retrieve column data using an indexer.<br />
<p>Also, note how we also provide two conversion operators for converting between the original object and its wrapper. This allows us to easily use our dynamic wrapper object in places where the framework is expecting the original wrapped object; and to quickly wrap an unwrapped object.</p><h2>Some Examples</h2><p>Here are some examples of our <code>DynamicDataReader</code> in action. All the examples use the Northwind database and are designed to show the <code>DynamicDataReader</code> in action. They are certainly not intended as an example of data-access best-practice (embedded SQL being a case in point)! Firstly, a custom HTTP handler to render a table of employees:</p><pre lang="cs" class="brush: csharp">// C#
public class DataReaderExample : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/html";
context.Response.Write("<html><body>");
context.Response.Write("<h1>Employees:</h1>");
context.Response.Write("<table><thead><tr><th>Employee Number</th><th>Surname</th><th>First Name</th><th>Date of Birth</th></tr></thead><tbody>");
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string query = "SELECT EmployeeID, LastName, FirstName, Title, TitleOfCourtesy, BirthDate, HireDate, Address, City, Region, PostalCode, Country, HomePhone, Extension, Photo, Notes, ReportsTo, PhotoPath " +
"FROM dbo.Employees";
SqlCommand command = new SqlCommand(query, connection);
connection.Open();
dynamic reader = (DynamicDataReader)command.ExecuteReader();
while (reader.Read())
{
int employeeId = reader.EmployeeID;
string lastName = reader.LastName;
string firstName = reader.FirstName;
DateTime birthDate = reader.BirthDate;
context.Response.Write(String.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3:dd/MM/yyyy}</td></tr>", employeeId, lastName, firstName, birthDate));
}
}
context.Response.Write("</tbody></table></body></html>");
}
public bool IsReusable
{
get
{
return false;
}
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class DataReaderExample
Implements System.Web.IHttpHandler
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
context.Response.ContentType = "text/html"
context.Response.Write("<html><body>")
context.Response.Write("<h1>Employees:</h1>")
context.Response.Write("<table><thead><tr><th>Employee Number</th><th>Surname</th><th>First Name</th><th>Date of Birth</th></tr></thead><tbody>")
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim query As String = "SELECT EmployeeID, LastName, FirstName, Title, TitleOfCourtesy, BirthDate, HireDate, Address, City, Region, PostalCode, Country, HomePhone, Extension, Photo, Notes, ReportsTo, PhotoPath " + _
"FROM dbo.Employees"
Dim command As SqlCommand = New SqlCommand(query, connection)
connection.Open()
Dim reader As Object = CType(command.ExecuteReader(), DynamicDataReader)
While (reader.Read())
Dim employeeId As Integer = reader.EmployeeID
Dim lastName As String = reader.LastName
Dim firstName As String = reader.FirstName
Dim birthDate As DateTime = reader.BirthDate
context.Response.Write(String.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3:dd/MM/yyyy}</td></tr>", employeeId, lastName, firstName, birthDate))
End While
End Using
context.Response.Write("</tbody></table></body></html>")
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
</pre><p>As our <code>DynamicDataReader</code> class implements <code>IEnumerable</code>, it can be used with the standard ASP.NET data controls. In this example, it is used as the data source for a <code>GridView</code>:</p><pre lang="aspnet" class="brush: xml"><!-- ASP.NET -->
<form id="form1" runat="server">
<div>
<h1>
GridView Examples</h1>
<h2>
Using DynamicDataReader</h2>
<asp:GridView ID="dataReaderGridView" runat="server" />
</div>
</form>
</pre><pre lang="cs" class="brush: csharp">// C#
private void BindDataReader()
{
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string query = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax " +
"FROM dbo.Customers";
SqlCommand command = new SqlCommand(query, connection);
connection.Open();
dynamic reader = (DynamicDataReader)command.ExecuteReader();
dataReaderGridView.DataSource = reader;
dataReaderGridView.DataBind();
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Sub BindDataReader()
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim query As String = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax " + _
"FROM dbo.Customers"
Dim command As SqlCommand = New SqlCommand(query, connection)
connection.Open()
Dim reader As Object = CType(command.ExecuteReader(), DynamicDataReader)
dataReaderGridView.DataSource = reader
dataReaderGridView.DataBind()
End Using
End Sub
</pre><p>However, in most real-world, multi-tier applications, we are probably most likely to be filling an object, or collection of objects, for use in the higher-level tiers of our system. The final example here shows the use of the <code>DynamicDataReader</code> class to fill an array of <code>Customer</code> objects:</p><pre lang="cs" class="brush: csharp">// C#
public static Customer[] GetCustomers()
{
List<Customer> customers = new List<Customer>();
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
string query = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax " +
"FROM dbo.Customers";
SqlCommand command = new SqlCommand(query, connection);
connection.Open();
dynamic reader = (DynamicDataReader)command.ExecuteReader();
while (reader.Read())
{
Customer customer = new Customer()
{
Id = reader.CustomerID,
Company = reader.CompanyName,
Name = reader.ContactName,
Title = reader.ContactTitle,
Address = reader.Address,
City = reader.City,
Region = reader.Region,
PostCode = reader.PostalCode,
Country = reader.Country,
Phone = reader.Phone,
Fax = reader.Fax
};
customers.Add(customer);
}
return customers.ToArray();
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Shared Function GetCustomers() As Customer()
Dim customers As List(Of Customer) = New List(Of Customer)
Using connection As SqlConnection = New SqlConnection(WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim query As String = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax " + _
"FROM dbo.Customers"
Dim command = New SqlCommand(query, connection)
connection.Open()
Dim reader As Object = CType(command.ExecuteReader(), DynamicDataReader)
While reader.Read()
Dim customer As Customer = New Customer() With _
{ _
.Id = reader.CustomerID, _
.Company = reader.CompanyName, _
.Name = reader.ContactName, _
.Title = reader.ContactTitle, _
.Address = reader.Address, _
.City = reader.City, _
.Region = reader.Region, _
.PostCode = reader.PostalCode, _
.Country = reader.Country, _
.Phone = reader.Phone, _
.Fax = reader.Fax _
}
customers.Add(customer)
End While
End Using
Return customers.ToArray()
End Function
</pre><h2>Limitations</h2><ul><li>If a database column has the same name as a propetry of the wrapped <code>DbDataReader</code> class, it cannot be accessed using the dynamic properties of the wrapper. Instead the column can be accessed via the indexer on the wrapper.</li>
<li>If the application is performance-critical, you may want to consider removing the reflection calls to the wrapped object in the <code>TryGetMember()</code> and <code>TrySetMember()</code> of the base <code>DynamicDataObjectWrapper</code> class.</li>
</ul><h2>Summary</h2><p>The <code>DynamicDataReader</code> provides a means of retrieving data from a database in a way that involves less code and increases code readability.</p><p>The source code will be available for download with Part II.</p><p><strong>Next:</strong> <a href="http://mrbigglesworth79.blogspot.com/2011/05/data-access-using-dynamics-part-ii.html"><em>The DynamicDataSet</em></a></p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-22182747757259266492011-05-16T21:40:00.000+01:002011-06-22T08:32:53.424+01:00Unit Testing 101: Getting Started with NUnit<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h2>Introduction</h2><p>Unit testing is the process of using short, programmatic tests to test the logic and functionality of discreet units of code. The use of such tests brings a whole host of benefits, not least facilitating change within the code base (including refactoring), by being able to easily identify if breaking-changes have been introduced; as well encouraging the use of test-driven development.</p><p><a href="http://www.nunit.org/">NUnit</a> is a well-established, open-source unit testing framework for .NET. It is, in my opinion, extremely easy to use; and is very well supported by most good continuous-integration systems, allowing unit tests to be run automatically as part of the build process.</p><p>This article is primarily for those of you who are new to unit testing and is intended as a basic introduction to unit testing and test-driven development; as well as how to write basic tests using the NUnit framework.</p><h2>Our Scenario</h2><p>The scenario we are going to look at in this article involves a simple <code>BankAccount</code> class. This class has three methods:<p><table><thead>
<tr><th>Method</th><th>Description</th></tr>
</thead> <tbody>
<tr><td><code>Deposit()</code></td><td>Deposits money into the account</td></tr>
<tr><td><code>Withdraw()</code></td><td>Withdraws money from the account. Throws an exception if there are insufficient funds to make the withdrawal.</td></tr>
<tr><td><code>Transfer()</code></td><td>Transfers money to another account. Throws an exception if there are insufficient funds to make the transfer.</td></tr>
</tbody> </table><h2>Our Tests</h2><p>With this scenario in mind, I have written the following unit tests to test the individual functionality of each of these methods:</p><pre class="brush: csharp" lang="cs">// C#
[TestFixture]
public sealed class BankAccountTestFixture
{
private BankAccount bankAccountA;
private BankAccount bankAccountB;
[SetUp]
public void SetUp()
{
bankAccountA = new BankAccount(100.00);
bankAccountB = new BankAccount(20.00);
}
[Test]
public void Deposit()
{
bankAccountA.Deposit(10.00);
Assert.That(bankAccountA.Balance, Is.EqualTo(110.00));
}
[Test]
public void Withdraw()
{
bankAccountA.Withdraw(10.00);
Assert.That(bankAccountA.Balance, Is.EqualTo(90.00));
}
[Test]
public void WithdrawWithInsufficentFunds()
{
Assert.That(() => bankAccountB.Transfer(30.00, bankAccountA), Throws.InstanceOf<OverdrawnException>());
Assert.That(bankAccountA.Balance, Is.EqualTo(100.00));
}
[Test]
public void Transfer()
{
bankAccountA.Transfer(20.00, bankAccountB);
Assert.That(bankAccountA.Balance, Is.EqualTo(80.00));
Assert.That(bankAccountB.Balance, Is.EqualTo(40.00));
}
[Test]
public void TransferWithInsufficientFunds()
{
Assert.That(() => bankAccountB.Transfer(30.00, bankAccountA), Throws.InstanceOf<OverdrawnException>());
Assert.That(bankAccountA.Balance, Is.EqualTo(100.00));
Assert.That(bankAccountB.Balance, Is.EqualTo(20.00));
}
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
<TestFixture()>
Public NotInheritable Class BankAccountTestFixture
Private _bankAccountA As BankAccount
Private _bankAccountB As BankAccount
<SetUp()>
Public Sub SetUp()
_bankAccountA = New BankAccount(100.0)
_bankAccountB = New BankAccount(20.0)
End Sub
<Test()>
Public Sub Deposit()
_bankAccountA.Deposit(10.0)
Assert.That(_bankAccountA.Balance, [Is].EqualTo(110.0))
End Sub
<Test()>
Public Sub Withdraw()
_bankAccountA.Withdraw(10.0)
Assert.That(_bankAccountA.Balance, [Is].EqualTo(90.0))
End Sub
<Test()>
Public Sub WithdrawWithInsufficientFunds()
Assert.That(Sub() _bankAccountB.Transfer(30.0, _bankAccountA), Throws.InstanceOf(Of OverdrawnException)())
Assert.That(_bankAccountA.Balance, [Is].EqualTo(100.0))
End Sub
<Test()>
Public Sub Transfer()
_bankAccountA.Transfer(20.0, _bankAccountB)
Assert.That(_bankAccountA.Balance, [Is].EqualTo(80.0))
Assert.That(_bankAccountB.Balance, [Is].EqualTo(40.0))
End Sub
<Test()>
Public Sub TransferWithInsufficientFunds()
Assert.That(Sub() _bankAccountB.Transfer(30.0, _bankAccountA), Throws.InstanceOf(Of OverdrawnException)())
Assert.That(_bankAccountA.Balance, [Is].EqualTo(100.0))
Assert.That(_bankAccountB.Balance, [Is].EqualTo(20.0))
End Sub
End Class
</pre><p>Now let's examine this code a little more closely. Firstly, you will have probably noticed the liberal use of attribute decoration. These attributes serve to identify each of the component parts of our test suite to NUnit. The <code>TestFixture</code> attribute informs NUnit that the class it is decorating contains one or more tests to be run. The <code>SetUp</code> attribute identifies a method which is to be executed before each test is run. In our example, we use this to ensure that both our bank accounts always have the same starting balances prior to the execution of each test:</p><pre class="brush: csharp" lang="cs">// C#
[SetUp]
public void SetUp()
{
bankAccountA = new BankAccount(100.00);
bankAccountB = new BankAccount(20.00);
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
<SetUp()>
Public Sub SetUp()
_bankAccountA = New BankAccount(100.0)
_bankAccountB = New BankAccount(20.0)
End Sub
</pre><p>There is also a corresponding <code>TearDown</code> attribute which identifies a method which is to be executed after each test is run. Although we don't use this in our example, it generally used for performing any clean-up operations.</p><p>Finally, the <code>Test</code> attribute identifies each of the individual tests. Let's have a look at one of our tests more closely:</p><pre class="brush: csharp" lang="cs">// C#
[Test]
public void TransferWithInsufficientFunds()
{
Assert.That(() => bankAccountB.Transfer(30.00, bankAccountA), Throws.InstanceOf<OverdrawnException>());
Assert.That(bankAccountA.Balance, Is.EqualTo(100.00));
Assert.That(bankAccountB.Balance, Is.EqualTo(20.00));
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
<Test()>
Public Sub TransferWithInsufficientFunds()
Assert.That(Sub() _bankAccountB.Transfer(30.0, _bankAccountA), Throws.InstanceOf(Of OverdrawnException)())
Assert.That(_bankAccountA.Balance, [Is].EqualTo(100.0))
Assert.That(_bankAccountB.Balance, [Is].EqualTo(20.0))
End Sub
</pre><p>The <code>Assert.That()</code> method is used to assert that certain conditions have been satisfied in order to pass the test. In our first assertion, we are asserting that an exception of type <code>OverdrawnException</code> is thrown when we make a call to the <code>Transfer()</code> method of the <code>BankAccount</code> object. The second and third assertions are asserting that the <code>Balance</code> properties of the two bank accounts are equal to 100.00 and 20.00 respectively.</p><h2>Running our Tests (and Fixing our Code)</h2><p>So what happens when we run our tests? NUnit provides a simple WinForms app for running unit tests: We simply load our dll and pick which test(s) we wish to run. The screenshot below shows the output of our tests:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Z21XJUWwy4YMih77MPwjou4zss4X8f-kpFqiYNexQsQpH_w4GMTnro7V3k96OHYPBpkabTSkDnmtsCL-OIRKu2a8esfv0fiXjlhvUZKZOBV5z4HhIzQ3CI2UDyzyE2PVVjIZTdR3Hqo/s1600/NUnit1.PNG" imageanchor="1" style=""><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Z21XJUWwy4YMih77MPwjou4zss4X8f-kpFqiYNexQsQpH_w4GMTnro7V3k96OHYPBpkabTSkDnmtsCL-OIRKu2a8esfv0fiXjlhvUZKZOBV5z4HhIzQ3CI2UDyzyE2PVVjIZTdR3Hqo/" /></a></div><p>As you can see, three of our five tests fail. This is because, in true test-driven development style, I have written the tests first and I am now only part-way through writing the implementation of the <code>BankAccount</code> class. Here is the code so far:<br />
<pre class="brush: csharp" lang="cs">// C#
public sealed class BankAccount
{
public BankAccount()
: this(0)
{
}
public BankAccount(double initialBalance)
{
Balance = initialBalance;
}
public double Balance { get; private set; }
public void Deposit(double amount)
{
Balance += amount;
}
public void Withdraw(double amount)
{
Balance -= amount;
}
public void Transfer(double amount, BankAccount destination)
{
//TODO: Do some stuff here.
}
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
Public NotInheritable Class BankAccount
Private _balance As Double
Public Sub New()
Me.New(0)
End Sub
Public Sub New(ByVal initialBalance As Double)
_balance = initialBalance
End Sub
Public ReadOnly Property Balance As Double
Get
Return _balance
End Get
End Property
Public Sub Deposit(ByVal amount As Double)
_balance = _balance + amount
End Sub
Public Sub Withdraw(ByVal amount As Double)
_balance = _balance - amount
End Sub
Public Sub Transfer(ByVal amount As Double, ByVal destination As BankAccount)
' TODO: Do some stuff here.
End Sub
End Class
</pre><p>As you can see, there is no implementation at all for the <code>Transfer()</code> method. Let's provide one now:</p><pre class="brush: csharp" lang="cs">// C#
public void Transfer(double amount, BankAccount destination)
{
this.Withdraw(amount);
destination.Deposit(amount);
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
Public Sub Transfer(ByVal amount As Double, ByVal destination As BankAccount)
Me.Withdraw(amount)
destination.Deposit(amount)
End Sub
</pre><p>If we re-run the tests, we can now see the <code>Transfer()</code> test now passes:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8nK4jPFhMPx4p526CfZUXRhyeHwwXL7r9LjfdTsml050U1fYL8cpXkY-C0aI4r638ciBPTFJzCwlv64cTlVVZXCSMFO2nS6zyJtGuym-wWg5rTRLNG6PU3dVB1w24jqIEXLpmSlbj3Ik/s1600/NUnit2.PNG" imageanchor="1" style=""><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8nK4jPFhMPx4p526CfZUXRhyeHwwXL7r9LjfdTsml050U1fYL8cpXkY-C0aI4r638ciBPTFJzCwlv64cTlVVZXCSMFO2nS6zyJtGuym-wWg5rTRLNG6PU3dVB1w24jqIEXLpmSlbj3Ik/" /></a></div><p>However, our tests are still failing when we have insufficient funds in our account to make either a withdrawal or a transfer. A simple modification to our <code>Withdraw()</code> method should now fix both of these:</p><pre class="brush: csharp" lang="cs">// C#
public void Withdraw(double amount)
{
if (Balance >= amount)
Balance -= amount;
else
throw new OverdrawnException("You have insufficient funds in the account.");
}
</pre><pre class="brush: vbnet" lang="vb.net">' Visual Basic
Public Sub Withdraw(ByVal amount As Double)
If _balance >= amount Then
_balance = _balance - amount
Else
Throw New OverdrawnException("You have insufficient funds in the account.")
End If
End Sub
</pre><p>Now, all our tests should be green when we run them again:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHEMLKUSPgoOixD4anQW1HusvwRgCmnOahYJBlSmjJkDdPNQso9gXi2nxUJuELbSFwvHLUH8Na_PYmx03adLDTwMlwz7LLAnzU8KzkoSpXjBkjrp3SjyMs3FLaiVvNfxAxI4awMXI0lQM/s1600/NUnit3.PNG" imageanchor="1" style=""><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHEMLKUSPgoOixD4anQW1HusvwRgCmnOahYJBlSmjJkDdPNQso9gXi2nxUJuELbSFwvHLUH8Na_PYmx03adLDTwMlwz7LLAnzU8KzkoSpXjBkjrp3SjyMs3FLaiVvNfxAxI4awMXI0lQM/" /></a></div><h2>Summary</h2><p>Unit testing is a highly useful and important tool when working on development projects of all sizes. It provides a degree of confidence that the code being developed is fit-for-purpose and does not break any existing functionality of the system.</p><p>With the NUnit framework, it is very simple to write unit tests for test-driven development scenarios.</p><h2>Further Reading</h2><ul><li>The official NUnit documentation: <a href="http://www.nunit.org/index.php?p=documentation">http://www.nunit.org/index.php?p=documentation</a></li>
<li>"Advanced Unit Testing." A excellent series of articles on CodeProject: <a href="http://www.codeproject.com/KB/cs/autp1.aspx">http://www.codeproject.com/KB/cs/autp1.aspx</a></li>
</ul>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-49164678499301178882011-05-13T17:45:00.000+01:002011-06-22T08:32:53.425+01:00A Simple Wrapper for log4net<h2>Introduction</h2><p><a href="http://logging.apache.org/log4net/index.html">log4net</a> is a highly flexible logging framework available for .NET. Now firstly, let me say that this article assumes a basic familiarity with the log4net framework, and is in not intended either as an introduction to log4net or a tutorial in how to configure and use log4net.</p><p>If you are unfamiliar with log4net, you can find the documentation <a href="http://logging.apache.org/log4net/release/manual/introduction.html">here</a>, or have a look at <a href="http://www.codeproject.com/KB/trace/log4net_intro.aspx">this article</a> on <a href="http://www.codeproject.com/">CodeProject</a>.</p><h2>The Problem</h2><p>One of my main criticisms of log4net is that I find the syntax somewhat clumsy. Take, for example, the following code to add a line of simple debugging logging:</p><pre lang="cs" class="brush: csharp">// C#
ILog log = LogManager.GetLogger("MyLog");
if (log.IsDebugEnabled)
{
using (ThreadContext.Stacks["MyProperty"].Push("MyValue"))
{
using(ThreadContext.Stacks["MyOtherProperty"].Push("MyOtherValue"))
{
log.Debug("This is a debugging message.");
}
}
}</pre><pre lang="vbnet" class="brush: vbnet">' Visual Basic
Dim log As ILog = LogManager.GetLogger("MyLog")
If log.IsDebugEnabled Then
Using ThreadContext.Stacks("MyProperty").Push("MyValue")
Using ThreadContext.Stacks("MyOtherProperty").Push("MyOtherValue")
log.Debug("This is a debugging message.")
End Using
End Using
End If</pre><p>As you can see, performing what should be a simple logging operation takes up about half-a-dozen lines of code. Clearly for even the smallest application, littering the code with statements like those just shown, simply for logging purposes, is not exactly desirable and would soon become unreadable as the logging code starts to drown out the logic code.</p><p>What is needed is a solution whereby a logging operation can be accomplished with a single line of code.</p><h2>The Solution</h2><p>My solution is a static class, called <code>Logger</code>, which has two methods (plus overloads): <code>Initialize()</code> to initialize log4net, and <code>Log()</code> which actually performs the logging operation</p><p>Firstly, let's look at the <code>Initialize()</code> method. This has two overloads: one takes a single parameter which specifies the config file to be used by log4net. The parameterless overload specifies that log4net should pull its configuration from <em>web.config</em> or <em>app.config</em>:</p><pre lang="cs" class="brush: csharp">// C#
private static bool isInitialized;
public static void Initialize()
{
Initialize(null);
}
public static void Initialize(string configFile)
{
if (!isInitialized)
{
if (!String.IsNullOrEmpty(configFile))
XmlConfigurator.ConfigureAndWatch(new FileInfo(configFile));
else
XmlConfigurator.Configure();
isInitialized = true;
}
else
throw new LoggingInitializationException(
"Logging has already been initialized.");
}</pre><br />
<pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private _isInitialized As Boolean
Public Sub Initialize()
Initialize(Nothing)
End Sub
Public Sub Initialize(ByVal configFile As String)
If Not _isInitialized Then
If Not String.IsNullOrEmpty(configFile) Then
XmlConfigurator.ConfigureAndWatch(New FileInfo(configFile))
Else
XmlConfigurator.Configure()
End If
_isInitialized = True
Else
Throw New LoggingInitializationException("Logging has already been initialized.")
End If
End Sub</pre><p>Next, we'll look at the <code>Log()</code> method and its overloads. These methods accept various combinations of the following parameters:</p><table class="ArticleTable"><thead>
<tr>
<th>Parameter</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>logName</code></td>
<td>The name of the log to write the event to. If omitted, the event is written to all logs.</td>
</tr>
<tr>
<td><code>loggingLevel</code></td>
<td>The level (debug, info, warning, error or fatal) of the event.</td>
</tr>
<tr>
<td><code>message</code></td>
<td>The message.</td>
</tr>
<tr>
<td><code>loggingProperties</code></td>
<td>An object whose properties map to log4net <code>PatternLayout</code> properties (e.g.: <code>%property{Foo}</code> where <code>Foo</code> is the name of the property).</td>
</tr>
<tr>
<td><code>exception</code></td>
<td>The exception (if any) to be logged.</td>
</tr>
</tbody>
</table><p>All of the <code>Log()</code> method overloads ultimately call <code>LogBase()</code> which is where the actual calls to log4net are made. The code for this method is as follows:</p><pre lang="cs" class="brush: csharp">// C#
private static void LogBase(ILog log, LoggingLevel loggingLevel,
string message, object loggingProperties, Exception exception)
{
if (ShouldLog(log, loggingLevel))
{
PushLoggingProperties(loggingProperties);
switch (loggingLevel)
{
case LoggingLevel.Debug: log.Debug(message, exception); break;
case LoggingLevel.Info: log.Info(message, exception); break;
case LoggingLevel.Warning: log.Warn(message, exception); break;
case LoggingLevel.Error: log.Error(message, exception); break;
case LoggingLevel.Fatal: log.Fatal(message, exception); break;
}
PopLoggingProperties(loggingProperties);
}
}</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Sub LogBase(ByVal log As ILog, ByVal loggingLevel As LoggingLevel, _
ByVal message As String, ByVal loggingProperties _
As Object, ByVal exception As Exception)
If ShouldLog(log, loggingLevel) Then
PushLoggingProperties(loggingProperties)
Select Case loggingLevel
Case VisualBasic.LoggingLevel.Debug
log.Debug(message, exception)
Case VisualBasic.LoggingLevel.Info
log.Info(message, exception)
Case VisualBasic.LoggingLevel.Warning
log.Warn(message, exception)
Case VisualBasic.LoggingLevel.Error
log.Error(message, exception)
Case VisualBasic.LoggingLevel.Fatal
log.Fatal(message, exception)
End Select
PopLoggingProperties(loggingProperties)
End If
End Sub</pre><p>As you can see, the first call made by this method is to the <code>ShouldLog()</code> method, which determines whether or not the event should be logged, based on the current logging level:</p><pre lang="cs" class="brush: csharp">// C#
private static bool ShouldLog(ILog log, LoggingLevel loggingLevel)
{
switch (loggingLevel)
{
case LoggingLevel.Debug: return log.IsDebugEnabled;
case LoggingLevel.Info: return log.IsInfoEnabled;
case LoggingLevel.Warning: return log.IsWarnEnabled;
case LoggingLevel.Error: return log.IsErrorEnabled;
case LoggingLevel.Fatal: return log.IsFatalEnabled;
default: return false;
}
}</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Function ShouldLog(ByVal log As ILog, _
ByVal loggingLevel As LoggingLevel) As Boolean
Select Case loggingLevel
Case VisualBasic.LoggingLevel.Debug
Return log.IsDebugEnabled
Case VisualBasic.LoggingLevel.Info
Return log.IsInfoEnabled
Case VisualBasic.LoggingLevel.Warning
Return log.IsWarnEnabled
Case VisualBasic.LoggingLevel.Error
Return log.IsErrorEnabled
Case VisualBasic.LoggingLevel.Fatal
Return log.IsFatalEnabled
Case Else
Return False
End Select
End Function</pre><p>If this method returns <code>true</code>, the next method to be called is <code>PushLoggingProperties()</code>. This takes the <code>loggingProperties</code> parameter and uses Reflection to push the values of each of the properties onto the log4net <code>ThreadContext</code> stack:</p><pre lang="cs" class="brush: csharp">// C#
private static void PushLoggingProperties(object loggingProperties)
{
if (loggingProperties != null)
{
Type attrType = loggingProperties.GetType();
PropertyInfo[] properties = attrType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < properties.Length; i++)
{
object value = properties[i].GetValue(loggingProperties, null);
if (value != null)
ThreadContext.Stacks[properties[i].Name].Push(value.ToString());
}
}
}</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Sub PushLoggingProperties(ByVal loggingProperties As Object)
If Not loggingProperties Is Nothing Then
Dim attrType As Type = loggingProperties.GetType()
Dim properties() As PropertyInfo = attrType.GetProperties(
BindingFlags.Public Or BindingFlags.Instance)
For i As Integer = 0 To properties.Length - 1 Step +1
Dim value As Object = properties(i).GetValue(loggingProperties, Nothing)
If Not value Is Nothing Then
ThreadContext.Stacks(properties(i).Name).Push(value.ToString())
End If
Next
End If
End Sub</pre><p>Next, the code calls the appropriate logging method on the log4net <code>ILog</code> object, based on the current logging level. This is followed by a call to the <code>PopLoggingProperties()</code> which simply performs the reverse of <code>PushLoggingProperties()</code> and pops the properties off the stack.</p><h2>Some Examples</h2><p>Here are some examples of the <code>Logger</code> class in action. In these examples, we are using the following <code>PatternLayout</code>:</p><pre lang="xml" class="brush: xml"><layout type="log4net.Layout.PatternLayout">
<conversionPattern
value="%date [%thread] %-5level %logger [%property{User}]:
%{property{Environment} - %message%newline%exception" />
</layout></pre><pre lang="cs" class="brush: csharp">// C#
// Initialise log4net
Logger.Initialize();
// Write a debugging event to all logs.
Logger.Log(LoggingLevel.Debug, "Calling method Foo().",
new { User = "Joe Bloggs", Environment = "UAT" });
// Write an exception to the log called "ErrorLog".
try
{
int i = 25;
int j = 0;
int foo = i / j;
}
catch (DivideByZeroException ex)
{
Logger.Log("ErrorLog", LoggingLevel.Error, "Attempted to divide by zero.",
new { User = "Fred Bloggs", Environment = "Production" }, ex);
}</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
' Initialise log4net
Logger.Initialize()
' Write a debugging event to all logs.
Logger.Log(LoggingLevel.Debug, "Calling method Foo().", _
New With {.User = "Joe Bloggs", .Environment = "UAT"})
' Write an exception to the log called "ErrorLog".
Try
Dim i As Integer = 25
Dim j As Integer = 0
Dim foo As Integer = CInt(i / j)
Catch ex As DivideByZeroException
Logger.Log("ErrorLog", LoggingLevel.Error, "Attempted to divide by zero.", _
New With {.User = "Fred Bloggs", .Environment = "Production"}, ex)
End Try</pre><h2>Summary</h2><p>With a simple wrapper, it is possible abstract away the code for interacting with the log4net framework, and at the same time improve the readability of our code by reducing logging operations to single lines.</p><p>You can download the complete code from <a href="http://www.jameshallsweb.co.uk/codesamples/log4netWrapper.zip">here</a>.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com4tag:blogger.com,1999:blog-5210693437012093137.post-6312905434667811362011-05-07T12:28:00.000+01:002011-06-22T08:32:53.426+01:00Rounding Floating-Point Numbers Up or Down in .NET<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h4>Introduction</h4><p>Yesterday, I needed to be able not only to round a floating-point number to a certain number of decimal places, but also control the direction the rounding (i.e.: always force it to round down).</p><p>Not a problem, I thought. There will be a method on the <code>System.Math</code> class that will let me do just that. How wrong can you be?? My first port of call was the <code>Math.Round()</code> method, but whilst you can specify the number of decimal places with this method, you cannot force it to always round either up or down. Next, I looked at <code>Math.Floor()</code> and <code>Math.Ceiling()</code> methods. These, however, will only round to the nearest integer.</p><p>There was nothing else for it, but to roll my own.</p><h4>The Solution</h4><p>The code for my solution is as follows:</p><pre lang="cs" class="brush: csharp">// C#
public static class EnhancedMath
{
private delegate double RoundingFunction(double value);
private enum RoundingDirection { Up, Down }
public static double RoundUp(double value, int precision)
{
return Round(value, precision, RoundingDirection.Up);
}
public static double RoundDown(double value, int precision)
{
return Round(value, precision, RoundingDirection.Down);
}
private static double Round(double value, int precision, RoundingDirection roundingDirection)
{
RoundingFunction roundingFunction;
if (roundingDirection == RoundingDirection.Up)
roundingFunction = Math.Ceiling;
else
roundingFunction = Math.Floor;
value *= Math.Pow(10, precision);
value = roundingFunction(value);
return value * Math.Pow(10, -1 * precision);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Module EnhancedMath
Private Delegate Function RoundingFunction(ByVal value As Double) As Double
Private Enum RoundingDireciton
Up
Down
End Enum
Public Function RoundUp(ByVal value As Double, ByVal precision As Integer) As Double
Return Round(value, precision, RoundingDireciton.Up)
End Function
Public Function RoundDown(ByVal value As Double, ByVal precision As Integer) As Double
Return Round(value, precision, RoundingDireciton.Down)
End Function
Private Function Round(ByVal value As Double, ByVal precision As Integer, ByVal roundingDirection As RoundingDireciton) As Double
Dim roundingFunction As RoundingFunction
If roundingDirection = RoundingDireciton.Up Then
roundingFunction = AddressOf Math.Ceiling
Else
roundingFunction = AddressOf Math.Floor
End If
value = value * Math.Pow(10, precision)
value = roundingFunction(value)
Return value * Math.Pow(10, -1 * precision)
End Function
End Module
</pre><p>The rounding algorithm is quite simple: Firstly we shift all the digits we are interested in keeping, to the left of the decimal point. We then either call <code>Math.Floor()</code> or <code>Math.Ceiling()</code> on the result depending on whether we are rounding down or up respectively. Finally, we shift our digits back to the right of the decimal point and return the value.</p><p>The method handles values of type <code>Double</code>. You can always add your own overloads to handle values of other types(e.g.: <code>Decimal</code>).</p><h4>Some Examples</h4><p>Here are some examples of our code in action:</p><pre lang="cs" class="brush: csharp">// C#
static void Main(string[] args)
{
Console.WriteLine(EnhancedMath.RoundUp(3.141592654, 2)); // Result: 3.15
Console.WriteLine(EnhancedMath.RoundUp(3.141592654, 3)); // Result: 3.142
Console.WriteLine(EnhancedMath.RoundDown(3.141592654, 2)); // Result: 3.14
Console.WriteLine(EnhancedMath.RoundDown(3.141592654, 3)); // Result: 3.141
Console.Read();
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub Main(ByVal args() As String)
Console.WriteLine(EnhancedMath.RoundUp(3.141592654, 2)) ' Result: 3.15
Console.WriteLine(EnhancedMath.RoundUp(3.141592654, 3)) ' Result: 3.142
Console.WriteLine(EnhancedMath.RoundDown(3.141592654, 2)) ' Result: 3.14
Console.WriteLine(EnhancedMath.RoundDown(3.141592654, 3)) ' Result: 3.141
Console.Read()
End Sub
</pre>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com2tag:blogger.com,1999:blog-5210693437012093137.post-71737080022930738282011-05-06T21:25:00.000+01:002011-06-22T08:32:53.427+01:00Delegates 101 - Part IV: Event Handling<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h4>Introduction</h4><p>So, here we are at the final part of this series on delegates; and it wouldn't be complete without looking at one of the key uses of delegates in .NET: Event handling.</p><h4>A Simple Timer</h4><p>In this article, we are going to look at a simple timer example. This timer raises two events: one when the timer is started and another when the timer has elapsed. The complete code for the timer is as follows:</p><pre lang="cs" class="brush: csharp">// C#
public delegate void TimerEventHandler(object sender, EventArgs eventArgs);
public class SimpleTimer
{
private TimeSpan time;
public event TimerEventHandler Started;
public event TimerEventHandler Elapsed;
public SimpleTimer(long milliseconds)
: this(TimeSpan.FromMilliseconds(milliseconds))
{
}
public SimpleTimer(TimeSpan time)
{
this.time = time;
}
public void Start()
{
Thread timerThread = new Thread
(
delegate()
{
OnStarted(new EventArgs());
Thread.Sleep(time);
OnElapsed(new EventArgs());
}
);
timerThread.Start();
}
private void OnStarted(EventArgs eventArgs)
{
if (Started != null)
Started(this, eventArgs);
}
private void OnElapsed(EventArgs eventArgs)
{
if (Elapsed != null)
Elapsed(this, eventArgs);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Delegate Sub TimerEventHandler(ByVal sender As Object, ByVal eventArgs As EventArgs)
Public Class SimpleTimer
Private _time As TimeSpan
Public Event Started As TimerEventHandler
Public Event Elapsed As TimerEventHandler
Public Sub New(ByVal milliseconds As Long)
Me.New(TimeSpan.FromMilliseconds(milliseconds))
End Sub
Public Sub New(ByVal time As TimeSpan)
_time = time
End Sub
Public Sub Start()
Dim timerThread As Thread = New Thread _
( _
Sub()
OnStarted(New EventArgs())
Thread.Sleep(_time)
OnElapsed(New EventArgs())
End Sub _
)
timerThread.Start()
End Sub
Private Sub OnStarted(ByVal eventArgs As EventArgs)
RaiseEvent Started(Me, New EventArgs())
End Sub
Private Sub OnElapsed(ByVal eventArgs As EventArgs)
RaiseEvent Elapsed(Me, New EventArgs())
End Sub
End Class
</pre><p>Now let's look at this code a little more closely. Firstly, notice how we declare a delegate for any methods which can be used to handle our two events:</p><pre lang="cs" class="brush: csharp">// C#
public delegate void TimerEventHandler(object sender, EventArgs eventArgs);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Delegate Sub TimerEventHandler(ByVal sender As Object, ByVal eventArgs As EventArgs)
</pre><p>By convention, event handlers accept two parameters:</p><table style="text-align: left;"><thead>
<tr>
<th>Parameter</th><th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sender</code></td><td>The object which raised the event.</td>
</tr>
<tr>
<td style="vertical-align: top"><code>eventArgs</code></td><td>An object of type <code>EventArgs</code>, or inherits from <code>EventArgs</code>, which contains encapsulates any optional data we wish to pass to our event handler</td>
</tr>
</tbody>
</table><p>We also declare the two events which out timer is going to raise:</p><pre lang="cs" class="brush: csharp">// C#
public event TimerEventHandler Started;
public event TimerEventHandler Elapsed;
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Event Started As TimerEventHandler
Public Event Elapsed As TimerEventHandler
</pre><p>Notice how the type of each of these events is that of the delegate which is going to handle them, in this case <code>TimerEventHandler</code>.</p><p>We also have convenience methods for raising each of the events:</p><pre lang="cs" class="brush: csharp">// C#
private void OnStarted(EventArgs eventArgs)
{
if (Started != null)
Started(this, eventArgs);
}
private void OnElapsed(EventArgs eventArgs)
{
if (Elapsed != null)
Elapsed(this, eventArgs);
}
</pre><pre lang="vb.net" class="brush:vbnet">' Visual Basic
Private Sub OnStarted(ByVal eventArgs As EventArgs)
RaiseEvent Started(Me, New EventArgs())
End Sub
Private Sub OnElapsed(ByVal eventArgs As EventArgs)
RaiseEvent Elapsed(Me, New EventArgs())
End Sub
</pre><p>Note how we fire the event in exactly the same way as we would call any other delegate. Additionally, in C#, we do a check to see if any handlers have been wired-up up to our event; as firing an event without any handlers will cause an exception to be thrown.</p><p>Finally, the code which actually runs our timer and raises the events. Note how the timer is executed in a separate thread and (simply because it is in-keeping with the theme of this series) is passed to the thread as an anonymous method:</p><pre lang="cs" class="brush: csharp">// C#
public void Start()
{
Thread timerThread = new Thread
(
delegate()
{
OnStarted(new EventArgs());
Thread.Sleep(time);
OnElapsed(new EventArgs());
}
);
timerThread.Start();
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Sub Start()
Dim timerThread As Thread = New Thread _
( _
Sub()
OnStarted(New EventArgs())
Thread.Sleep(_time)
OnElapsed(New EventArgs())
End Sub _
)
timerThread.Start()
End Sub
</pre><h4>Using our Timer</h4><p>The following code shows how to use our simple timer and hook up our own methods to handle the events it raises:</p><pre lang="cs" class="brush: csharp">// C#
static void Main(string[] args)
{
SimpleTimer timer = new SimpleTimer(5000);
timer.Started += timer_Started;
timer.Elapsed += timer_Elapsed;
timer.Start();
Console.Read();
}
static void timer_Started(object sender, EventArgs e)
{
Console.WriteLine("Timer has started.");
}
static void timer_Elapsed(object sender, EventArgs e)
{
Console.WriteLine("Timer has elapsed.");
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub Main(ByVal args() As String)
Dim timer As SimpleTimer = New SimpleTimer(5000)
AddHandler timer.Started, AddressOf timer_Started
AddHandler timer.Elapsed, AddressOf timer_Elapsed
timer.Start()
Console.Read()
End Sub
Private Sub timer_Started(sender As Object, eventArgs As EventArgs)
Console.WriteLine("Timer has started.")
End Sub
Private Sub timer_Elapsed(sender As Object, eventArgs As EventArgs)
Console.WriteLine("Timer has elapsed.")
End Sub
</pre><p>Note how the syntax for wiring up our event handlers to the events is slightly different from what we've seen previously. This is because events are a type of multicast delegate, and as such multiple handlers can be wired up the same event.</p><h4>Summary</h4><p>Handling of events is one of the key uses for delegates in .NET. All event handlers follow a specific convention in terms of their signature and multiple different handlers can be wired up to the same event.</p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com2tag:blogger.com,1999:blog-5210693437012093137.post-72810468947288479952011-05-05T20:39:00.002+01:002011-06-22T08:32:53.428+01:00Delegates 101 - Part III: Generic Delegates<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h4>Introduction</h4><p>In <a href="http://mrbigglesworth79.blogspot.com/2011/05/delegates-101-part-ii-anonymous-methods.html">Part II</a> we looked at how we can use anonymous methods and lambdas to pass in-line method implementations to a method which takes a delegate as a parameter.</p><p>In this article, we will look at how we can leverage the power of generics in order to make our delegates more ...well ...generic!</p><h4>Generic Delegates</h4><p>Continuing with our maths theme, let's look again at our original <code>MathFunction</code> delegate:</p><pre lang="cs" class="brush: csharp">// C#
delegate int MathFunction(int int1, int int2);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Delegate Function MathFunction(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
</pre><p>Well that is fine, providing all we ever want to work with are integers! In the real world, however, we are probably going to want to perform mathematical operations on all sorts of different data types. By use of a generic delegate, we can have a single delegate which will handle any data type we like in a type-safe manner:</p><pre lang="cs" class="brush: csharp">// C#
delegate TResult MathFunction<T1, T2, TResult>(T1 var1, T2 var2);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Delegate Function MathFunction(Of T1, T2, TResult)(ByVal var1 As T1, ByVal var2 As T2) As TResult
</pre><p>Our delegate will now accept parameters of any arbitrary type and return a result of any arbitrary type.</p><p>Next, we need to modify our <code>PrintResult()</code> method accordingly to handle our new delegate:</p><pre lang="cs" class="brush: csharp">// C#
static void PrintResult<T1, T2, TResult>(MathFunction<T1, T2, TResult> mathFunction, T1 var1, T2 var2)
{
TResult result = mathFunction(var1, var2);
Console.WriteLine(String.Format("Result is {0}", result));
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub PrintResult(Of T1, T2, TResult)(ByVal mathFunction As MathFunction(Of T1, T2, TResult), ByVal var1 As T1, ByVal var2 As T2)
Dim result As TResult = mathFunction(var1, var2)
Console.WriteLine(String.Format("Result is {0}", result))
End Sub
</pre><p>Our simple calculator should now be able to perform mathematical operations on any data type of our choosing. Here are some examples:</p><pre lang="cs" class="brush: csharp">// C#
PrintResult((x, y) => x / y, 5, 2); // Integer division - Result: 2
PrintResult((x, y) => x / y, 5, 2.0); // Real division - Result: 2.5
PrintResult((x, y) => 2 * y * x, 25, Math.PI); // Circumference of a circle - Result: 157.07963267949
PrintResult((x, y) => y * Math.Pow(x, 2), 25, Math.PI); // Area of circle - Result: 1963.49540849362
PrintResult((x, y) => (x - y).TotalDays, DateTime.Now, new DateTime(1940, 10, 9)); // Days since birth of John Lennon - Result: 25775.8028079865
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
PrintResult(Function(x, y) CInt(x / y), 5, 2) ' Integer division - Result: 2
PrintResult(Function(x, y) x / y, 5, 2.0) ' Real division - Result: 2.5
PrintResult(Function(x, y) 2 * y * x, 25, Math.PI) ' Circumference of a circle - Result: 157.07963267949
PrintResult(Function(x, y) y * Math.Pow(x, 2), 25, Math.PI) ' Area of circle - Result: 1963.49540849362
PrintResult(Function(x, y) (x - y).TotalDays, DateTime.Now, New DateTime(1940, 10, 9)) ' Days since birth of John Lennon - Result: 25775.8028079865
</pre><p>In each example, note how the types of <code>x</code> and <code>y</code>, as well as the return type of the lambda expression, are inferred by the compiler.</p><h4>Delegate Re-use</h4><p>Our <code>MathFunction</code> generic delegate can now be used as a type for any method that accepts two parameters of any type and returns a value of any type. As such, our delegate is highly re-usable and not just restricted to our simple calculator scenario.</p><p>Indeed, Microsoft have included a whole stack of generic <code>Func</code> and <code>Action</code> delegates in the <code>System</code> namespace which would probably cover most conceivable scenarios. As such, we could have used one of those in our example here; however that would have defeated the object of the exercise. I personally believe there is still a strong case for creating your own delegates in certain scenarios, as it often improves code readability.</p><h4>Summary</h4><p>Using generics allows us to write more versatile delegates which can be re-used for a whole variety of different scenarios. Even when using generic delegates, the compiler is still able to infer the types of any parameters and the return type.</p><p><strong>Next:</strong> <em><a href="http://mrbigglesworth79.blogspot.com/2011/05/delegates-101-part-iv-event-handling.html">Event Handling</a></em></p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-1292929385531848462011-05-04T22:33:00.009+01:002011-06-22T08:32:53.429+01:00Delegates 101 - Part II: Anonymous Methods and Lambdas<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h4>Introduction</h4><p>In <a href="http://mrbigglesworth79.blogspot.com/2011/05/delegates-101-part-i-what-is-delegate.html">Part I</a> of this series, we looked at what .NET delegates are, and how they can be used to hold methods in variables, or to pass methods as parameters into other methods.</p><p>In this article, we are going to look at how we can use anonymous methods and lambdas, to make our code more terse by reducing the amount of code we need to write.</p><h4>Anonymous Methods</h4><p>Firstly, here is a recap of our <code>SimpleCalc.exe</code> code from <a href="http://mrbigglesworth79.blogspot.com/2011/05/delegates-101-part-i-what-is-delegate.html">Part I</a>:</p><pre lang="cs" lang="cs" class="brush: csharp">// C#
delegate int MathFunction(int int1, int int2);
static int Add(int int1, int int2)
{
return int1 + int2;
}
static int Subtract(int int1, int int2)
{
return int1 - int2;
}
static int Multiply(int int1, int int2)
{
return int1 * int2;
}
static int Divide(int int1, int int2)
{
return int1 / int2;
}
static void PrintResult(MathFunction mathFunction, int int1, int int2)
{
int result = mathFunction(int1, int2);
Console.WriteLine(String.Format("Result is {0}", result));
}
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
MathFunction mathFunction;
if (theOperator == '+')
mathFunction = Add;
else if (theOperator == '-')
mathFunction = Subtract;
else if (theOperator == '*')
mathFunction = Multiply;
else
mathFunction = Divide;
PrintResult(mathFunction, left, right);
}
</pre><pre lang="vb.net" lang="vb.net" class="brush: vbnet">' Visual Basic
Delegate Function MathFunction(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Function Add(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 + int2
End Function
Function Subtract(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 - int2
End Function
Function Multiply(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 * int2
End Function
Function Divide(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return CInt(int1 / int2)
End Function
Sub PrintResult(ByVal mathFunction As MathFunction, ByVal int1 As Integer, ByVal int2 As Integer)
Dim result As Integer = mathFunction(int1, int2)
Console.WriteLine(String.Format("Result is {0}", result))
End Sub
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
Dim mathFunction As MathFunction
If theOperator = "+" Then
mathFunction = AddressOf Add
ElseIf theOperator = "-" Then
mathFunction = AddressOf Subtract
ElseIf theOperator = "*" Then
mathFunction = AddressOf Multiply
Else
mathFunction = AddressOf Divide
End If
PrintResult(mathFunction, left, right)
End Sub
</pre><p>As you can see, each method that we want to assign to a variable of type <code>MathFunction</code> has been declared as a specific, named method (e.g.: <code>Add()</code>, <code>Subtract()</code> etc.). Now, it would be good if we could specify our <code>MathFunction</code> methods anonymously and in-line as we pass them into our <code>PrintResult()</code> method.</p><p>Well, this we can do as follows:</p><pre lang="cs" class="brush: csharp">// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
if (theOperator == '+')
PrintResult(delegate(int int1, int int2) { return int1 + int2; }, left, right);
else if (theOperator == '-')
PrintResult(delegate(int int1, int int2) { return int1 - int2; }, left, right);
else if (theOperator == '*')
PrintResult(delegate(int int1, int int2) { return int1 * int2; }, left, right);
else
PrintResult(delegate(int int1, int int2) { return int1 / int2; }, left, right);
}
</pre><p>Note how the parameters required by our anonymous methods are enclosed between the parentheses; and the main body of our methods are enclosed in curly braces. This removes the need to have named methods for each of our mathematical functions, considerably reducing the number of lines of code.</p><p>If you are wondering why there is no Visual Basic example, it is because Visual Basic currently does not support anonymous methods. It does, however, support the use of <a href="#lambdas">Lambdas</a>.</p><h4>Lambdas</h4><p>A statement lambda is, to all intents and purposes, simply a shorthand way of writing an anonymous method. We can thus shorten our code further by changing our anonymous methods into statement lambdas, as follows:</p><pre lang="cs" class="brush: csharp">// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
if (theOperator == '+')
PrintResult((int1, int2) => { return int1 + int2; }, left, right);
else if (theOperator == '-')
PrintResult((int1, int2) => { return int1 - int2; }, left, right);
else if (theOperator == '*')
PrintResult((int1, int2) => { return int1 * int2; }, left, right);
else
PrintResult((int1, int2) => { return int1 / int2; }, left, right);
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
If theOperator = "+" Then
PrintResult(Function(int1, int2)
Return int1 + int2
End Function, left, right)
ElseIf theOperator = "-" Then
PrintResult(Function(int1, int2)
Return int1 - int2
End Function, left, right)
ElseIf theOperator = "*" Then
PrintResult(Function(int1, int2)
Return int1 * int2
End Function, left, right)
Else
PrintResult(Function(int1, int2)
Return CInt(int1 / int2)
End Function, left, right)
End If
End Sub
</pre><p>Note how the types of our <code>int1</code> and <code>int2</code> parameters are now automatically inferred by the compiler.</p><p>We can now reduce our code even further by using expression lambdas instead of statement lambdas. Expression lambdas are single-line lambdas which implicitly return a value. In our example, they would look as follows:</p><pre lang="cs" class="brush: csharp">// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
if (theOperator == '+')
PrintResult((int1, int2) => int1 + int2, left, right);
else if (theOperator == '-')
PrintResult((int1, int2) => int1 - int2, left, right);
else if (theOperator == '*')
PrintResult((int1, int2) => int1 * int2, left, right);
else
PrintResult((int1, int2) => int1 / int2, left, right);
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
If theOperator = "+" Then
PrintResult(Function(int1, int2) int1 + int2, left, right)
ElseIf theOperator = "-" Then
PrintResult(Function(int1, int2) int1 - int2, left, right)
ElseIf theOperator = "*" Then
PrintResult(Function(int1, int2) int1 * int2, left, right)
Else
PrintResult(Function(int1, int2) CInt(int1 / int2), left, right)
End If
End Sub
</pre><p>Note how the body of the lambda is no longer enclosed within an explicit code block; and that the the explicit <code>return</code> statement has been removed.</p><h4>Summary</h4><p>Anonymous methods (C# only) and lambdas (C# and VB.NET) allow us to write much more concise code by removing the need to declare and name each method specifically.</p><p><strong>Next:</strong> <em><a href="http://mrbigglesworth79.blogspot.com/2011/05/delegates-101-part-iii-generic.html">Generic Delegates</a></em></p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com1tag:blogger.com,1999:blog-5210693437012093137.post-58748824673231915492011-05-03T19:19:00.003+01:002011-06-22T08:32:53.430+01:00Delegates 101 - Part I: What is a Delegate?<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<h4>Introduction</h4><p>OK, so what has prompted me to start writing a basic tutorial series; and why on delegates? Well, the answer is quite simple: I have found few (if any) articles on the Web which actually do a good job of explaining what delegates are in .NET and how they can be used. Nearly every article I have read (and there have been a few) go into great depths about event-handling and how delegates are used in that scenario; whilst neglecting to cover the basics. Important though it is, event handling is not a delegate's <em>raison d'être</em>.</p><p>It's little wonder, therefore, that I have seen many a trainee/junior developer (myself included, back in the day...) scratching their heads and looking somewhat befuddled when trying to get their minds round concepts such as passing methods as parameters, and lambda expressions.</p><p>As a result, this article will specifically <strong>not</strong> cover event handling but instead will be, for the moment, sticking with the basics of .NET delegates</p><h4>An Example in JavaScript</h4></p>I'm going to start with an example in JavaScript, in the hope that it will make explanations easier when we move onto a .NET example.</p><p>The following is a simple calculator. The user selects and operator from the drop-down and inputs an integer in either side. When the user clicks the 'Calculate' button, the result of the simple sum is output on the page. Here is the HTML:</p><pre lang="html" class="brush: html"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Simple Calculator</title>
<script src="../Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="../Scripts/SimpleCalc.js" type="text/javascript"></script>
</head>
<body>
<h1>
Simple Calculator</h1>
<div>
<input id="intLeft" type="text" style="width: 50px;" />&nbsp;
<select id="operator">
<option value="+">Add</option>
<option value="-">Subtract</option>
<option value="*">Multiply By</option>
<option value="/">Divide By</option>
</select>&nbsp;
<input id="intRight" type="text" style="width: 50px;" />&nbsp;
<input id="calculate" type="button" value="Calculate" />
</div>
<hr />
<div>
<label for="result">
Result:</label><input id="result" type="text" style="width: 150px;" disabled="disabled" />
</div>
</body>
</html>
</pre><p>And the JavaScript which performs the calculation is broken down as follows. Firstly we have four functions which perform each type of mathematical operation:</p><pre lang="jscript" class="brush: js">function add(int1, int2) {
return int1 + int2;
}
function subtract(int1, int2) {
return int1 - int2;
}
function multiply(int1, int2) {
return int1 * int2;
}
function divide(int1, int2) {
return int1 / int2;
}
</pre><p>OK, nothing magical there. What is more interesting is what we do when the 'Calculate' button is clicked:</p><pre lang="jscript" class="brush: js">$(document).ready(function () {
$("#calculate").click(function () {
var left = parseInt($("#intLeft").val())
var right = parseInt($("#intRight").val());
var operator = $("#operator").val();
var mathFunc;
if (operator === '+')
mathFunc = add;
else if (operator === '-')
mathFunc = subtract;
else if (operator === '*')
mathFunc = multiply;
else
mathFunc = divide;
printResult(mathFunc, left, right);
});
});
</pre><p>As you can see, we first parse the two integers from the textboxes and get the chosen operator from the drop-down. Next we declare the variable <code>mathFunc</code>. Now since JavaScript is a weakly-typed language we can assign just about anything we like to a variable, including <em>functions</em>. We then perform some simple logic to assign the appropriate function to the variable. Note the lack of parentheses ('(' and ')') when assigning the function. This is how we assign the function itself to the variable instead of the result of calling it.</p><p>Next we call the <code>printResult()</code> function, passing in our <code>mathFunc</code> variable as well as the two integers:</p><pre lang="jscript" class="brush: js">function printResult(mathFunc, int1, int2) {
var result = mathFunc(int1, int2);
$("#result").val(result);
}
</pre><p>This function <em>calls</em> the function we have passed to it, and outputs the result to the page.</p><h4>Now for some .NET</h4><p>Now we can do exactly the same thing in .NET and for this illustration I am going to use a console application which takes the two integers and the operator as arguments (e.g.: <code>SimpleCalc.exe 10 + 7</code>)</p><p>Again we have our basic mathematical functions:</p><pre lang="cs" class="brush: csharp">// C#
static int Add(int int1, int int2)
{
return int1 + int2;
}
static int Subtract(int int1, int int2)
{
return int1 - int2;
}
static int Multiply(int int1, int int2)
{
return int1 * int2;
}
static int Divide(int int1, int int2)
{
return int1 / int2;
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Function Add(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 + int2
End Function
Function Subtract(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 - int2
End Function
Function Multiply(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 * int2
End Function
Function Divide(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return CInt(int1 / int2)
End Function
</pre><p>But in a strongly-typed environment, such as .NET, how do we assign a method to a variable? What should the type of such a variable be? Well, this is where delegates come in. Essentially a delegate can be thought of as another custom type which specifies the signature of a method which can be assigned to variables of that type. We declare our delegate as follows:</p><pre lang="cs" class="brush: csharp">// C#
delegate int MathFunction(int int1, int int2);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Delegate Function MathFunction(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
</pre><p>As you can see, the signature of the delegate matches the signatures of our four functions. We are saying that any variable of type <code>MathFunction</code> can only be assigned a method which takes two integers as parameters and returns an integer.</p><p>Now if we look at our <code>PrintResult()</code> method:</p><pre lang="cs" class="brush: csharp">// C#
static void PrintResult(MathFunction mathFunction, int int1, int int2)
{
int result = mathFunction(int1, int2);
Console.WriteLine(String.Format("Result is {0}", result));
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub PrintResult(ByVal mathFunction As MathFunction, ByVal int1 As Integer, ByVal int2 As Integer)
Dim result As Integer = mathFunction(int1, int2)
Console.WriteLine(String.Format("Result is {0}", result))
End Sub
</pre><p>Here we see the <code>mathFunction</code> parameter is of type <code>MathFunction</code> and so accepts one of our functions. As with the JavaScript example, the method calls the function passed to it and outputs the result.</p><p>Finally, the <code>Main()</code> method of our console application:</p><pre lang="cs" class="brush: csharp">// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
MathFunction mathFunction;
if (theOperator == '+')
mathFunction = Add;
else if (theOperator == '-')
mathFunction = Subtract;
else if (theOperator == '*')
mathFunction = Multiply;
else
mathFunction = Divide;
PrintResult(mathFunction, left, right);
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
Dim mathFunction As MathFunction
If theOperator = "+" Then
mathFunction = AddressOf Add
ElseIf theOperator = "-" Then
mathFunction = AddressOf Subtract
ElseIf theOperator = "*" Then
mathFunction = AddressOf Multiply
Else
mathFunction = AddressOf Divide
End If
PrintResult(mathFunction, left, right)
End Sub
</pre><p>Again, as in our JavaScript example, we declare a variable to hold our function and then perform some simple logic to assign the correct function. This, along with the two integers, is then passed to our <code>PrintResult()</code> method for output to the console.</p><h4>Summary</h4><p>In the strongly-typed .NET environment, delegates provide the means by which a variable or parameter can be specified as accepting a method with a particular signature.</p><p><strong>Next:</strong> <em><a href="http://mrbigglesworth79.blogspot.com/2011/05/delegates-101-part-ii-anonymous-methods.html">Anonymous Methods and Lambdas</a></em></p>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com12tag:blogger.com,1999:blog-5210693437012093137.post-52678954386365383772011-05-02T19:27:00.003+01:002011-06-22T08:32:53.431+01:00Oh No! Not Another Way to Write a CSV File??<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<p>It's one of those things I've implemented many times, usually when phrase "... and we'd also like to be able to export it as a CSV." has been tacked onto the end of some requirement at the last minute! Accordingly, each time I have had to knock up some sort of "CSV writer" at the last minute, I have invariably used a different approach.</p><p>The approach I am generally favouring at the moment, mainly for its versatility, is the use of an extension method which can be called from any strongly-typed collection. After all, in most scenarios our data is usually in the shape of some form of object collection. In brief, the extension method is as follows:</p><pre lang="cs" class="brush: csharp">// C#
public static void ToCsv<T>(this IEnumerable<T> objects, Stream outputStream, Encoding encoding, char columnSeparator, string lineTerminator, char encapsulationCharacter, bool autoGenerateColumnHeaders, string[] columnHeaders, params Expression<Func<T, object>>[] outputValues)
{
StreamWriter writer = new StreamWriter(outputStream, encoding);
WriteColumnHeaders(writer, columnSeparator, lineTerminator, encapsulationCharacter, autoGenerateColumnHeaders, columnHeaders, outputValues);
WriteData(objects, writer, columnSeparator, lineTerminator, encapsulationCharacter, outputValues);
writer.Flush();
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
<Extension()>
Public Sub ToCsv(Of T)(ByVal objects As IEnumerable(Of T), ByVal outputStream As Stream, ByVal encoding As Encoding, ByVal columnSeparator As Char, ByVal lineTerminator As String, ByVal encapsulationCharacter As Char, ByVal autoGenerateColumns As Boolean, ByVal columnHeaders() As String, ByVal ParamArray outputValues() As Expression(Of Func(Of T, Object)))
Dim writer As StreamWriter = New StreamWriter(outputStream, encoding)
WriteColumnHeaders(writer, columnSeparator, lineTerminator, encapsulationCharacter, autoGenerateColumns, columnHeaders, outputValues)
WriteData(objects, writer, columnSeparator, lineTerminator, encapsulationCharacter, outputValues)
writer.Flush()
End Sub
</pre><p>The parameters of the method are as follows:</p><table style="text-align: left;"><thead>
<tr> <th>Parameter</th><th>Description</th> </thead> <tbody>
<tr> <td><code>objects</code></td><td>The collection of objects to be output in the CSV.</td> </tr>
<tr> <td><code>outputStream</code></td><td>The <code>Stream</code> to output the CSV to. For example, this could be a filesystem stream or an HTTP stream.</td> </tr>
<tr> <td><code>encoding</code></td><td>The type of character encoding to use.</td> </tr>
<tr> <td><code>columnSeparator</code></td><td>The character used to separate the columns. Traditionally a comma (',').</td> </tr>
<tr> <td><code>lineTerminator</code></td><td>The character sequence to denote the end of a line, e.g.: CRLF for Windows, LF for UNIX.</td> </tr>
<tr> <td><code>encapsulationCharacter</code></td><td>The character used to encapsulate a value if that value contains the <code>columnSeparator</code> character. Traditionally double-quotes ('"').</td> </tr>
<tr> <td><code>autoGenerateColumnHeaders</code></td><td>Specifies whether to auto-generate the column headers.</td> </tr>
<tr> <td><code>columnHeaders</code></td><td>Specifies column headers. Ignored if <code>autoGenerateColumnHeaders</code> is <strong>true</strong>.</td> </tr>
<tr> <td><code>outputValues</code></td><td>A series of expressions to determine which values are to be output to the CSV.</td> </tr>
</tbody> </table><p>As you can see, the extension methods calls two methods: <code>WriteColumnHeaders()</code> and <code>WriteData()</code> for writing the column headers and data respectively. Firstly, let's look at the code for <code>WriteColumnHeaders()</code>:</p><pre lang="cs" class="brush: csharp">// C#
private static void WriteColumnHeaders<T>(StreamWriter writer, char columnSeparator, string lineTerminator, char encapsulationCharacter, bool autoGenerateColumnHeaders, string[] columnHeaders, params Expression<Func<T, object>>[] outputValues)
{
if (autoGenerateColumnHeaders)
{
for (int i = 0; i < outputValues.Length; i++)
{
Expression<Func<T, object>> expression = outputValues[i];
string columnHeader;
if (expression.Body is MemberExpression)
{
MemberExpression body = (MemberExpression)expression.Body;
columnHeader = body.Member.Name;
}
else
columnHeader = expression.Body.ToString();
writer.Write(String.Format("{0}{1}", columnHeader.EncapsulateIfRequired(columnSeparator, encapsulationCharacter), columnSeparator));
}
writer.Write(lineTerminator);
}
else
{
if (columnHeaders != null && columnHeaders.Length > 0)
{
if (columnHeaders.Length == outputValues.Length)
{
for (int i = 0; i < columnHeaders.Length; i++)
writer.Write(String.Format("{0}{1}", columnHeaders[i].EncapsulateIfRequired(columnSeparator, encapsulationCharacter), columnSeparator));
writer.Write(lineTerminator);
}
else
throw new ArgumentException("The number of column headers does not match the number of output values.");
}
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Sub WriteColumnHeaders(Of T)(ByVal writer As StreamWriter, ByVal columnSeparator As Char, ByVal lineTerminator As String, ByVal encapsulationCharacter As Char, ByVal autoGenerateColumns As Boolean, ByVal columnHeaders() As String, ByVal ParamArray outputValues() As Expression(Of Func(Of T, Object)))
If autoGenerateColumns Then
For i As Integer = 0 To outputValues.Length - 1
Dim expression As Expression(Of Func(Of T, Object)) = outputValues(i)
Dim columnHeader As String
If TypeOf expression.Body Is MemberExpression Then
Dim body As MemberExpression = DirectCast(expression.Body, MemberExpression)
columnHeader = body.Member.Name
Else
columnHeader = expression.Body.ToString()
End If
writer.Write("{0}{1}", columnHeader.EncapsulateIfRequired(columnSeparator, encapsulationCharacter), columnSeparator)
Next
writer.Write(lineTerminator)
Else
If Not columnHeaders Is Nothing And columnHeaders.Length > 0 Then
If columnHeaders.Length = outputValues.Length Then
For i As Integer = 0 To columnHeaders.Length - 1
writer.Write(String.Format("{0}{1}", columnHeaders(i).EncapsulateIfRequired(columnSeparator, encapsulationCharacter), columnSeparator))
Next
writer.Write(lineTerminator)
Else
Throw New ArgumentException("The number of column headers does not match the number of output values.")
End If
End If
End If
End Sub
</pre><p>If <code>autoGenerateColumns</code> is set to <strong>true</strong>, then this method will evaluate each of the expressions specified in <code>outputValues</code>. If the expression is a simple <code>MemberExpression</code> (i.e.: a call to a single property) then the column header will be set to the name of the member. If not, then the column header will be set to the string form of the expression.</p><p>If <code>autoGenerateColumns</code> is set to <strong>false</strong>, then the method will use the column headers supplied in <code>columnHeaders</code>, first making sure that the number of columns and column headers match.</p><p>Next, the code for the <code>WriteData()</code> method:</p><pre lang="cs" class="brush: csharp">// C#
private static void WriteData<T>(this IEnumerable<T> objects, StreamWriter writer, char columnSeparator, string lineTerminator, char encapsulationCharacter, params Expression<Func<T, object>>[] outputValues)
{
foreach (T obj in objects)
{
if (obj != null)
{
for (int i = 0; i < outputValues.Length; i++)
{
Func<T, object> valueFunc = outputValues[i].Compile();
object value = valueFunc(obj);
if (value != null)
{
string valueString = value.ToString();
writer.Write(valueString.EncapsulateIfRequired(columnSeparator, encapsulationCharacter));
}
writer.Write(columnSeparator);
}
writer.Write(lineTerminator);
}
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Sub WriteData(Of T)(ByVal objects As IEnumerable(Of T), ByVal writer As StreamWriter, ByVal columnSeparator As Char, ByVal lineTerminator As String, ByVal encapsulationCharacter As Char, ByVal ParamArray outputValues() As Expression(Of Func(Of T, Object)))
For Each obj As T In objects
If Not obj Is Nothing Then
For i As Integer = 0 To outputValues.Length - 1
Dim valueFunc As Func(Of T, Object) = outputValues(i).Compile()
Dim value As Object = valueFunc(obj)
If Not value Is Nothing Then
Dim valueString As String = value.ToString()
writer.Write(valueString.EncapsulateIfRequired(columnSeparator, encapsulationCharacter))
End If
writer.Write(columnSeparator)
Next
writer.Write(lineTerminator)
End If
Next
End Sub
</pre><p>This method enumerates through our collection of objects and outputs the desired values to our CSV, in the order we have specified them.</p><p>You will see that both of these methods make use of a further extension method, <code>EncapsulateIfRequired()</code>, for encapsulating the string values if they contain the column-separator. The code for this method is as follows:</p><pre lang="cs" class="brush: csharp">// C#
private static string EncapsulateIfRequired(this string theString, char columnSeparator, char encapsulationCharacter)
{
if (theString.Contains(columnSeparator))
return String.Format("{1}{0}{1}", theString, encapsulationCharacter);
else
return theString;
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
<Extension()>
Private Function EncapsulateIfRequired(ByVal theString As String, ByVal columnSeparator As Char, ByVal encapsulationCharacter As Char) As String
If theString.Contains(columnSeparator) Then
Return String.Format("{1}{0}{1}", theString, encapsulationCharacter)
Else
Return theString
End If
End Function
</pre><p>With all this in place you can add various overloads as required, to supply default values when calling the method. For example:</p><pre lang="cs" class="brush: csharp">// C#
public static void ToCsv<T>(this IEnumerable<T> objects, Stream outputStream, params Expression<Func<T, object>>[] outputValues)
{
objects.ToCsv(outputStream, Encoding.Default, ',', "\r\n", '"', true, null, outputValues);
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
<Extension()>
Public Sub ToCsv(Of T)(ByVal objects As IEnumerable(Of T), ByVal outputStream As Stream, ByVal ParamArray outputValues() As Expression(Of Func(Of T, Object)))
objects.ToCsv(outputStream, Encoding.Default, ",", vbCrLf, """", True, Nothing, outputValues)
End Sub
</pre><h4>An Example</h4>In this example we are going to output an array of <code>Person</code> objects to a CSV file on the filesystem, with the columns in the order of <code>LastName</code> followed by <code>FirstName</code>:</p><pre lang="cs" class="brush: csharp">// C#
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Person[] people = new Person[]
{
new Person() { FirstName = "Joe", LastName = "Bloggs" },
new Person() { FirstName = "Fred", LastName = "Bloggs" },
new Person() { FirstName = "John", LastName = "Smith" },
new Person() { FirstName= "David, John", LastName = "Jones" }
};
using (Stream fileStream = new FileStream("People.csv", FileMode.Create, FileAccess.Write, FileShare.None))
people.ToCsv(fileStream, x => x.LastName, x => x.FirstName);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class Person
Public Property FirstName As String
Public Property LastName As String
End Class
Dim people() As Person = _
{ _
New Person() With {.FirstName = "Joe", .LastName = "Bloggs"}, _
New Person() With {.FirstName = "Fred", .LastName = "Bloggs"}, _
New Person() With {.FirstName = "John", .LastName = "Smith"}, _
New Person() With {.FirstName = "David, John", .LastName = "Jones"}
}
Using fileStream As Stream = New FileStream("People.csv", FileMode.Create, FileAccess.Write, FileShare.None)
people.ToCsv(fileStream, Function(x) x.LastName, Function(x) x.FirstName)
End Using
</pre>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-6223016442150807412011-04-29T09:28:00.006+01:002011-06-22T08:32:53.432+01:00Accessing ASP.NET Session Data using Dynamics<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<p>We all know that in ASP.NET (up to and including version 3.5) accessing session data was done via an indexer on the <code>HttpSessionState</code> object. For example:</p><pre lang="cs" class="brush: csharp">// C#
Session["MyInt"] = 12;
int myInt = (int)Session["MyInt"];
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Session("MyInt") = 12
Dim myInt as Integer = CInt(Session("MyInt"))
</pre><p>The <code>System.Dynamic</code> namespace (introduced in .NET 4.0) gives us the ability to create objects whose members (properties, methods etc.) are not specifically coded; but instead are added dynamically as and when they are required. This means we should now be able to access session information via a dynamic object where, instead of an indexer, we use a dynamic property to specify the name of the session item.</p><p>We can already see examples of where this has been done elsewhere in the framework. Take, for example, <code>ViewData</code> in MVC:</p><pre lang="cs" class="brush: csharp">// C#
// MVC 1.0 and 2.0
ViewData["MyString"] = "Foo";
// MVC 3.0
ViewBag.MyString = "Foo";
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
' MVC 1.0 and 2.0
ViewData("MyString") = "Foo"
' MVC 3.0
ViewBag.MyString = "Foo"
</pre><p>So how can we achieve the same with ASP.NET session data? Firstly, we need to create ourselves a dynamic object which is going wrap the current <code>HttpSessionState</code> object:</p><pre lang="cs" class="brush: csharp">// C#
public sealed class SessionBag : DynamicObject
{
private SessionBag()
{
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public NotInheritable Class SessionBag
Inherits DynamicObject
Private Sub New()
End Sub
End Class
</pre><p>Note how the class inherits from <code>DynamicObject</code>. This is a base class for specifying dynamic behaviour at run time. Additionally, the constructor is set as private. The reason for this will become apparent later.</p><p>Next, we add a convenience property for accessing the current <code>HttpSessionState</code> object:</p><pre lang="cs" class="brush: csharp">// C#
private HttpSessionStateBase Session
{
get { return new HttpSessionStateWrapper(HttpContext.Current.Session); }
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private ReadOnly Property Session As HttpSessionStateBase
Get
Return New HttpSessionStateWrapper(HttpContext.Current.Session)
End Get
End Property
</pre><p>We then override the <code>TryGetMember()</code> and <code>TrySetMember()</code> methods of <code>DynamicObject</code>. These methods define how our dynamic object should behave when a dynamic property is accessed. In this case we want it to retrieve and add items to the <code>HttpSessionState</code> respectively:</p><pre lang="cs" class="brush: csharp">// C#
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = Session[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
Session[binder.Name] = value;
return true;
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Overrides Function TryGetMember(binder As System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean
result = Session(binder.Name)
Return True
End Function
Public Overrides Function TrySetMember(binder As System.Dynamic.SetMemberBinder, value As Object) As Boolean
Session(binder.Name) = value
Return True
End Function
</pre><p>Additionally, we can override the <code>TryGetIndex()</code> and <code>TrySetIndex()</code> methods so our session data is still accessible using its index position:</p><pre lang="cs" class="brush: csharp">// C#
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
int index = (int)indexes[0];
result = Session[index];
return result != null;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
int index = (int)indexes[0];
Session[index] = value;
return true;
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Overrides Function TryGetIndex(binder As System.Dynamic.GetIndexBinder, indexes() As Object, ByRef result As Object) As Boolean
Dim index As Integer = CInt(indexes(0))
result = Session(index)
Return Not result Is Nothing
End Function
Public Overrides Function TrySetIndex(binder As System.Dynamic.SetIndexBinder, indexes() As Object, value As Object) As Boolean
Dim index As Integer = CInt(indexes(0))
Session(index) = value
Return True
End Function
</pre><p>Finally, we add a static variable for the current <code>SessionBag</code> object and a convenience property for accessing it. This ensures that only one <code>SessionBag</code> object is created and is why its constructor is private:</p><pre lang="cs" class="brush: csharp">// C#
private static readonly SessionBag sessionBag;
static SessionBag()
{
sessionBag = new SessionBag();
}
public static dynamic Current
{
get { return sessionBag; }
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Private Shared ReadOnly _sessionBag As SessionBag
Shared Sub New()
_sessionBag = New SessionBag()
End Sub
Public Shared ReadOnly Property Current As Object
Get
Return _sessionBag
End Get
End Property
</pre><p>Note that in C# the return type of the <code>Current</code> property is of type <code>dynamic</code>. This tells the C# compiler to use late binding on that object and allow members to be added dynamically. The same thing is achieved in Visual Basic by setting <code>Option Strict</code> to <code>Off</code>.</p><p>The complete code for the class is as follows:</p><pre lang="cs" class="brush: csharp">// C#
public sealed class SessionBag : DynamicObject
{
private static readonly SessionBag sessionBag;
static SessionBag()
{
sessionBag = new SessionBag();
}
private SessionBag()
{
}
private HttpSessionStateBase Session
{
get { return new HttpSessionStateWrapper(HttpContext.Current.Session); }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = Session[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
Session[binder.Name] = value;
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
int index = (int)indexes[0];
result = Session[index];
return result != null;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
int index = (int)indexes[0];
Session[index] = value;
return true;
}
public static dynamic Current
{
get { return sessionBag; }
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public NotInheritable Class SessionBag
Inherits DynamicObject
Private Shared ReadOnly _sessionBag As SessionBag
Shared Sub New()
_sessionBag = New SessionBag()
End Sub
Private Sub New()
End Sub
Private ReadOnly Property Session As HttpSessionStateBase
Get
Return New HttpSessionStateWrapper(HttpContext.Current.Session)
End Get
End Property
Public Overrides Function TryGetMember(binder As System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean
result = Session(binder.Name)
Return True
End Function
Public Overrides Function TrySetMember(binder As System.Dynamic.SetMemberBinder, value As Object) As Boolean
Session(binder.Name) = value
Return True
End Function
Public Overrides Function TryGetIndex(binder As System.Dynamic.GetIndexBinder, indexes() As Object, ByRef result As Object) As Boolean
Dim index As Integer = CInt(indexes(0))
result = Session(index)
Return Not result Is Nothing
End Function
Public Overrides Function TrySetIndex(binder As System.Dynamic.SetIndexBinder, indexes() As Object, value As Object) As Boolean
Dim index As Integer = CInt(indexes(0))
Session(index) = value
Return True
End Function
Public Shared ReadOnly Property Current As Object
Get
Return _sessionBag
End Get
End Property
End Class
</pre><h4>An Example</h4><p>Here is an example of our <code>SessionBag</code> class in action. It is a simple MVC application which generates random numbers and uses the ASP.NET session to output the current number and the last number to the browser:</p><pre lang="cs" class="brush: csharp">//C#
public class RandomController : Controller
{
private Random random;
public RandomController()
{
random = new Random();
}
public ActionResult Index()
{
SessionBag.Current.LastNumber = SessionBag.Current.CurrentNumber;
int number = random.Next();
SessionBag.Current.CurrentNumber = number;
return View();
}
}
</pre><pre lang="aspnet" class="brush: html"><%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Random</title>
</head>
<body>
<div>
<% using (Html.BeginForm())
{ %>
<% if (SessionBag.Current.CurrentNumber != null)
{%>
Current number is
<%: SessionBag.Current.CurrentNumber %>
<br />
<%} %>
<% if (SessionBag.Current.LastNumber != null)
{%>
Last number was
<%: SessionBag.Current.LastNumber %>
<br />
<%} %>
<input type="submit" value="Generate" />
<%} %>
</div>
</body>
</html>
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class RandomController
Inherits System.Web.Mvc.Controller
Private _random As Random
Public Sub New()
_random = New Random()
End Sub
Public Function Index() As ActionResult
SessionBag.Current.LastNumber = SessionBag.Current.CurrentNumber
Dim number As Integer = _random.Next()
SessionBag.Current.CurrentNumber = number
Return View()
End Function
End Class
</pre><pre lang="aspnet" class="brush: html"><%@ Page Language="VB" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Random</title>
</head>
<body>
<div>
<%: "" %>
<% Using Html.BeginForm()
%>
<% If Not SessionBag.Current.CurrentNumber Is Nothing Then
%>
Current number is
<%: SessionBag.Current.CurrentNumber %>
<br />
<%
End If%>
<% If Not SessionBag.Current.LastNumber Is Nothing Then
%>
Last number was
<%: SessionBag.Current.LastNumber %>
<br />
<%
End If%>
<input type="submit" value="Generate" />
<%
End Using%>
</div>
</body>
</html>
</pre>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com0tag:blogger.com,1999:blog-5210693437012093137.post-28019472556217808742011-04-28T22:04:00.003+01:002011-06-22T08:32:53.433+01:00Accessing MVC Routing URLs in JavaScript<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<p>As we all know, the MVC framework for ASP.NET uses the .NET routing engine, introduced in .NET 3.5, for generating and resolving URLs at runtime. As such, we can use the <code>UrlHelper</code> class to query the routing engine and generate the correct URL for us. For example:</p><pre lang="aspnet" class="brush: html"><a href='<%= Url.Action("MyAction", "MyController") %>'>Foo<a>
</pre><p>will generate the following at runtime:<br />
<pre lang="html" class="brush: html"><a href='/MyController/MyAction'>Foo<a>
</pre><p>This is great, as it means any changes we make to our routing rules are automagically reflected any affected URLs. Additionally, the routing engine will take into account where on our web-server our application is located. In the example above, if our application were deployed to virtual directory called <code>MySite</code>, the routing engine would generate the following:</p><pre lang="html" class="brush: html"><a href='/MySite/MyController/MyAction'>Foo<a>
</pre><p>Well that's fine and dandy, but what happens when we want to use URLs in JavaScript? Consider the following simple scenario: A simple web-page with a text-box and a button. You enter a person ID into the text-box, click the button; and via an AJAX request, the person's details are displayed on the page. We have a controller:<br />
<pre lang="cs" class="brush: csharp">// C#
public class PersonController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Details(int id)
{
PersonService service = new PersonService();
Person person = service.GetPerson(id);
return PartialView(person);
}
}
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Class PersonController
Inherits System.Web.Mvc.Controller
Public Function Index() As ActionResult
Return View()
End Function
Public Function Details(ByVal id As Integer) As ActionResult
Dim service As PersonService = New PersonService()
Dim person As Person = service.GetPerson(id)
Return PartialView(person)
End Function
End Class
</pre><p>A view:</p><pre lang="aspnet" class="brush: html"><!-- C# -->
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Person</title>
<script type="text/javascript" src='<%= Url.Content("~/Scripts/jquery-1.5.1.min.js") %>'></script>
</head>
<body>
<div>
<input id="personId" type="text" />
<input id="getPersonButton" type="button" value="Get Person" />
</div>
<div id="personPlaceHolder">
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#getPersonButton").click(function () {
var id = $("#personId").val();
getPerson(id);
});
});
function getPerson(id) {
var url = '/Person/Details/' + id;
$.get(url, function (result) {
$("#personPlaceHolder").html(result);
});
}
</script>
</body>
</html>
</pre><pre lang="aspnet" class="brush: html"><!-- Visual Basic -->
<%@ Page Language="VB" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Person</title>
<script type="text/javascript" src='<%= Url.Content("~/Scripts/jquery-1.5.1.min.js") %>'></script>
</head>
<body>
<div>
<input id="personId" type="text" />
<input id="getPersonButton" type="button" value="Get Person" />
</div>
<div id="personPlaceHolder">
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#getPersonButton").click(function () {
var id = $("#personId").val();
getPerson(id);
});
});
function getPerson(id) {
var url = '/Person/Details/' + id;
$.get(url, function (result) {
$("#personPlaceHolder").html(result);
});
}
</script>
</body>
</html>
</pre><p>And a partial view:</p><pre lang="aspnet" class="brush: html"><!-- C# -->
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Person>" %>
<fieldset>
<legend>Person</legend>
<table>
<tbody>
<tr>
<th>
ID
</th>
<td>
<%: Html.DisplayFor(model => model.Id) %>
</td>
</tr>
<tr>
<th>
Name
</th>
<td>
<%: Html.DisplayFor(model => model.Name) %>
</td>
</tr>
<tr>
<th>
Date of Birth
</th>
<td>
<%: Html.DisplayFor(model => model.DateOfBirth) %>
</td>
</tr>
</tbody>
</table>
</fieldset>
</pre><pre lang="aspnet" class="brush: html"><!-- Visual Basic -->
<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of Person)" %>
<fieldset>
<legend>Person</legend>
<table>
<tbody>
<tr>
<th>
ID
</th>
<td>
<%: Html.DisplayFor(Function(model) model.Id)%>
</td>
</tr>
<tr>
<th>
Name
</th>
<td>
<%: Html.DisplayFor(Function(model) model.Name)%>
</td>
</tr>
<tr>
<th>
Date of Birth
</th>
<td>
<%: Html.DisplayFor(Function(model) model.DateOfBirth)%>
</td>
</tr>
</tbody>
</table>
</fieldset>
</pre><p>Now look closely at the <code>getPerson()</code> function:</p><pre lang="jscript" class="brush: js">function getPerson(id) {
var url = '/Person/Details/' + id;
$.get(url, function (result) {
$("#personPlaceHolder").html(result);
});
}
</pre><p>You will see that the URL is hard-coded and the person ID is simply concatenated onto the end. Clearly this will break if we either change our routing rules, or deploy the application to anywhere other the root of our web-server. One solution is to place an ASP.NET call to the routing engine within the string literal used for the URL:</p><pre lang="jscript" class="brush: js">// C#
function getPerson(id) {
var url = '<%= Url.Action("Details", "Person", new { Id = -999 }) %>'.replace('-999', id);
$.get(url, function (result) {
$("#personPlaceHolder").html(result);
});
}
</pre><pre lang="jscript" class="brush: js">// Visual Basic
function getPerson(id) {
var url = '<%= Url.Action("Details", "Person", New With {.Id = -999}) %>'.replace('-999', id);
$.get(url, function (result) {
$("#personPlaceHolder").html(result);
});
}
</pre><p>Note how we use a 'dummy' value of "-999" for the person ID, to ensure that the routing engine resolves the URL correctly. We then replace this value with the 'real' ID at runtime.</p><p>However, what if we want to move the <code>getPerson()</code> function to a separate .js file? Our ASP.NET call to the routing engine will no longer work as the .js file is not processed server-side. The solution is to use a JavaScript helper object:</p><pre lang="jscript" class="brush: js">// C#
function UrlHelper() {
this.personDetails = function (id) {
return '<%= Url.Action("Details", "Person", new { Id = -999 }) %>'.replace('-999', id);
}
}
</pre><pre lang="jscript" class="brush: js">// Visual Basic
function UrlHelper() {
this.personDetails = function (id) {
return '<%= Url.Action("Details", "Person", New With {.Id = -999}) %>'.replace('-999', id);
}
}
</pre><p>The helper object must still remain in the view, as the call to the routing engine needs to be processed server-side. However, our <code>getPerson()</code> function can now be modified to use the helper object and be safely moved into a separate .js file:<br />
<pre lang="jscript" class="brush: js">function getPerson(id) {
var urlHelper = new UrlHelper();
var url = urlHelper.personDetails(id);
$.get(url, function (result) {
$("#personPlaceHolder").html(result);
});
}
</pre>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com6tag:blogger.com,1999:blog-5210693437012093137.post-42130457186802071812011-04-28T16:34:00.006+01:002011-06-22T08:32:53.435+01:00Resizing an Image On-The-Fly using .NET<a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=4331760" style="display: none;" rel="tag">CodeProject</a><br />
<p>The other day, I was given the requirement to be able to dynamically resize a JPEG image server-side before rendering it down to the browser, thus reducing unnecessary bandwidth usage between the server and the client.</p><br />
<p>Clearly it would be more efficient to store the original image in the different sizes required, as resizing the images on-the-fly would put an unnecessary additional load on the server, however in this case it wasn't an option</p><br />
<p>Firstly we are going to need the <code>System.Drawing</code> and <code>System.Drawing.Drawing2D</code> namespaces:</p><br />
<pre lang="cs" class="brush: csharp">// C#
using System.Drawing;
using System.Drawing.Drawing2D;
</pre><br />
<pre lang="vb.net" class="brush: vbnet">' Visual Basic
Imports System.Drawing
Imports System.Drawing.Drawing2D
</pre><br />
<p>Secondly the signature for our method which is going to do the resizing:</p><br />
<pre lang="cs" class="brush: csharp">// C#
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
}
</pre><br />
<pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Shared Function ResizeImage(ByVal image As Image, ByVal size As Size, Optional ByVal preserveAspectRatio As Boolean = True) As Image
End Function
</pre><br />
<p>As you can see, the method takes two mandatory parameters: the image to be resized and the new size it is to be resized to. An optional third parameter specifies whether to preserve the image's original aspect ratio.</p><br />
<p>If we are not going to preserve the aspect ratio of the original image, then we simply set the height and width of the new image accordingly. However, if we <em>do</em> wish to preserve the aspect ratio, then the situation is a little more complex: Firstly we need to calculate the percentage difference, in each axis (height and width), between the original image and the desired size. Then we use whichever difference is the smaller to calculate the new height <strong>and</strong> width of the new image:<br />
<br />
<pre lang="cs" class="brush: csharp">// C#
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = image.Width;
int originalHeight = image.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
</pre>
<pre lang="vb.net" class="brush: vbnet">' Visual Basic
Dim newWidth As Integer
Dim newHeight As Integer
If preserveAspectRatio Then
Dim originalWidth As Integer = image.Width
Dim originalHeight As Integer = image.Height
Dim percentWidth As Single = CSng(size.Width) / CSng(originalWidth)
Dim percentHeight As Single = CSng(size.Height) / CSng(originalHeight)
Dim percent As Single = If(percentHeight < percentWidth, percentHeight, percentWidth)
newWidth = CInt(originalWidth * percent)
newHeight = CInt(originalHeight * percent)
Else
newWidth = size.Width
newHeight = size.Height
End If
</pre>
<p>Next, we create a blank bitmap using our new dimensions:<p><pre lang="cs" class="brush: csharp">// C#
Image newImage = new Bitmap(newWidth, newHeight);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Dim newImage As Image = New Bitmap(newWidth, newHeight)
</pre><p>Finally, we use the graphics handle of the new bitmap to draw the original image onto our new bitmap and return it:</p><pre lang="cs" class="brush: csharp">// C#
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Using graphicsHandle As Graphics = Graphics.FromImage(newImage)
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight)
End Using
Return newImage
</pre><p>You can experiment with the interpolation mode to vary the quality of the resized image. Personally, I found <code>HighQualityBicubic</code> to give the best results. The code for the complete method is as follows:
<pre lang="cs" class="brush: csharp">// C#
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = image.Width;
int originalHeight = image.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
Image newImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
}
</pre>
<pre lang="vb.net" class="brush: vbnet">' Visual Basic
Public Shared Function ResizeImage(ByVal image As Image, ByVal size As Size, Optional ByVal preserveAspectRatio As Boolean = True) As Image
Dim newWidth As Integer
Dim newHeight As Integer
If preserveAspectRatio Then
Dim originalWidth As Integer = image.Width
Dim originalHeight As Integer = image.Height
Dim percentWidth As Single = CSng(size.Width) / CSng(originalWidth)
Dim percentHeight As Single = CSng(size.Height) / CSng(originalHeight)
Dim percent As Single = If(percentHeight < percentWidth, percentHeight, percentWidth)
newWidth = CInt(originalWidth * percent)
newHeight = CInt(originalHeight * percent)
Else
newWidth = size.Width
newHeight = size.Height
End If
Dim newImage As Image = New Bitmap(newWidth, newHeight)
Using graphicsHandle As Graphics = Graphics.FromImage(newImage)
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight)
End Using
Return newImage
End Function
</pre>
<p>We can then call the method at an appropriate point to resize the image. In this example, I simply read the original image from disk and then save the resized image to a <code>MemoryStream</code> for use later in the application:</p><pre lang="cs" class="brush: csharp">// C#
Image original = Image.FromFile(@"C:\path\to\some.jpg");
Image resized = ResizeImage(original, new Size(1024, 768));
MemoryStream memStream = new MemoryStream();
resized.Save(memStream, ImageFormat.Jpeg);
</pre><pre lang="vb.net" class="brush: vbnet">' Visual Basic
Dim original As Image = Image.FromFile("C:\path\to\some.jpg")
Dim resized As Image = ResizeImage(original, New Size(1024, 768))
Dim memStream As MemoryStream = New MemoryStream()
resized.Save(memStream, ImageFormat.Jpeg)
</pre>MrBigglesworthhttp://www.blogger.com/profile/10403500373615286085noreply@blogger.com1