Showing posts with label LINQ. Show all posts
Showing posts with label LINQ. Show all posts

Tuesday, August 12, 2008

XDocument.ToStringWithXmlDeclaration() - Get the string representation of XDcoument with its Xml Declaration

The System.Xml.Linq.XDocument.ToString() produces a serialized string version of the XDocument object. But unfortunately, while doing so, it leaves the xml declaration in the serialized version which may be required in your application.

Again, there is another method called Save that produces the serialized version including xml declaration. So, I think we can write a simple extension method for producing the xml declaration as shown in the following -

14 class Program

15 {

16 static void Main(string[] args)

17 {

18

19 XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("root"));

20 Console.WriteLine(doc.ToStringWithXmlDeclaration());

21 }

22 }

23

24

25 public static class XDocumentExtensions

26 {

27 public static string ToStringWithXmlDeclaration(this XDocument doc)

28 {

29 StringBuilder builder = new StringBuilder();

30 StringWriter writer = new StringWriter(builder);

31 doc.Save(writer);

32 writer.Flush();

33 return builder.ToString();

34 }

35 }

Apart from its purpose, this is also an example use of the Extension Method feature of C# 3.0.

Tuesday, August 05, 2008

Comparing with NULL in where clause using Linq to SQL

In SQL Server, a SQL statement like 'NULL=NULL' evaluates to false. however 'NULL IS NULL' evaluates to true. So, for NULL values in your database columns, you need to use the 'IS' operator instead of the regular '=' operator.

The problem is, in Linq to SQL, there is no such 'IS' operator since 'IS' is already used as a C# language keyword. So, when you are invoking an equality check in your Linq to SQL where clause to a nullable column you need to be alert on this behavior.

For example, take the following sample code that I wrote to demonstrate this topic.

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.Text;

5 using System.IO;

6

7 namespace ConsoleApplication1

8 {

9 class Program

10 {

11 static void Main(string[] args)

12 {

13 MyDataContext context = new MyDataContext();

14 context.Log = new ConsoleWriter();

15

16 string name = null;

17 var aff = from a in context.Affiliates

18 where

19 a.CompanyName == name

20 select a.ID;

21 var aff2 = from a in context.Affiliates where a.CompanyName == null select a.ID;

22

23 aff.ToList();

24 aff2.ToList();

25 }

26 }

27

28 class ConsoleWriter : TextWriter

29 {

30

31 public override Encoding Encoding

32 {

33 get { return Encoding.UTF8; }

34 }

35

36 public override void Write(string value)

37 {

38 base.Write(value);

39 Console.WriteLine(value);

40 }

41

42 public override void Write(char[] buffer, int index, int count)

43 {

44 base.Write(buffer, index, count);

45 Console.WriteLine(buffer, index, count);

46 }

47 }

48 }

In this code, I have attached a sample logger to my DataContext so that all my queries are logged. Now I ran two queries. Lets take a look at the first query and its logger output,

16 string name = null;

17 var aff = from a in context.Affiliates

18 where

19 a.CompanyName == name

20 select a.ID;

The logger output after executing this query is, as follows -

SELECT [t0].[ID]
FROM [dbo].[Affiliates] AS [t0]
WHERE [t0].[CompanyName] = @p0

-- @p0: Input VarChar (Size = 0; Prec = 0; Scale = 0) [Null]

So, you see that although a null is assigned in the variable 'name', the Linq to SQL generated query uses the '=' operator which may lead to undesired results.

However, the second query and its logger output looks like the following -

21 var aff2 = from a in context.Affiliates where a.CompanyName == null select a.ID;

SELECT [t0].[ID]
FROM [dbo].[Affiliates] AS [t0]
WHERE [t0].[CompanyName] IS NULL

Here, the generated query uses the 'IS' operator which is desirable.

In case, you want Linq to SQL to generate the first code using 'IS' operator, you may use something like the following one -

26 var aff3 = from a in context.Affiliates

27 where

28 ((name == null && a.CompanyName == null) || (a.CompanyName == name))

29 select a.ID;

This query produces the following SQL code -

SELECT [t0].[ID]
FROM [dbo].[Affiliates] AS [t0]
WHERE ([t0].[CompanyName] IS NULL) OR ([t0].[CompanyName] = @p0)

So, to end, whenever you are writing a where clause on a nullable column using Linq to SQL, make sure you know the consequences and take measures accordingly.

Happy coding!

Wednesday, May 14, 2008

Read My Article at CodeProject on LINQ to Log4Net Logging

Today CodeProject has published an article of mine where I have shown a way to use Log4Net logging to capture your LINQ to SQL class generated log messages. I suggest you all to take a look and comment at http://www.codeproject.com/KB/linq/LINQ2Log4Net.aspx.

Tuesday, April 29, 2008

How To Set Default Application Wide CommandTimeout in LINQ to SQL DataContext Subclasses

Sometimes you have written long running stored procedures and you need to invoke the stored procedures through your LINQ to SQL class. By default the SqlCommand has a timeout value of 30 seconds and in some instances you may need a longer value to complete your long running database operations. In a situation like this, if you are using the default time out value, you may encounter the following exception scenario,

Error: System.Data.SqlClient.SqlException: Timeout expired

Today I was looking for a solution after I encountered the above exception. In my project I am using LINQ to SQL classes to execute my stored procedures which sometimes may exceed the 30 seconds timeout value to complete the operations. I wanted to set the default timeout for my LINQ to SQL DataContext subclass generated by Visual Studio 2008 and wanted a clean approach. I came up with the following solution and think it may help some of you with similar needs.

Firstly, I can set the command timeout in the following way,

MyDataContext context = new MyDataContext();

context.CommandTimeout = 300; //Value in seconds

However, if I do it this way, then I need to do it at every place where I want the timeout value to be changed from the default value of 30 seconds. And I wanted to avoid this annoying approach. Fortunately, the auto generated DataContext subclass provides me a very simple way to get around to this problem through partial methods.

If you take a look into the auto generated DataContext subclass's code, you will notice the presence of the following partial methods at the top-

#region Extensibility Method Definitions
partial void OnCreated();

//Some other partial Methods depending on your db

#endregion

This OnCreated() method is placed to provide you extensibility with the context constructor logic. Anytime you invoke any one of the constructors of your DataContext subclass, this method is called. However, since this is a partial method without a body, this call has no effect unless I define the body myself!

So, we are going to utilize this method. To do so, we will write another partial class along with this generated partial class (the one at the *.designer.cs file) and implement the partial void OnCreated() method in our newly added partial class where we will set our desired CommandTimeout value.

public partial class MyDataContext : System.Data.Linq.DataContext
{
partial void OnCreated()
{
//Set the timeout value to 300 = 5 Min.
base.CommandTimeout = 300;
}
}

So, we just set the value of CommandTimeout Property of the base DataContext class with just the addition of this new partial class. Also, we got rid of the annoying 'change everywhere' scenario in this approach. Thanks to the creators of the code generator, who were wise enough to foresee our needs, for providing us with a way to take such a clean approach!

By the way, I was interested to see a handy use of the partial method in C# ever since I first learned about it. I am happy that, I found a real life use myself!

Tuesday, April 08, 2008

Posts of the day that I liked most

1. The Monostate Pattern - Another way to look into Singleton and some improvement over singleton issues.

Read it Here

2. Another good article on LINQ regarding the grouping queries. Read it Here

Monday, April 07, 2008

Logging the LINQ to SQL Generated SQL Queries/Commands

I was looking for logging solutions for the generated SQL in one of my projects where I am using LINQ to SQL. I found that sometimes the the Exceptions and StackTrace information does not say much about what's really causing the problem with LINQ to SQL queries/commands.

To see the actual SQL that's generated from LINQ you may write a code like the following-

  1. SampleDataContext context = new SampleDataContext();
  2. //You may wish to use any subclass of System.IO.TextWriter in place of Console.out
  3. context.Log = Console.Out;
  4. Session session = new Session
  5. {
  6. SessionID = Guid.NewGuid().ToString(),
  7. Site = "Google",
  8. StartTime = DateTime.Now
  9. };
  10. context.Sessions.InsertOnSubmit(session);
  11. context.SubmitChanges();

And when run, a log message like the following will be shown in your console.

INSERT INTO [dbo].[Sessions]([SessionID], [StartTime], [Site], [UserHostAddress]
, [Url], [UserAgent], [UrlReferrer])
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6)
-- @p0: Input VarChar (Size = 36; Prec = 0; Scale = 0) [0522d634-d9e6-43c6-985e-
69c61090b5c4]
-- @p1: Input SmallDateTime (Size = 0; Prec = 0; Scale = 0) [4/7/2008 3:28:02 PM
]
-- @p2: Input VarChar (Size = 6; Prec = 0; Scale = 0) [Google]
-- @p3: Input VarChar (Size = 0; Prec = 0; Scale = 0) [Null]
-- @p4: Input VarChar (Size = 0; Prec = 0; Scale = 0) [Null]
-- @p5: Input VarChar (Size = 0; Prec = 0; Scale = 0) [Null]
-- @p6: Input VarChar (Size = 0; Prec = 0; Scale = 0) [Null]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

Well, I know you guys are not just writing Console Applications like me (!) and need something better to be worthy. You may visit the following links for more-

  1. To use Log4Net for capturing the log data, this is a simple yet useful implementation.
  2. As usual, don't forget to take a look at this MSDN page for more on this.