Entity Framework: Hoofdlettergevoeligheid in SQL
Entity Framework is een mapper waarmee je objecten in je code kunt mappen met je database. Dit is vaak MS-SQL, maar kan ook een andere database zijn, zoals MySql of SqLite. Entity Framework zorgt ervoor dat je je bezig kunt houden met je applicatie, terwijl de database-koppeling geregeld wordt. Maar het blijft belangrijk om te beseffen welke databaseserver gebruikt wordt, omdat er soms database-specifieke regels kunnen gelden.
Een voorbeeld:
Er is een tabel Pages bestaande PageId, Id en Name:
Via Entity Framework wil ik nu een aantal paginaâs ophalen op basis van hun naam:
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("c4586f8d-c347-43e4-983b-c7e4788abc57", "x");
dict.Add("1CDA085F-1539-47D0-B54B-2AF7134C44DB", "y");
dict.Add("7c8c0c6b-89dc-4f4f-b61c-5a0613e6be19", "z");
var keys = dict.Keys;
var pagesContains = context.Pages.Where(x => keys.Contains(x.Name)).ToList();
Deze drie staan in de database en krijg ik inderdaad terug. Ik maak gebruik van een Dictionary, omdat ik iets wil doen met de waardes 'x','y' en 'z', voor de betreffende paginaâs, bijvoorbeeld de Id aanpassen.
Ik loop door de gevonden paginaâs, om die bewerking uit te voeren, en krijg een foutmelding, omdat een bepaald item niet is gevonden:
Dat is natuurlijk opmerkelijk, want zojuist heb ik ze wél kunnen ophalen uit de database op basis van de Name. Wat is hier aan de hand? In dit geval maakt Entity Framework een query die wordt uitgevoerd op de SQL-server. De database, in dit geval, en de query is echter case-insensitive, er wordt niet gelet op een verschil in kleine letters of hoofdletters.
De key van een Dictionary in C# daarentegen is wél hoofdlettergevoelig en vindt een bepaald object in de collectie niet.
Hoe dit op te lossen?
Collation toepassen op je query
Je kan dit oplossen door in he query COLLATE te gebruiken. Hiermee geef je aan dat de query gebruik moet maken van andere regels voor karakters.
select * from Pages where Name = '86B2D64A-83BE-4D21-A246-C32DE5165B85';
select * from Pages where Name = lower('86B2D64A-83BE-4D21-A246-C32DE5165B85') ;
select * from Pages where Name = '86B2D64A-83BE-4D21-A246-C32DE5165B85' COLLATE Latin1_General_CS_AS ;
select * from Pages where Name = lower('86B2D64A-83BE-4D21-A246-C32DE5165B85') COLLATE Latin1_General_CS_AS ;
Dit houdt echter in dat je wel gebruik moet maken van een custom query in je C#-code: Entity Framework zelf biedt de mogelijkheid niet om de collation aan te passen.
ToLower of ToUpper gebruiken
Een tweede oplossing is om in C# de keys altijd om te zetten naar kleine letters of hoofdletters.
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("c4586f8d-c347-43e4-983b-c7e4788abc57".ToLower(), "x");
dict.Add("1CDA085F-1539-47D0-B54B-2AF7134C44DB".ToLower(), "y");
dict.Add("7c8c0c6b-89dc-4f4f-b61c-5a0613e6be19".ToLower(), "z");
var keys = dict.Keys;
var pagesContains = context.Pages.Where(x => keys.Contains(x.Name)).ToList();
// pagesContains.Count == 3
foreach (var page in pagesContains)
{
var dictEntry = dict[page.Name.ToLower()];
}
Kortom, hoewel Entity Framework zoveel mogelijk werk van je overneemt, is het raadzaam om te weten welke techniek erachter hangt en welke eigenaardigheden die techniek met zich meebrengt.