Wednesday, August 08, 2007

Errors in PageFlowInstanceStore.sql when installing DB for PageFlowQuickstart in WCSF



When trying to install PageFlowQuickstart.sql to create DB for the PageFlowQuickstart of WCSF I've encountered the following errors:



OSQL -S "localhost" -d "PageFlowPersistenceStore" -E -n -i "..\..\Blocks\PageFlow\Scripts\PageFlowInstanceStore.sql"
Msg 137, Level 15, State 2, Server localhost, Procedure pageFlow_DeleteInstance, Line 6
Must declare the scalar variable "@instanceId".
Msg 207, Level 16, State 1, Server localhost, Procedure pageFlow_GetLastRunningInstanceByCorrelationToken, Line 12
Invalid column name 'running'.
Msg 207, Level 16, State 1, Server localhost, Procedure pageFlow_GetInstanceByTypeAndByCorrelationToken, Line 6
Invalid column name 'InstanceId'.
Msg 137, Level 15, State 2, Server localhost, Procedure pageFlow_ChangeInstanceStatus, Line 8
Must declare the scalar variable "@Running".
Msg 137, Level 15, State 2, Server localhost, Procedure pageFlow_SetRunningInstanceForCorrelationToken, Line 24
Must declare the scalar variable "@instanceID".
Msg 207, Level 16, State 1, Server localhost, Procedure pageFlow_GetTypeByInstanceId, Line 9
Invalid column name 'InstanceId'.




If you're getting hit by this problem - here is the reason: whole SQL script is written in assumption that DB collation is case-insensitive.
Just fix the letters case ( @InstanceId instead of @instanceId , etc.) and you have it fixed.

Tuesday, August 07, 2007

How to keep Format Painter active in Office-style applications...


Here is the trick - double click the format painter icon to format multiple elements. Press ESC to end formating.

Monday, July 30, 2007

Free e-book: Rapid C# Windows Development


"Rapid C# for Windows Development" by Joseph Chancellor is available (in .pdf format) for free download from LLBLGen Pro site.

Monday, July 16, 2007

Benchmarking LINQ vs. DAAB vs. ADO.NET



Abstract: This article presents benchmarking results of performance of SP calls using LINQ, Data-access application block and ADO.NET.

Some time ago I was doing LINQ performance benchmarking, looking for an option of replacing our existing data layer with it. One thing that was especially important for me to check - was performance of executing SP's. The reason is that most of our DAL logic resides in SP's at one hand and that performance-critical parts are implemented as SP's as well (for all the good reasons, like possibility to benefit from queries compilation etc).

Below are performance benchmarking results of execution uspGetManagerEmployees SP from AdventureWorks DB. (Basically it does some sort of JOIN to retrieve records of employees data for the specified Manager ID).
I tested three data-access models - raw ADO.NET vs. DAAB (our current DAL workhorse) vs. LINQ (May 2006 preview version).

As a prerequisite for running this test you'll need AdventureWorks DB and installation of LINQ.
Few side-notes:
  • I've left opening of DB connection out of the context of benchmarked section. Only SP execution is measured.

  • I use ExecuteReader rather than ExecuteDataSet, as it is much more lightweight. For LINQ benchmark I using GetEnumerator().

So here goes the test code:

1. Plain ADO.NET:


public static void SqlSPBench()
{
string connectionString =
"Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True";

using( SqlConnection conn = new SqlConnection( connectionString ) )
{
conn.Open();

Stopwatch watch = new Stopwatch();
watch.Start();

int count = 10000;
for( int i = 0; i < count; i++ )
{
using( SqlCommand cmd = new SqlCommand() )
{
cmd.Connection = conn;
cmd.CommandText = "uspGetManagerEmployees";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add( new SqlParameter( "ManagerID", 3 ) );

using( SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.Default ) )
{
//while (reader.Read())
//{
// Console.WriteLine(reader[3]);
//}
}
}
}
watch.Stop();
Console.WriteLine(
"Running " + count +
" SP's using SQL ExecuteReader took " + watch.ElapsedMilliseconds + " ms");
Console.ReadLine();
}
}


2. DAAB (Data Access Application Block):


public static void SqlSpDAAB()
{
Database adventureWorksDataBase = DatabaseFactory.CreateDatabase( "AdventureWorks" );

Stopwatch watch = new Stopwatch();
watch.Start();

int count = 10000;
for( int i =0; i<count; i++ )
{
using( DBCommandWrapper sp_uspGetManagerEmployees =
adventureWorksDataBase.GetStoredProcCommandWrapper(
"uspGetManagerEmployees" ) )
{
sp_uspGetManagerEmployees.AddInParameter( "@ManagerID", DbType.Int32, 3 );
using( IDataReader dataReader =
adventureWorksDataBase.ExecuteReader( sp_uspGetManagerEmployees ) )
{
//DataSet results =
// adventureWorksDataBase.ExecuteDataSet( sp_uspGetManagerEmployees );
}
}
}
watch.Stop();
Console.WriteLine(
"Running " + count + " SP's using DAAB took " +
watch.ElapsedMilliseconds + " ms");
Console.ReadLine();
}


3. LINQ (using ORM layer produced by SQLMetal.exe - "c:\Program Files\LINQ Preview\Bin\SqlMetal.exe" /server:. /database:AdventureWorks /pluralize /sprocs /code:AdventureWorks.cs):



public static void LinqSPBench()
{
string connectionString =
"Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True";
AdventureWorks adventureWorks = new AdventureWorks( connectionString );

Stopwatch watch = new Stopwatch();
watch.Start();

int count = 10000;
for( int i =0; i<count; i++ )
{
StoredProcedureResult<UspGetManagerEmployeesResult> result =
adventureWorks.UspGetManagerEmployees( 3 );
IEnumerator<UspGetManagerEmployeesResult> enumerator = result.GetEnumerator();
//while( enumerator.MoveNext() )
//{
// Console.WriteLine( enumerator.Current.LastName );
//}
}
watch.Stop();
Console.WriteLine(
"Running " + count +
" SP's using LINQ took " + watch.ElapsedMilliseconds + " ms");
Console.ReadLine();
}


Here are timing results of this benchmarking test:
  • Running 10000 SP's using SQL ExecuteReader took 42806 ms

  • Running 10000 SP's using DAAB took 46443 ms

  • Running 10000 SP's using LINQ took 55136 ms




Results
So, comparing to the raw ADO.NET - DAAB is 8% slower and LINQ is 28% slower.
Comparing to DAAB - LINQ is 18% slower.
CPU usage intensity is about 2% average for raw ADO.NET vs. about 8% for DAAB vs. about 20% for LINQ - order of magnitude worse than raw ADO.NET (CPU graphs captured with perfmon below).


Fig. 1 ADO.NET - CPU usage


Fig. 2 DAAB - CPU usage



Fig. 3 LINQ - CPU usage

In overall - this performance overhead is quite disappointing. I still consider to use LINQ, as we have DAL/BLL servers in our architecture, which we may merely upgrade to faster hardware, while isolating thin-client computers from this performance overhead. I think it's a big challenge for the LINQ team to address its performance.



Tuesday, May 22, 2007

Fix DNKA 'invalid request' problem

It seems that every second post in DNKA discussion group shouts something like 'Getting "invalid request" on DNKA page. Not working!"
(If you don't know what DNKA is and how it could be usefull - read this post).
Well, the bad news is that the latest DNKA version (0.49.7) does not works with the newest GDS versions 5.x.x. The good news is that you still may get the last version of GDS that works with DNKA.
Here is how:
  1. Uninstall newer version of GDS if you have it installed.

  2.    
  3. Download GDS v 4.2006.627 from here and install it.

  4.    
  5. Optionally you may change place where your index files would reside - it's specified at HKEY_CURRENT_USER\Software\Google\Google Desktop => data_dir

  6.    
  7. Install DNKA v 0.42 from here. Go to its installation directory and backup the ws2_32.dll file.

  8.    
  9. Install DNKA v 0.49 from here above v 0.42. Replace the ws2_32.dll file with one you've saved aside during previous installation.

  10.    
  11. (All DNKA releases could be found here).

  12.    
  13. Enjoy!


Wednesday, April 11, 2007

Generics collection serializer


Here is small utility class to serialize/deserialize generics collection... My usage of it is passing of multiple parameters to a web service at once.



using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Xml.Serialization;
public class CollectionSerializer<TKey, TValue>
{
public static void Serialize(TextWriter writer, IDictionary<TKey, TValue> dictionary)
{
List<ListEntry<TKey, TValue>> entries = new List<ListEntry<TKey, TValue>>(dictionary.Count);
foreach (TKey key in dictionary.Keys)
{
entries.Add(new ListEntry<TKey, TValue>(key, dictionary[key]));
}

XmlSerializer serializer = new XmlSerializer(typeof(List<ListEntry<TKey, TValue>>));
serializer.Serialize(writer, entries);
}

public static void Deserialize(TextReader reader, IDictionary dictionary)
{
dictionary.Clear();
XmlSerializer serializer = new XmlSerializer(typeof(List<ListEntry<TKey, TValue>>));
List<ListEntry<TKey, TValue>> list = (List<ListEntry<TKey, TValue>>)serializer.Deserialize(reader);

foreach( ListEntry<TKey, TValue> entry in list )
{
dictionary[entry.Key] = entry.Value;
}
}

public class ListEntry<TKeyEntry, TValueEntry>
{
public ListEntry()
{}

public ListEntry(TKeyEntry key, TValueEntry value)
{
Key = key;
Value = value;
}

public TKeyEntry Key;
public TValueEntry Value;
}
}
Please note that standard Tuple type can't be used instead of custom ListEntry as it can't be serialized.

Thursday, March 22, 2007

Export SQL query results with BCP


Following command exports query results with the bcp into the specified text file. It could be used to get, for example, NTEXT fields that are longer than 4000 and cutted out in SQL query analyzer:
>>bcp "select * from MyTable" queryout C:\result.txt -c -S SERVERNAME -U USERNAME -P PASSWORD