Toolroom Tech Blog

Devlopers Digest

Linq: Order by Null last

How to order null elements to the bottom with Linq

Sometimes you might to order values, where Null values should go to the bottom of the result. To achieve this, just use the following:

myObjects.OrderBy(_ => _.Value == null).ThenBy(_ => _.Value);

 

Hex string to Color conversion on Windows Phone 7.x

Function to convert a hex string to a Color

In a Windows Phone 7 project I recently needed a function that does the work of System.Drawing.ColorTranslator.FromHtml(string color). Therefore I wrote this little extension method.

It deals with RGB and ARGB input values, with or without the leading hash.

public static Color FromHtml(this string s)
{
	// Check fore a valid code (RGB = 6 chars, ARGB = 8 chars)
	var regex = new Regex(@"^[A-Fa-f0-9]*$");
	if (s == null || ((s = s.Trim().TrimStart('#')).Length != 6 && s.Length != 8) || !regex.IsMatch(s))
		return Colors.White;

	int index = 0;

	// If the color length is 8, the first 2 chars contain the alpha part
	byte a = 255;
	if (s.Length == 8) 
	{
		a = Convert.ToByte(s.Substring(0, 2), 16);
		index += 2;
	}

	// Get R value
	byte r = Convert.ToByte(s.Substring(index, 2), 16);
	index += 2;
	// Get G value
	byte g = Convert.ToByte(s.Substring(index, 2), 16);
	index += 2;
	// Get B value
	byte b = Convert.ToByte(s.Substring(index, 2), 16);

	return Color.FromArgb(a, r, g, b);
}

Usage:

var color1 = "#CC3333".FromHtml();

var color2 = "CC3333".FromHtml();

var color3 = "#FFCC3333".FromHtml();

T-SQL statement for Entity Framework query

A very useful snippet that creates the T-SQL query for a linq statement.

Snippet

This very useful snippet below creates the T-SQL query from a linq statement.

/// <summary>
/// Returns the T-SQL string for a given query
/// </summary>
/// <param name="queryable">Source ObjectQuery object to get the SQL for</param>
/// <returns>T-SQL statement for the input query object</returns>
public static string GetQuery(this IQueryable queryable)
{
	if(queryable == null || !(queryable is ObjectQuery))
		throw new ArgumentException("Argument is null or no ObjectQuery.", "queryable");
	 
	return ((ObjectQuery)queryable).ToTraceString();
}

Linq to Sql: Row not found or changed

Finding a way back from LINQ to SQL cuncurrency hell.

Yesterday I was trapped in concurrency hell and had no idea why.

Background

The Windows Phone App I am currently working on, synchronizes data between a local database and a REST service. When the app receives an update for a row, I select the row to be updated from the local database via LinqToSql, update its values with the data from the service and save it.

The issue

As you can see in the snippet below, I select a record, update it ... and a few lines later the update crashes with a lovely System.Data.Linq.ChangeConflictException saying "Row not found or changed". How is that possible? I'm sure that nobody modifies or deletes the data in the meantime.

private void updatePerson(Person person)
{
	var localPerson = DataStore.Data.Persons.Where(p => p.Id == person.Id);
	if(localPerson == null)
	{
		//person is new, so create it
		DataStore.Data.Persons.InsertOnSubmit(person);
	}
	else
	{
		//update existing person
		localPerson.Name = person.Name;
		localPerson.CustomerId = person.CustomerId;
		localPerson.DepartmentId = person.DepartmentId;
		localPerson.IsActive = person.IsActive;
	}
	
	DataStore.Data.SubmitChanges(); //crash boom bang --> Row not found or changed.
}

Aha! LINQ to SQL uses Optimistic Concurrency

Hahaaa, that's it! When LINQ to SQL selects a record, it does not lock any data. When it is updating a record, it compares all columns with their original values.

So what does this mean? The update query that I expected from LINQ to SQL was:

UPDATE Persons
SET Name = 'Mr. Buggy',
	CustomerId = null,
	DepartmentId = 6,
	IsActive = true
WHERE
	Id = 9999

But the real world is different; due to optimistic concurrency it compared all columns:

UPDATE Persons
SET Name = 'Mr. Buggy',
	CustomerId = null,
	DepartmentId = 6,
	IsActive = true
WHERE
	Id = 9999 AND
	Name = 'Mr. Buggy' AND
	CustomerId = 1234 AND
	DepartmentId = 1 AND
	IsActive = true

This still does neither explain nor solve my issue, since my original values did not change in the meantime. But with the knowledge of Optimistic Concurrency and a few more minutes of debugging, I found (my) fault: The database field for CustomerId is not nullable ... but the REST Service returned null.

I fixed this and everyone (that's me) is happy!

Eigene Applikation mit Url starten

click://me

Ein Kunde hat angefragt, ob es möglich wäre, eine Feature in seine (von mir entwickelte :)) Windows-Unternehmenslösung einzubauen, mit der er Links zu Inhalten aus der Applikation per Mail verschicken kann. So wie bei Skype: Try it here! 

Natürlich geht das!

Und so geht's

Gehen wir davon aus, dass wir mit der Url myapp://user/m.muster das Benutzerprofil des Benutzers m.muster in unserer Applikation, deren ausführbare Datei unter C:\Programme\MyApplication\myapplication.exe zu finden ist, öffnen wollen. Dazu sind nur wenige Schritte notwendig:

1) Protokoll registrieren

Zuerst muss Windows das eigene Protokoll mitgeteilt werden, indem folgende Registry Keys angelegt werden:

[HKEY_CLASSES_ROOT\myapp]
@="URL:My Application Protocol"
"URL Protocol"=""

[HKEY_CLASSES_ROOT\myapp\shell]

[HKEY_CLASSES_ROOT\myapp\shell\open]

[HKEY_CLASSES_ROOT\myapp\shell\open\command]
@="\"C:\\Programme\\MyApplication\\myapplication.exe\" \"%1\""

Man kann das dann natürlich auch schön im Setup machen :)

Zu beachten ist dabei, dass der Key unter HKEY_CLASSES_ROOT der Protokollname ist (also myapp://). Der Pfad in der letzten Zeile zeigt auf die aufzurufende Datei, und %1 ist der Platzhalter für die aufgerufene Url.

2) Die Applikation vorbereiten

Jetzt muss man der App noch beibringen, mit dem Parameter umzugehen. Grundsätzlich muss man dazu lediglich die Url aus den StartupEventArgs holen und intern auswerten.

In meinem Fall ging das alles sehr schnell, da ich dank MVVM und meiner generischen Genialität (bin ich dann generial?) nur ein paar Aufrufe zu erledigen hatte. Ich schreibe die Parameter ausserdem in eine Queue, die erst dann abgearbeitet wird, sobald das MainWindow initialisiert ist, dann gibts auch keine seltsamen Fehlermeldungen. 

protected override void OnStartup(StartupEventArgs e)
{
    if (e.Args.Length > 0)
        ParamQueue.Enqueue(e.Args[0]);
}

private static void HandleUrlParameter(string url)
{
    if(!Current.Dispatcher.CheckAccess())
    {
        Current.Dispatcher.BeginInvoke(new HandleUrlParameterDelegate(HandleUrlParameter), url);
        return;
    }

    if(!url.StartsWith(string.Format("{0}://", UrlProtocol.ProtocolKey)))
    {
        return;
    }

    url = url.Replace(string.Format("{0}://", UrlProtocol.ProtocolKey), "");

    var data = url.Replace(string.Format("{0}://", UrlProtocol.ProtocolKey), "");
    var parts = data.Split(new []{"/"}, StringSplitOptions.RemoveEmptyEntries);

    try
    {
        switch (parts.Length)
        {
            case 1:
                if (parts[0] == "user") { LoadListView<UserListViewModel, User>(); return;}
                if (parts[0] == "customer") { LoadListView<CustomerListViewModel, Customer>(); return; }
                break;
            case 2:
                if (parts[0] == "user") { LoadEditForm(typeof(UserViewModel), typeof(User), new User { Id = Convert.ToInt64(parts[1]) }); return; }
                if (parts[0] == "customer") { LoadEditForm(typeof(CustomerViewModel), typeof(Customer), new Customer { Id = Convert.ToInt64(parts[1]) }); return; }
                break;
        }
    }
    catch (Exception ex)
    {
                
    }

    MessageBox.Show("Invalid Link");
}

 In diesem Beispiel wird zur Vereinfachung bei jedem Aufruf eine neue Instanz der Applikation gestartet. Um bestehende Instanzen zu verwenden, muss man dann noch die Verwendung einer SingleInstance implementieren. Aber das ist eine andere Geschichte ...

LINQes Ding

LINQ debuggen

Es soll ja vorkommen, dass sich unerklärbare Exceptions in Linq Statements einschummeln.

from contact in Contacts
    where contact != null && !string.IsNullOrEmpty(contact.DisplayName)
    group contact by contact.DisplayName[0]
    into c
    orderby c.Key
    select new Group<Contact>(c.Key.ToString(), c);

Das Helferlein:

Um das Debugging etwas zu erleichtern, ändern wir die Query in die (von mir bevorzugte :)) Method Chain Notation. Jetzt können wir ein simples Select einbauen und den Breakpoint reinsetzen und voila ... wir haben Zugriff auf die Items.

Contacts.Where(contact => contact != null && !string.IsNullOrEmpty(contact.DisplayName))
.Select(item =>
            {
                return item;
            })
.GroupBy(contact => contact.DisplayName[0])
.OrderBy(c => c.Key)
.Select(c => new Group<Contact>(c.Key.ToString(), c));