Categories

My coding performance

From 24/08/2011 till 17/11/2011 (that is 85 days) doing a full-time job, I’ve produced 203,932 bytes of C# code (*.cs files not including designers and other auto-generated code). So my performance is roughly 2399 bytes of code per day.
It is 5400 lines of code, making 63.5 LOC/day. Average line length in characters is 37.7 (including tabs and \r\n).
I belive it is modest result but I felt very busy working theese days.

C# void arrays are possible

C# compiler rejects System.Void, void variables declaration and void type parameter but allows typeof(void). In my experiments I finished with working List<void[]>:

using System;
using System.Collections.Generic;
 
class Program
{
	static void Main(string[] args)
	{
		Type voidType = typeof(void);
		Type voidArray = voidType.MakeArrayType();
		Type listOfVoidArrays = typeof(List<>).MakeGenericType(voidArray);
		var v = Activator.CreateInstance(listOfVoidArrays);
		dynamic w = v;
		w.Add(null);
		Console.WriteLine(w.Count);
		Console.WriteLine(v.GetType().FullName);		
	}
}

I do not know the reason why typeof(void).MakeArrayType() is allowed, I suppose it’s a design bug.
It would be fun to obtain void variable in C#.

Entity Framework 4.0 Mass Operations

I’ve made it! My project named Entity Framework 4.0 Mass Operations is available at codeplex.
It adds support for mass data manipulation operations by means of LINQ-to-entities.
What? INSERT+SELECT (mass insert), UPDATE FROM (mass update), DELETE WHERE EXISTS (mass delete) are available from C# with EF 4.0
Why? Because I love static typing. I think that being bound to database structure by compiler is much better than bookkeeping with stringly typed column and table names. Just do not forget to update edmx.
Where do I get it? Grab the source code at project’s page.
How do I use it? See documentation.
How does it work? Basically the library uses ToTraceString and ObjectParameters collection of LINQ query to build a string with SQL query and populate DbCommand’s parameters. The hardest part is property-to-column mappings discovery of LINQ and entity objects.
Are there any restrictions? Many of them. I’ll write a special post concerning restrictions and implications.
Is it tested? Not much. It works with EF 4.0 and PostgreSQL, should work with SQL Server too. You can post suggestions and reports at project page.
The following resources helped me:

  1. Meta-Me blog – Rolling your own SQL Update on-top of the Entity Framework series: Part 1, Part 2, Part 3 and Part 4.
  2. Stefan Cruysberghs – .NET – ADO.NET Entity Framework : Querying metadata
  3. A forum thread – Dynamically Obtain Mapping Info
  4. My own stackoverflow question :)How does Entity Framework manage mapping query result to anonymous type?

VCVarsApply – a more convenient way to make Visual Studio environment variables persistent

Do you prefer typing small code in text editor and compiling from command line instead of launching Visual Studio? Do you use cl.exe, csc.exe, vbc.exe or other Microsoft command-line compilers? Do you feel tired of launching Visual Studio command prompt every time you need your compiler? Or are you bored with adjusting system environment variables every time you reinstall Windows?
Would you like visual studio environment (which is set by vcvarsall.bat) available everywhere, e.g. cmd.exe, far manager?
If yes is your answer, then this script is right for you!
It adjusts INCLUDE, LIB, LIBPATH, PATH and other useful environment variables per-system registry key for all users.
Instructions:

  1. Download it here: VCVarsApply and extract
  2. Run VCVarsApply.wsf as administrator, it’s safe for your computer and your data, I promise
  3. Locate vcvarsall.bat file in Visual Studio\VC folder which sets Visual Studio environment and copy it’s location in the text prompt. There’s a default for VS 2010 on x64.



    Surround full path with double quotes “. Also you can pass parameters for the script. If in doubt, do not add them.
  4. Then the script does it’s magic and you get prompt like this one.



    Once you press Yes, listed changes are applied to registry. Don’t forget to log off and on to feel changes.

Script idea is simple:

  1. Run ‘cmd.exe /c set’ command and read it’s output. That’s how current unmodified environment is obtained.
  2. Run ‘cmd.exe /c “vcvarsall.bat” && set’ to read modified environment.
  3. Compare results of two commands to filter unchanged and user values.
  4. Write modified values in registry [HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment]

If you want to look at the source first, here it is.

<?xml version="1.0"?>
<job id="">
<script language="VBScript">
	<![CDATA[	
		rem The solution to make InputBox in JScript, many thanks to http://wsh2.uw.hu/ch08c.html
		Function WSHInputBox(Message, Title, Value)
			WSHInputBox = InputBox(Message,Title,Value)
		End Function
	]]>
</script>
 
<script language="JScript">
	<![CDATA[
		var wait_read=function(exec) {
			while (exec.Status == 0)
				WScript.Sleep(100);
			var input = '';
			while (!exec.StdOut.AtEndOfStream) {
				var l = exec.StdOut.ReadLine();
				input += l + '\n';
			}
			return input;
		};
 
		var read=function(stream) {
			input = "";
			var go = true;
			while (go) {
				var l = stream.ReadLine();
				input += l + '\n';
				if (!l) go=false;
			}
			return input;
		};
 
		var parse_vars=function(str) {
			sa = str.split('\n');
			res = {};
			for (var j=0; j<sa.length; ++j) {
				var s = sa[j];
				var i = s.indexOf('=');
				if (i>=0) {
					var key = s.substr(0, i);
					var vs = s.substr(i+1).split(';');
					var vvs = [];
					for (var k = 0; k<vs.length; ++k)
						if (vs[k])
							vvs[vvs.length]=vs[k];
					res[key]=vvs;
				}
			}
			return res;
		};
 
		var diff = function(vars1, vars2) {
			var res = {};
			for (var key2 in vars2) {
				if (!vars1[key2]) {
					res[key2]=vars2[key2];
				}
				else {
					var a2 = vars2[key2];
					var a1 = vars1[key2];
					var i = 0;
					for (; i < a2.length; ++i) {				
						var j = 0;
						for (;j<a1.length;++j) {
							if (a1[j]==a2[i])
								break;
						}
						if (j==a1.length)
							break;
					}
					if (i<a2.length)
						res[key2] = a2;
				}
			}
			return res;
		};
 
		var pr=function(s) {
			var res = '';
			for (var k in s) {
				res+=k+'='+s[k].join(';')+'\n';
			}
			return res;
		};
 
		var havePath = function(vals) {
			for (var i=0; i<vals.length; ++i) {
				if (vals[i].indexOf(':\\')==1)
					return true;
			}
			return false;
		};
 
		var write_reg2 = function(kv, shell) {
			for (var key in kv) {
				var vals = kv[key];
				shell.RegWrite(reg_key+'\\'+key, vals.join(';'), (havePath(vals)?'REG_EXPAND_SZ':'REG_SZ'));
			}
		};
 
		var vcvars = '"C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\vcvarsall.bat" x64';
		vcvars = WSHInputBox("Please, input path to vcvarsall.bat file and do not forget double quotes around it. "
			+ "If you have to define some options like x86 and x64 put them here to.",
			"Input path to vcvarsall.bat",vcvars);
		var shell = WScript.CreateObject("WScript.Shell");
		var cmd_exe = shell.ExpandEnvironmentStrings('%comspec%');
		var exec = shell.Exec(cmd_exe+' /c set');
		var res1 = read(exec.StdOut);
		res1 += wait_read(exec);
 
		exec = shell.Exec(cmd_exe+' /c "'+vcvars+'" && set');
		var res2 = read(exec.StdOut);
		res2 += wait_read(exec);
		if (exec.ExitCode!=0) {
			shell.Popup('vcvarsall.bat failed with nozero exit code. Please make sure, you specified right file',0,'Error',0x10);
			WScript.Quit(exec.ExitCode);
		}
 
		// parse environment values and calculate difference
		var p1 = parse_vars(res1);
		var p2 = parse_vars(res2);
		var difference = diff(p1,p2);
 
		var reg_filename='environment';
		var reg_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment';
 
		// backup registry
		var dt = new Date();
		var backup_filename = reg_filename+ '_backup_'
			+dt.getFullYear()+'-'+(dt.getMonth()+1)+'-'+dt.getDate()+'_'+dt.getHours()+'-'+dt.getMinutes()+'-'+dt.getSeconds()
			+'.reg';
		var backup_command = 'regedit.exe /e "' + backup_filename + '" "' + reg_key + '"';
		var backup_res = shell.Run(backup_command,1,true);
 
		// create prompt
		var prompt =
			'Old registry values ' +((backup_res==0)?'were':'WERE NOT')+ ' backed up in file ' + backup_filename + '.\n'
			+ 'To merge new values in the registry press Yes.\n'
			+ 'The following information will be added to the registry key\n' + reg_key + ':\n\n'
			+ pr(difference);
		var decision = shell.Popup(prompt,0,"Apply new Environment values?",0x4|0x20);
 
		// apply changes
		if (decision==6) {
			write_reg2(difference, shell);
			shell.Popup('Changes seem to be applied don\'t forget to log off and on',0,'Changes applied',0x40);
		}
	]]>
</script>
</job>

Coding mania

It’s been a while since I’ve made my last post. There are always things I’d like to share but I don’t have enough motivation yet. Now it feels like I’m under influence of coding mania trying to improve every little bit of my every day computer job.
Yesterday I felt frustrated after typing cl and csc on command line and getting unrecognized command result. An argument against my promise not to improve MS software was that I have to reflect changes from vcvarsall.bat to system environment pretty often. Usually I change environment variables with Rapid Environment Editor, pretty and freeware program. But it lacks for per-process environment support taking all values from registry instead.
So I decided to write my own tool to make visual studio environment variables persistent. Welcome VCVarsApply.wsf

Videocard overpayment bookmarklet for nix.ru

Sorry, this entry is only available in Русский.

Why do developers prefer Entity Framework over other ORM systems?

Sorry, this entry is only available in Русский.

Another sad story: COM interop in C# 2010

As usual in this life words do not always correspond with actions. There are some speakers whose words almost never correspond to actions. So if somebody says “we are doing things odd because we would like to save backwards compatibility” wait to see things in action.
One silent revolution happened in C# 2010 when they introduced Dynamic Language Runtime wich also changed COM Interop behavior. As it is said in MSDN, “Visual C# 2010 includes several features that improve the experience of interoperating with COM APIs such as the Office Automation APIs.” Specifically, there is no assembly information and type information, because objects now of type System.__ComObject.
Well, there’s an option to still try to convert type library to .NET assembly with tlbimp.exe statically. Or using System.Runtime.InteropServices.TypeLibConverter.ConvertTypeLibToAssembly dynamically but hey have you tried it? The method ConvertTypeLibToAssembly expects ITypeLibImporterNotifySink.ResolveRef to be implemented in order to resolve references of the type library. When run against MSWORD.OLB type library, ResolveRef is called by runtime and an instance of __ComObject is passed as parameter to it. Why not to resolve reference?
Take a time and open tlbimp.exe with reflector. Classes are internal – feel like you are forbidden to reuse their code. TlbImpCode.ImporterCallback.ResolfeRef – 169 lines of disassemblied code. Uneasy to make yor own.

Question sign in Django urls

Why are URLs like “http://mysite.com/something/details?id=506” not parsed by urls.py file?
Actually these URLs are parsed but everything after the question sign (which is “?id=506”) is not matched against the regular expression. Why? Because this is a GET request string.
It took a while for me to realize this, while I was looking at this message.
So, when you request URL “http://mysite.com/something/details?id=506” in your browser django which is servicing mysite.com splits the resource location “something/details?id=506” into two parts. First it considers everything after the question sign as GET parameters (“id=506”) parsing them to GET dictionary of the request object. Second it passes the resource location without GET parameters (“something/details”) to the URL resolution mechanism.
Rephrasing Jarek Zgoda: Django does not match GET parameters by url patterns, it processes GET parameters independently. In order to access parameters after “?” sign, use the GET dictionary in your view.

def my_view(request):
	id = request.GET['id']
	...

And leave url pattern like there’s nothing after ‘details’

(r'^details$', 'myapp.views.my_view')

My crush on X64 Oracle client and SSIS

The root of all problems with Oracle connectivity on x64 windows is ADO.NET Oracle driver (provider). There are two version of ODAC (Oracle data access components) x64 and x86 and both bring ado.net provider built for specific processor architecture.
Oracle provider assemblies in GAC
SQL Server Business Intelligence Development Studio 2005 and 2008 are x86 applications. When developing SSIS application, x86 driver is used (since x86 and x64 modes cannot be mixed).
There’s a problem with Oracle drivers and applications having braces in path like “Program Files (x86)”. And there’s a nice solution (a copy here) based on this history.

rem Business Intelligence Development Studio 2005
start /B "C:\Progra~2\Microsoft Visual Studio 8\Common7\IDE" "C:\Progra~2\Microsoft Visual Studio 8\Common7\IDE\devenv.exe"
rem Business Intelligence Development Studio 2008
start /B "C:\Progra~2\Microsoft Visual Studio 9.0\Common7\IDE" "C:\Progra~2\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe"
rem SQL Server Management Studio 2005
start /B "C:\Progra~2\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE" "C:\Progra~2\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\SqlWb.exe"
rem SQL Server Management Studio 2008
start /B "C:\Progra~2\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE" "C:\Progra~2\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe"

Having these bat files for launching BI studio and management studio 2005 and 2008 I’ve managed to develop and run SSIS package. There is only x64 driver installed on the server so I had to change Oracle driver assembly string in dtsx file with notepad in order to deploy it. That wokred fine. Later I decided to write a program which would switch provider string for me. I did it, but there’s a problem. Dtsx file loaded and saved with XmlDocument no longer works. Try this:

using System;
using System.Xml;
class App
{
	static void Main(string[] args)
	{
		XmlDocument doc=new XmlDocument();
		doc.Load(args[0]);
		doc.Save(args[1]);
	}
}
> csc test.cs
> test input.dtsx output.dtsx
> xmldiff input.dtsx output.dtsx
Comparing input.dtsx to output.dtsx
Files are identical.

The funny things is changing string with notepad works but changing with Xml api does not. When input is a file with sensitive data encrypted with the password, the output does not work and complains

The package failed to load due to error 0xC0010014 "One or more error occurred. There should be more specific errors preceding this one that explains the details of the errors. This message is used as a return value from functions that encounter errors.". This occurs when CPackage::LoadFromXML fails.

There are lots of forum posts and discussions upon LoadFromXML issues. So the next step is to find the solution.
But this is it for me. No more wasting my time looking for solutions for Microsoft or Oracle problems. They are earning billions and can help themselves. And help thier users too.
Oh, and there’s my program to change Oracle driver in dtsx if anybody needs it. It’s based on GAC API
Warning: it does produce dtsx files that do not work. You’ve been warned. :)