Monthly Archives: July 2012

Easier ArgumentException messages

It’s a good practice to include parameter name when throwing ArgumentException (or similar). But I hate typing the parameter name as string. It’s A) boring and B) dumb, because when you refactor the parameter name you have to manually also change this string.

But why not let the language constructs help us? 8-) If we capture the parameter as expression we’ll be able to use static typing and extract the name from the expression node.

Shouldn’t be that hard, I thought one afternoon, and here’s the result (it really isn’t).

static string ArgumentExceptionMessage<T>(Expression<Func<T>> argument)
{
	var me = argument.Body as MemberExpression;
	if (me == null)
		throw new ArgumentException("Cannot extract argument name.", ArgumentExceptionMessage(() => argument));
	return me.Member.Name;
}

And you can use it (normally you would throw exception in all cases, I just wanted to show the result, hence Console.WriteLine).

class Foo
{
	public int MyProperty { get; set; }
}

static void Main(string[] args)
{
	var f = new Foo();
	Console.WriteLine(ArgumentExceptionMessage(() => args));
	Console.WriteLine(ArgumentExceptionMessage(() => f.MyProperty));

	throw new ArgumentException("This argument is not correct.", ArgumentExceptionMessage(() => args));
}

There’s never enough precondition checks. And understandable messages/parameters of exceptions is a valued help for developers. You can even wrap it to method returning ArgumentException directly to have more succinct code.

Firebird’s DDEX provider in Visual Studio 2012 (RC)

As with every new Visual Studio, I’m trying the DDEX provider for Firebird with it. The version 2010 was piece of cake. The VS 2012 (RC in time of writing) was little bit of trial and error. But I’m not a quitter! Anyway, the good news is, it works. No magic changes needed. Only some tweaking of registry file. Rest is same as in ReadMe.txt. These will be available with next build (and of course in SVN) of provider, probably when VS 2012 RTM will be released.

Some images to turn you on. :)

Jak (ne)zrušit službu u VSHosting – part deux

Když jsem psal předchozí příspěvek čekal jsem jestli se něco stane. A stalo. Ne, problém se nevyřešil, ale celá věc dostala zajímavý spád. Řekl bych, že z toho pěkně vybruslili.

Poté co jsem výše odkazovaný příspěvek publikoval jsem odkaz zaslal i do VSHostingu – všehovšudy je to veřejné a nemám (a i jsem to uvedl) problém s tím, pokud by někdo z VSHostingu přidal komentář – jsem dostal odpověď (překvapivě v 1:28). Údajně celou věc projednávali a chystali se mne kontaktovat. Zajímavé, že na první ani druhý email nikdo nereagoval a nyní odpověď. Popravdě jim to nežeru (počkejte na druhý zvrat níže). Předpokládejme, že opravdu celou věc opravdu projednávali. Dva týdny? Proč jsem nedostal alespoň odpověď ve stylu “Řešíme to, ozveme se později.” (připusťme např. odpovědnou osobu na dovolené)? Nevadí. Druhý zvrat přišel o několik vět později. Prý vzhledem k publikaci předchozího příspěvku o kompromisní řešení nemám zájem a tím pádem už není co dál řešit.

Musím přiznat, že tento obrat je skvělý a sám bych jej nevymyslel. Míč se přesunul na mojí stranu a finito – “mohlo to jít, ale posral sis to” jinak vzato “dohodnout jsme se stejně nechtěli, ale teď to krásně přehrajeme na tebe a jsme z toho venku”. Brilantní.

Pokud máte nějakou aplikaci, která by jeden menší server (1CPU, 1GB RAM, 30GB HDD) potřebovala, dejte mi klidně vědět.

Application using Entity Framework’s Code First to dynamically connect to two different databases

I was doing Entity Framework training roughly two weeks ago and normally I have 99% of people working with MS SQL. This however I had two guys from some company, where they worked with different databases, as the product they are working on is able to use couple of different databases. What a great setup. Finally I can show how to use Entity Framework with non-MS SQL databases.

They were interested particularly in Oracle Database, but I’m not that familiar with the 3rd party providers for Oracle Database so we agreed on using Firebird. ;) The code we wanted to try was to connect with same application (without recompile) to two different databases based on connection string (or connection object itself), without mapping hassle etc.

I chose Code First, because it’s the mapping itself is more separated from other parts than in EDMX (although the EDMX can be split to CSDL, MSL and SSDL files as well, you’ll then need to edit there manually or temporarily create EDMX file back again to use designer easily).

Let’s start with the structure. It’s basic Books – Authors example (sorry, names in Czech, but Book = Kniha, Author = Autor).
Firebird:

create table Knihy(
  ISBN varchar(20) character set ASCII primary key,
  Nazev varchar(100) character set utf8 not null,
  Cena decimal(10,4) not null,
  ID_Autor int not null
);
create table Autori(
  ID int primary key,
  Jmeno varchar(100) character set utf8 not null,
  Prijmeni varchar(100) character set utf8 not null
);
alter table Knihy add foreign key (ID_Autor) references Autori(ID);

MS SQL:

create table Knihy(
  ISBN varchar(20) primary key,
  Nazev nvarchar(100) not null,
  Cena money not null,
  ID_Autor int not null
);
create table Autori(
  ID int primary key,
  Jmeno nvarchar(100) not null,
  Prijmeni nvarchar(100) not null
);
alter table Knihy add foreign key (ID_Autor) references Autori(ID);

Now the C# code. I’ll start with classes, then basic Code First structure and finally the mapping.

Classes:

class Autor
{
	public int ID { get; set; }
	public string Jmeno { get; set; }
	public string Prijmeni { get; set; }
	public ICollection<Kniha> Knihy { get; set; }
}

class Kniha
{
	public string ISBN { get; set; }
	public string Nazev { get; set; }
	public decimal Cena { get; set; }
	public Autor Autor { get; set; }
	public int AutorID { get; set; }
}

First interesting point. The DbContext class needs to create proper mapping based on connection object used:

class MyContext : DbContext
{
	public MyContext(DbConnection connection)
		: base(connection, true)
	{ }

	protected override void OnModelCreating(DbModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);

		if (Database.Connection is FbConnection)
		{
			modelBuilder.Configurations.Add(new FirebirdAutorConfiguration());
			modelBuilder.Configurations.Add(new FirebirdKnihaConfiguration());
		}
		else if (Database.Connection is SqlConnection)
		{
			modelBuilder.Configurations.Add(new MSSQLAutorConfiguration());
			modelBuilder.Configurations.Add(new MSSQLKnihaConfiguration());
		}
		else
			throw new NotSupportedException();
	}

	public IDbSet<Autor> Autori { get; set; }
	public IDbSet<Kniha> Knihy { get; set; }
}

Looking at Database.Connection type I switch between two different configurations. So the DbContext itself isn’t dealing with the mapping.

Finally the mapping (second interesting point):

class MSSQLAutorConfiguration : EntityTypeConfiguration
{
	public MSSQLAutorConfiguration()
	{
		this.ToTable("Autori");
	}
}

class MSSQLKnihaConfiguration : EntityTypeConfiguration<Kniha>
{
	public MSSQLKnihaConfiguration()
	{
		this.HasKey(x => x.ISBN);
		this.Property(x => x.AutorID).HasColumnName("ID_Autor");
		this.HasRequired(x => x.Autor).WithMany(x => x.Knihy).HasForeignKey(x => x.AutorID).WillCascadeOnDelete(false);
		this.Map(map =>
			{
				map.Properties(x => new { x.ISBN, x.Nazev, x.Cena, x.AutorID });
				map.ToTable("Knihy");
			});
	}
}

class FirebirdAutorConfiguration : EntityTypeConfiguration<Autor>
{
	public FirebirdAutorConfiguration()
	{
		this.Property(x => x.ID).HasColumnName("ID");
		this.Property(x => x.Jmeno).HasColumnName("JMENO");
		this.Property(x => x.Prijmeni).HasColumnName("PRIJMENI");
		this.ToTable("AUTORI");
	}
}

class FirebirdKnihaConfiguration : EntityTypeConfiguration<Kniha>
{
	public FirebirdKnihaConfiguration()
	{
		this.HasKey(x => x.ISBN);
		this.Property(x => x.ISBN).HasColumnName("ISBN");
		this.Property(x => x.Nazev).HasColumnName("NAZEV");
		this.Property(x => x.Cena).HasColumnName("CENA").HasColumnType("decimal").HasPrecision(10, 4);
		this.Property(x => x.AutorID).HasColumnName("ID_AUTOR");
		this.HasRequired(x => x.Autor).WithMany(x => x.Knihy).HasForeignKey(x => x.AutorID).WillCascadeOnDelete(false);
		this.Map(map =>
		{
			map.Properties(x => new { x.ISBN, x.Nazev, x.Cena, x.AutorID });
			map.ToTable("KNIHY");
		});
	}
}

For MS SQL I took a chance to save some typing and let conventions to kick in and do some work for me. For Firebird, I had to type a little bit more. What may not be absolutely clear is fact, that the mapping classes (the ones derived from EntityTypeConfiguration<T>) can create hierarchy. It doesn’t have to be just one level deep. So if you have i.e. same column names in both databases you can use HasColumnName in some base class and use i.e. just HasColumnType and Map in derived classes used later to configure model builder.

And to test it actually works and produces expected queries and so on, simple test code.

static void Main(string[] args)
{
	Database.SetInitializer<MyContext>(null);
	var fb = new FbConnection("database=localhost:test;user=sysdba;password=masterkey");
	var mssql = new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=test;Integrated Security=True");
	Test(fb);
	Test(mssql);
}

static void Test(DbConnection conn)
{
	using (MyContext c = new MyContext(conn))
	{
		Console.WriteLine(c.Autori
			.Where(a => a.Knihy.Any(k => k.Cena > 10.0m)).ToString());
	}
}

I hope everything is clear. If you have a question, comments are here for you, I’ll try my best to answer. If you’d like to have a training, let me know.

Jak (ne)zrušit službu u VSHosting

Jak (ne)zrušit službu u VSHosting – part deux

Vetšinou se snažím psát hlavně o službách apod., u kterých jsem se setkal s výjimečně dobrým přístupem. Stežovačů je všude dost.

Ale pro tentokrát to bude změna. :) Před skoro dvěma lety jsem potřeboval pro jeden projekt menší server. Po průzkumu trhu a pár doporučeních padla volba na VSHosting. Konkrétně jejich VDS/VPS. Pro projekt to stačilo a cena byla slušná. Zaplaceno bylo rovnou na rok, proč si přidělávat administrativu. Po roce se server prodloužil, žádný problém. Nyní ale vyvstala potřeba provoz ukončit. Toliko k úvodu do “problému”.

Obrátil jsem se tedy na obchodní oddělení s žádostí o ukončení. Předpokládal jsem nějakou plus minus měsíc výpovědní lhůtu a dobropisaci zbytku (přibližně půl roku) nevyužité částky. Jaké bylo moje překvapení, když, po urgenci, jsem byl odbyt odpovědí, že službu nelze ukončit.

Zde bych se rád zastavil. Jedna se o VPS. Zrušení je otázka deseti minut a žádné “zbytky” (jako například po fyzickém dedikovaném serveru). Slušně jsem se tedy zeptal, zdali se přeci jen nemůžeme domluvit, že zbývá ještě skoro polovina předplaceného období a že jsem pripraven akceptovat nějakou, oběma stranám vyhovující, výpovědní lhůtu. Po prvním týdnu bez odpovědi, urgenci a druhém týdnu stále bez odpovědi, stále bez zrušení…

Veřím, že je toto v podmínkách podchyceno. Předpokládal bych ale, že dobra platební morálka, neporušovaní podmínek, snaha o domluvu atp. by zasloužila alespoň odpověď. U VSHosting asi ne… Jasně, prachy mají, tak co.