Jeśli korzystamy Entity Framework to możemy (dość często podejrzewam) natknąć się na pewien zagadkowy z pozoru problem pojawiający się przy rzutowaniu lub konwersji jednego typu do drugiego. Weźmy sobie taki życiowy przykład: mamy tabelę użytkowników i na naszej formatce w ASP.NET chcemy dać dropdowna z listą wszystkich użytkowników. Wbrew pozorom ten scenariusz jest całkiem prosty i intuicyjny w ASP.NET MVC ;]

Korzystając z Helpera do tworzenia Selectów potrzeba nam jedynie listy elementów do pokazania w tej liście. Możemy  bez problemu wykorzystać klasę SelectListItem tylko jak szybko przekonwertować nasze obiekty bazodanowe do docelowego typu? Można zastosować taki oto kod:

var query = from user in dbEntities.aspnet_Users 
select new SelectListItem { 
	Text = user.UserName, 
    Value = user.id_user.ToString() 
};

Jest to poprawny kod, jednakże próba uruchomienia powyższego zwróci nam dość zagadkowy błąd:

“Składnik LINQ to Entities nie może rozpoznać metody System.String ToString()” i nie można przetłumaczyć jej na wyrażenie magazynu.”

lub po angielsku:

“System.NotSupportedException: LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression”

Na ogół da się jakoś to obejść, unikając konwersji, ale w powyższym przypadku nie można inaczej. W czym tkwi problem? Kod tego zapytania jest przerabiany na zapytanie SQL, które wykonywane jest w całości po stronie serwera bazodanowego, co skutkuje pewnymi ograniczeniami – na przykład tymi związanymi z konwersjami. Jak można sobie z tym poradzić? Są 3 sposoby, które rozwiązują problem:

  • Można napisać własne zapytanie SQL i wykonać je przez Entity Framework; metoda skuteczna, jednak większość ludzi używa rozwiązań typu Entity Framework właśnie po to aby uniknąć potrzeby pisania zapytań SQL
  • W Entity Framework v2 są dostępne konwertery, których można użyć aby wykonać poprawną konwersję na serwerze. Wtedy przykładowy kod zamiast Value = user.id_user.ToString() miałby: **Value = SqlFunctions.StringConvert(user.id_user). **Wymaga to od nas jednak użycia C# w wersji 4.0 oraz Visual Studio 2010. Nie jest to na tyle duży problem by zniżać się do korzystania z najgorszej wersji Visual Studio… chyba że już się w nim pracuje – wtedy współczuję.
  • Najprostsze zaś rozwiązanie to pobranie wyniku zapytania i dopiero na pobranych danych klienckich wykonanie konwersji. Wygląda to tak:
var collection = dbEntities.aspnet_Users.ToDictionary(i => i.id_user, i => i.UserName); 
var query = from user in collection 
select new SelectListItem 
{ 
	Text = user.Value, 
    Value = user.Key.ToString() 
};

I to wszystko; ot – potencjalnie nietrywialny błąd ze względnie nietrywialnym rozwiązaniem :)