Monthly Archives: May 2012

C#’s overload resolution with enum and object with 0 as value

Today I faced surprising behavior once again. Completely off guard.

Let’s have a code:

static void Test(string s, object o)
{
	Console.WriteLine("object");
}

static void Test(string s, TestEnum e)
{
	Console.WriteLine("enum");
}
enum TestEnum
{
	Zero = 0,
	One = 1,
	Two = 2,
}

and call it:

Test("rrr", 0);
Test("rrr", 1);
Test("rrr", 2);
Test("rrr", -1);
Test("rrr", 100);

What do you think you’ll see? Surprisingly, at least for me, it’s:

enum
object
object
object
object

After searching for a while I found Eric Lippert’s post about it. Basically there’s something we can call magic zero and this can be implicitly converted to any enum (even empty one or one without 0 value). That’s the root cause of this behavior. Of course, when C# compiler sees this value in method call, it selects the more specific overload (the one with TestEnum) over the one with object.

Learning new stuff every day…

One day of my life – debugging SQL generator for Entity Framework support in .NET provider for Firebird

Couple of days back I was hunting some problem in code, that processes and later translates LINQ queries preprocessed by Entity Framework. The bottom line is that you are in code, where you have pieces of query, you’re rewriting these (even partially) or exchanging/adding new pieces if needed. You don’t have whole picture, because pieces are changing and so on.

So even if you partially know what you are producing, at the end you have to look at final SQL command and see whether it’s correct and/or semantically same as previous one, if you applied some changes or optimizations.

It took my roughly a day to find cause of issue and fix it. During this I created a lot of notes with commands and did a lot comparisons. I am placing these here, thus you can take a look at these and enjoy the feeling with me. If you’re interested, you can also check the SqlGenerator class, where I was working mainly.

SELECT 
"Skip1"."Limit1"."id_memo" AS "id_memo", 
"Skip1"."Limit1"."id_text_grup" AS "id_text_grup", 
"Skip1"."Extent2"."nom_grup" AS "nom_grup"
FROM ( SELECT SKIP (0) "Limit1"."id_memo" AS "id_memo", "Limit1"."id_text_grup" AS "id_text_grup1", "Extent2"."id_text_grup" AS "id_text_grup2", "Extent2"."nom_grup" AS "nom_grup"
	FROM   (SELECT FIRST (45) "Extent1"."id_memo" AS "id_memo", "Extent1"."id_text_grup" AS "id_text_grup"
		FROM "memos" AS "Extent1"
		ORDER BY "Extent1"."id_memo" ASC ) AS "Limit1"
	LEFT OUTER JOIN "texts_grups" AS "Extent2" ON "Limit1"."id_text_grup" = "Extent2"."id_text_grup"
	ORDER BY "Limit1"."id_memo" ASC
)  AS "Skip1"
SELECT SKIP (0) 
"Limit1"."id_memo" AS "id_memo", 
"Limit1"."id_text_grup" AS "id_text_grup", 
"Extent2"."nom_grup" AS "nom_grup"
FROM   (SELECT FIRST (45) "Extent1"."id_memo" AS "id_memo", "Extent1"."id_text_grup" AS "id_text_grup"
	FROM "memos" AS "Extent1"
	ORDER BY "Extent1"."id_memo" ASC ) AS "Limit1"
LEFT OUTER JOIN "texts_grups" AS "Extent2" ON "Limit1"."id_text_grup" = "Extent2"."id_text_grup"
SELECT SKIP (0) 
"Limit1"."id_memo" AS "id_memo", 
"Limit1"."id_text_grup" AS "id_text_grup", 
"Extent2"."nom_grup" AS "nom_grup"
FROM   (SELECT FIRST (45) "Extent1"."id_memo" AS "id_memo", "Extent1"."id_text_grup" AS "id_text_grup"
	FROM "memos" AS "Extent1"
	ORDER BY "Extent1"."id_memo" ASC ) AS "Limit1"
LEFT OUTER JOIN "texts_grups" AS "Extent2" ON "Limit1"."id_text_grup" = "Extent2"."id_text_grup"
SELECT SKIP (0) 
"Limit1"."id_memo" AS "id_memo", 
"Limit1"."id_text_grup" AS "id_text_grup", 
"Extent2"."nom_grup" AS "nom_grup"
FROM   (SELECT FIRST (45) "Extent1"."id_memo" AS "id_memo", "Extent1"."id_text_grup" AS "id_text_grup"
	FROM "memos" AS "Extent1"
	ORDER BY "Extent1"."id_memo" ASC ) AS "Limit1"
LEFT OUTER JOIN "texts_grups" AS "Extent2" ON "Limit1"."id_text_grup" = "Extent2"."id_text_grup"
ORDER BY "Limit1"."id_memo" ASC
SELECT SKIP (0) 
"D"."id_memo" AS "id_memo", 
"D"."id_text_grup" AS "id_text_grup", 
"E"."nom_grup" AS "nom_grup"
FROM   (SELECT FIRST (45) "C"."id_memo" AS "id_memo", "C"."id_text_grup" AS "id_text_grup"
	FROM "memos" AS "C"
	ORDER BY "C"."id_memo" ASC ) AS "D"
LEFT OUTER JOIN "texts_grups" AS "E" ON "D"."id_text_grup" = "E"."id_text_grup"
ORDER BY "D"."id_memo" ASC
SELECT SKIP (0) "Limit1"."id_memo" AS "id_memo", "Limit1"."id_text_grup" AS "id_text_grup", "Extent2"."nom_grup" AS "nom_grup" FROM   (SELECT FIRST (45) "Extent1"."id_memo" AS "id_memo", "Extent1"."id_text_grup" AS "id_text_grup" FROM "memos" AS "Extent1" ORDER BY "Extent1"."id_memo" ASC ) AS "Limit1" LEFT OUTER JOIN "texts_grups" AS "Extent2" ON "Limit1"."id_text_grup" = "Extent2"."id_text_grup"
SELECT SKIP (0) "Limit1"."id_memo" AS "id_memo", "Limit1"."id_text_grup" AS "id_text_grup1", "Extent2"."id_text_grup" AS "id_text_grup2", "Extent2"."nom_grup" AS "nom_grup" FROM   (SELECT FIRST (45) "Extent1"."id_memo" AS "id_memo", "Extent1"."id_text_grup" AS "id_text_grup" FROM "memos" AS "Extent1" ORDER BY "Extent1"."id_memo" ASC ) AS "Limit1" LEFT OUTER JOIN "texts_grups" AS "Extent2" ON "Limit1"."id_text_grup" = "Extent2"."id_text_grup" ORDER BY "Limit1"."id_memo" ASC
SELECT SKIP (0) "D"."id_memo" AS "id_memo", "D"."id_text_grup" AS "id_text_grup", "E"."nom_grup" AS "nom_grup" FROM   (SELECT FIRST (45) "C"."id_memo" AS "id_memo", "C"."id_text_grup" AS "id_text_grup" FROM "memos" AS "C" ORDER BY "C"."id_memo" ASC ) AS "D" LEFT OUTER JOIN "texts_grups" AS "E" ON "D"."id_text_grup" = "E"."id_text_grup" ORDER BY "D"."id_memo" ASC
SELECT
"H"."id_memo_data" AS "id_memo_data", 
"H"."id_memo" AS "id_memo", 
"H"."id_idioma" AS "id_idioma", 
"H"."text" AS "text"
FROM   (SELECT SKIP (3) 
	"C"."id_memo" AS "id_memo", 
	(SELECT 
		COUNT("D"."A1") AS "A1"
		FROM ( SELECT 
			1 AS "A1"
			FROM "memos_data" AS "E"
			WHERE "C"."id_memo" = "E"."id_memo"
		)  AS "D") AS "C1"
	FROM "memos" AS "C"
	ORDER BY "C"."C1" DESC ) AS "G"
INNER JOIN "memos_data" AS "H" ON "G"."id_memo" = "H"."id_memo"
SELECT
"H"."id_memo_data" AS "id_memo_data", 
"H"."id_memo" AS "id_memo", 
"H"."id_idioma" AS "id_idioma", 
"H"."text" AS "text"
FROM   (SELECT SKIP (3) "B"."id_memo" AS "id_memo", "B"."C1" AS "C1"
	FROM ( SELECT 
		"C"."id_memo" AS "id_memo", 
		(SELECT 
			COUNT("D"."A1") AS "A1"
			FROM ( SELECT 
				1 AS "A1"
				FROM "memos_data" AS "E"
				WHERE "C"."id_memo" = "E"."id_memo"
			)  AS "D") AS "C1"
		FROM "memos" AS "C"
	)  AS "B"
	ORDER BY "B"."C1" DESC ) AS "G"
INNER JOIN "memos_data" AS "H" ON "G"."id_memo" = "H"."id_memo"
SELECT 
"H"."id_memo_data" AS "id_memo_data", 
"H"."id_memo" AS "id_memo", 
"H"."id_idioma" AS "id_idioma", 
"H"."text" AS "text"
FROM   (SELECT SKIP (3) "skip"."id_memo" AS "id_memo", "skip"."C1" AS "C1"
	FROM ( SELECT 
		"C"."id_memo" AS "id_memo", 
		(SELECT 
			COUNT("D"."A1") AS "A1"
			FROM ( SELECT 
				1 AS "A1"
				FROM "memos_data" AS "E"
				WHERE "C"."id_memo" = "E"."id_memo"
			)  AS "D") AS "C1"
		FROM "memos" AS "C"
	)  AS "skip"
	ORDER BY "skip"."C1" DESC ) AS "G"
INNER JOIN "memos_data" AS "H" ON "G"."id_memo" = "H"."id_memo"

ADO.NET provider for Firebird 2.7.7 released

The 2.7.7 version of ADO.NET provider for Firebird is ready for download. This version contains some bug fixes as well as new functionality.

From bug fixes I’d like to point out to:

  • fixes of Firebird events handling and receiving
  • FbConnection.GetSchema(“ForeignKeyColumns”) fixes
  • Entity Framework query generator fixes

The functionality is mainly about:

You can find all details in tracker and download it from website or NuGet.

Disabling database triggers in FirebirdClient

Database triggers are a nice new feature added to Firebird in version 2.1. And as you can do a lot of stuff with them, sometimes you also might wanna to connect without firing these, especially if you made a mistake there and it’s forcibly closing your connection. :) To disable these, standard Firebird utilities have a new switches. But it boils down to the API itself, nothing magical. 

And if it’s in API, it could be FirebirdClient, right? In last few days among working on other bugs and my daily responsibilities I had a time to dig into this. And now also from .NET world we can use this feature.

It’s on two places. First is standard FbConnection level. New connection string property “no db triggers” (and some aliases) was added (and similarly named property in FbConnectionStringBuilder). If you set it to “True“, database triggers will be disabled for this connection. If you think about pooling and this feature carefully you might come to conclusion it’s unclear how it should behave. Same for me. After a quick discussion in mailing list, it’s now invalid to use pooling and disabling database triggers in same connection string. You’ll get error when trying to use it.

The other part is for backups via FbBackup class. Here it’s based on long lived FbBackupFlags, NoDatabaseTriggers to be precise. Simply add it to your options and you’re done. Connection string’s option is ignored here.

I know nothing mind blowing, but I’m always happy to have new feature there and provide more power for developers.

Using NuoDB from .NET

In previous post I introduced you NuoDB, the so-called NewSQL database. On this foundation we’ll build today. Simple .NET/C# application to access the data, because if I can’t program it why bother with it, right? :)

NuoDB has currently JDBC and ODBC drivers in the box. Other drivers are created by community. There’s also .NET driver being created but it’s based on C++ API implementation not a fully managed implementation.

OK, so for now we’ll try the ODBC as it’s in the box. If you installed NuoDB, the driver was installed to your system already. If you’re going to deploy your application you should install it yourself. Whole ODBC is based on DSNs. You create one in advance and application the uses this one, with minimal knowledge advance how to connect to data source etc. Go to Control Panel > Administrative Tools > Data Sources (ODBC) and you can create DSN there. In .NET world we’re used to use connection strings to describe our data source, so instead of creating DSN manually, I’ll create it in application [1], just for my comfort.

The application is pretty simple, just to try something. Because NuoDB supports standard SQL, you can use constructs you’re familiar with, like CREATE TABLE etc.

using System;
using System.Data;
using System.Data.Odbc;
using System.Linq;

namespace NuoDB
{
	class Program
	{
		[System.Runtime.InteropServices.DllImport("ODBCCP32.dll")]
		static extern bool SQLConfigDataSource(IntPtr parent, int request, string driver, string attributes);
		
		static bool HandleDSN(bool remove)
		{
			return SQLConfigDataSource(IntPtr.Zero, remove ? /* ODBC_REMOVE_DSN */ 3 : /* ODBC_ADD_DSN */ 1, 
			     "NuoDB ODBC Driver\0",
			     "DSN=TestChorusDSN\0UID=admin\0PWD=admin\0Database=TestChorus@localhost\0");

		}

		static void Main(string[] args)
		{
			const string Separator = "\t|\t";

			Console.WriteLine("Adding DSN...");
			HandleDSN(remove: false);

			var csb = new OdbcConnectionStringBuilder();
			csb.Dsn = "TestChorusDSN";
			using (var conn = new OdbcConnection(csb.ToString()))
			{
				Console.WriteLine("Opening connection...");
				conn.Open();
				Console.WriteLine("Starting transaction...");
				using (var tx = conn.BeginTransaction())
				{
					string tableName = string.Format("user.test{0}", DateTime.UtcNow.Ticks);
					using (var cmd = conn.CreateCommand())
					{
						cmd.Transaction = tx;
						cmd.CommandText = string.Format("create table {0} (id int primary key, foobar string not null)", tableName);
						Console.WriteLine("Creating table...");
						cmd.ExecuteNonQuery();
					}
					using (var cmd = conn.CreateCommand())
					{
						cmd.Transaction = tx;
						cmd.CommandText = string.Format("insert into {0}(id, foobar) values (?, ?)", tableName);
						cmd.Prepare();
						IDbDataParameter parameter;
						for (var i = 0; i < 10; i++)
						{
							cmd.Parameters.Clear();

							parameter = cmd.CreateParameter();
							parameter.Value = i;
							cmd.Parameters.Add(parameter);
							
							parameter = cmd.CreateParameter();
							parameter.Value = string.Format("value of {0}", i);
							cmd.Parameters.Add(parameter);

							Console.WriteLine("Inserting...");
							cmd.ExecuteNonQuery();
						}
					}
					using (var cmd = conn.CreateCommand())
					{
						cmd.Transaction = tx;
						cmd.CommandText = string.Format("select * from {0}", tableName);
						Console.WriteLine("Reading data...");
						using (var reader = cmd.ExecuteReader())
						{
							Console.WriteLine(string.Join(Separator, Enumerable.Range(0, reader.FieldCount).Select(x => reader.GetName(x))));
							while (reader.Read())
							{
								var values = new object[reader.FieldCount];
								reader.GetValues(values);
								Console.WriteLine(string.Join(Separator, values));
							}
						}
					}
					Console.WriteLine("Committing...");
					tx.Commit();
				}
			}
			
			Console.WriteLine("Removing DSN...");
			HandleDSN(remove: true);
		}
		
	}
}

First I create DSN for my application (using ugly raw method), using same chorus set up like in previous post. The driver is called NuoDB ODBC Driver. Then I just specify username (UID), password (PWD) and database (Database) in form <chorus>@<location>, same as if you’re using nuosql. This DSN is also removed at the end.

Rest is simple .NET/C# code, same as any other ADO.NET code. Connection, transaction (if explicitly needed), command(s)…

I create table (hence DDL and ExecuteNonQuery works fine) with some magic 8-) name. Then some inserts with parameters (hence parameters, Prepare and ExecuteNonQuery with DML works) and finally select (hence basic stuff around ExecuteReader and IDataReader works).

Considering, I can store data in structured form in tables, use SQL and still be able to scale out easily, I think it’s neat. What could be even better? Having ADO.NET driver (preferable fully managed) and Entity Framework (aka LINQ) support. Maybe in the future…

What’s next? We’ll explore some failure and scaling scenarios.

[1]: http://www.codeproject.com/Articles/7652/Dynamically-adding-DSN-names

Book: “Entity Framework 4.1: Expert’s Cookbook”

I recently did a technical review of book from Packt Publishing (actually I reviewed some chapters, not all). It’s called Entity Framework 4.1: Expert’s Cookbook.

Because I haven’t read it thoroughly during my review, there are some chapters I have no idea what’s inside. The book contains a lot of samples and in a Test Driven Development style. First the test is provided and then the code. It’s really a developer’s cookbook. You don’t have to read a lot of paragraphs to see what’s going on. Just read the test, check the code and if everything is clear, go to next one. If not, there’s a short explanation after the code.

Though I’m not the author and I’m not familiar with the book completely, I’d like to hear your opinions. Maybe in the future I’ll write one as well and this might be good learning.