Entity Framework 4.0 et SQL Server CE

11. juin 2010

Je viens de faire le portage de UrzaGatherer depuis SQL Server 2008 R2 Express vers SQL Server CE 3.5 SP2.

Plusieurs choses sont bonnes à savoir pour que ce genre de portage se passe bien:

Le provider SQL Server CE pour Entity Framework 4.0 ne supporte pas la génération automatique des clefs il faut donc se faire une petite requête pour les générer:

Card card = new Card();
card.ID = (from c in Tools.Entities.Cards select c.ID).Max() + 1;

 

Les requêtes LINQ avec des jointures sont très mal supportées. Par exemple, ce genre de requêtes est extrêmement lent:

 var query = from card in Tools.Entities.Cards
     join blk in Tools.Entities.Blocks on card.Expansion.BlockID equals blk.ID
     where (expansionID == 0 || card.ExpansionID == expansionID) && 
(blockID == 0 || blk.ID == blockID) select card.Expansion.Name; expansionsCount = query.Distinct().Count();

La version suivante donne le même résultat mais avec un gain de performances de l’ordre de 20:

var query = from card in Tools.Entities.Cards                            
            where (expansionID == 0 || card.ExpansionID == expansionID) 
&& (blockID == 0 || card.Expansion.BlockID == blockID) select card.ExpansionID; expansionsCount = query.Distinct().Count();

Les comparaisons de chaines sont un peu hasardeuses. Par exemple le code suivant provoque un bug:

 from card in expansion.Cards
 where
    (card.Color.Name == filterColor || filterColor == " ")                     
 select card;

Le provider dans ce cas la se prend un peu les pieds dans le tapis et considère que la chaine filterColor doit faire une longueur de 1 (à cause du “ “). Il faut donc évaluer au préalable la valeur de test:

 bool enableColorFilter = filterColor != " ";

 return from card in expansion.Cards
            where
                   (card.Color.Name == filterColor || !enableColorFilter)
            select card;

Il faut aussi tenir compte du fait que le Skip() et le Take() ne sont pas gérés.

Pour le reste, le provider s’en sort très bien et le résultat est excellent.

Bookmark and Share

.Net

Le truc à la con du jour : Lancement de plusieurs fenêtres en série dans WPF

4. juin 2010

Un truc bien débile qui m’est arrivé aujourd’hui. Au sein d’une application WPF qui poutre (http://urzagatherer.codeplex.com), je voulais, lors du lancement ouvrir une première fenêtre avant la fenêtre principale.

Le code ressemble donc à ça dans le constructeur de mon App:

            InstallDatabaseWindow databaseWindow = new InstallDatabaseWindow();
            databaseWindow.ShowDialog();

            MainWindow mainWindow = new MainWindow();
            mainWindow.Show();

Rien de bien formidable me direz-vous? Et bien si vous faites un test, vous verrez que le Show sur votre mainWindow.Show() plantera généreusement avec le message suivant:

“Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.”

Bon, au premier abord, je me suis dit que j’ai du merdé dans mon constructeur.

En fait, pas du tout (je me disais aussi que ce n’était pas possible) l’explication est plus sioux : Par défaut, une application WPF possède une propriété ShutdownMode qui est par défaut réglée sur OnLastWindowClose. En gros dès qu’il n’y a plus de fenêtres en vie, l’application va se fermer gentillement.

De ce fait, dans le cas du lancement de plusieurs fenêtre en série, dès que la première se ferme, l’application vérifie sa propriété ShutdownMode et se retrouve à se fermer puisque la fenêtre suivante n’a pas encore été instanciée! Ce qui fait que lorsque l’on va faire le Show() suivant, comme l’application est en cours de fermeture, la fenêtre va recevoir un Close et donc ne pourra pas s’ouvrir puisqu’elle a déjà été fermée :)

Deux solutions:

  • Instancier ses fenêtres toutes en simultanée avant de faire apparaitre la première
  • Mettre l’application sur le mode ShutdownMode.OnExplicitShutdown et s’abonner à l’événement Closed de la dernière fenêtre pour appeler la méthode Shutdown() sur l’application

 

Et le tour est joué…

Bookmark and Share

.Net, WPF

De l’utilisation intelligente d’Entity Framework 4.0

31. mai 2010

Un framework a beau être super puissant, il ne peut pas empêcher l'a mauvaise utilisation. Il peut tenter par de nombreux moyens de la réduire mais il ne peut l’empêcher.

Du moins pas sans se brider par la suite.

J’en veux pour preuve l’utilisation d’Entity Framework 4.0. Ce framework permet de manière extrêmement élégante de requêter une source de données. Et, bien utilisé, il permet aussi d’être très performant.

Voici un exemple issu d’UrzaGatherer: Mon modèle possède une collection de Card qui, entre autres, contient une propriété Check qui permet de définir si l’utilisateur détient complètement la carte en question. Dans le cadre de la treeview qui affiche les collections de cartes, je voulais modifier le texte de chaque collection en y ajoutant le nombre de cartes manquantes le cas échéant.

Pour se faire et comme tout est issu du binding dans UrzaGatherer, j’ai développé un ValueConverter utilisé ainsi:

<TextBlock Text="{Binding Converter={StaticResource CountConverter}, Mode=OneWay
, IsAsync=True}"
/>

Le code du converter était le suivant:

    public class CountConverter : IValueConverter
    {
    public object Convert(object value, Type targetType, object parameter, 
CultureInfo culture) { Expansion expansion = (Expansion) value; var query = from card in MainWindow.Entities.Cards where card.ExpansionID == expansion.ID select card; int check = 0; List<Card> cards = query.ToList(); int count = cards.Count; foreach (Card card in cards) { if (card.Check) check++; } int missing = count - check; if (missing == 0) return string.Format("- {0} cards", count); return string.Format("({0}/{1} - Missing : {2})", check, count, missing); } public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture) { return value; } }

Dans ce converter, on voit donc une requête LINQ sur les cartes en question, puis une transformation en liste pour faire notre comptage.

Et c’est là qu’apparait la mauvaise utilisation. En effet, la conversion en liste va effectivement faire la requête sur la base de données et créer les entités en mémoire. Ces entités qui peuvent être lourdes (c’est le cas ici ou chaque carte porte plusieurs images).

Tout ça pour juste obtenir un comptage.

Il faut garder à l’esprit que EF4 est efficace tant qu’on reste dans le monde des expressions LINQ, c’est à dire tant qu’on reste au final dans le monde SQL.

Ici par exemple, la solution pour être bien plus efficace est simple : Il ne faut pas utiliser de listes locales mais tout traiter en LINQ:

public object Convert(object value, Type targetType, object parameter, 
CultureInfo culture) { Expansion expansion = (Expansion) value; var query = from card in MainWindow.Entities.Cards where card.ExpansionID == expansion.ID select card; int check = query.Count(c => c.Check); int count = query.Count(); int missing = count - check; if (missing == 0) return string.Format("- {0} cards", count); return string.Format("({0}/{1} - Missing : {2})", check, count, missing); }

La différence est subtile mais ici tout se passera sur SQL Server. Aucune entité ne sera créée ni ramenée côté client. Les méthodes Count() issues de LINQ vont générer des requêtes efficaces côté serveur contrairement à la méthode ToList() qui va faire un bon gros SELECT des familles pour tout reconstruire en mémoire.

Donc en conclusion et en ce qui concerne Entity Framework 4.0 : Retardez toujours au plus tard la récupération des entités!!

Bookmark and Share

.Net, WPF ,

UrzaGatherer

31. mai 2010

Parmi mes nombreux vices, j’ai notamment le plaisir d’être collectionneur de cartes Magic.

Or cette collection est extrêmement riche et complexe à gérer. J’ai donc développé en WPF4.0, une application de gestion associée.

Elle utilise SQL Server 2008 R2 express pour stocker les dizaines de milliers d’images. Le système s’appuie sur Entity Framework 4.0 (qui d’ailleurs est une tuerie).

Je mettrai dans quelques temps le code source de l’application sur Codeplex.

image image

image image

Bookmark and Share

WPF, .Net, UrzaGatherer

Nova4Phone Preview 2

8. mai 2010

image

Nova4Phone avance bien puisque les animations (positions, rotations, chemins, scaling) sont désormais gérées.

De plus le système d’actions de Nova est pris en compte: on peut donc toucher des objets pour déclencher des interactions.

Une petite vidéo pour illustrer tout ça.

 

 

  

 

Bookmark and Share

.Net, Nova, Windows Phone

Et ca continue…

3. mai 2010

image

 

Allez hop nouvelle version avec:

- Double texturation
- Culling (Back face et frustrum)
- Gestion des lightmaps
- Export direct depuis Nova

Prochaine étape:

- Collisions
- Actions

 

Ce qui est dommage c’est que je reprends le taf demain, ca va fortement réduire ma productivité :(

Par contre, au passage, XNA 4.0 ne gérera pas les shaders sur Windows Phone dans cette release. C’est fort dommage, ouh la oui!

Allez hop, petite vidéo pour voir le truc tourner en live.

Bookmark and Share

.Net, Nova, Windows Phone

Le truc à la con du jour : “Method Implementation is empty”

22. avril 2010

Après avoir passé plus de deux heures avec deux collègues sur ce bug, je me devais d’en parler ici.

Donc pour la petite histoire, lors du lancement d’une application, nous recevions ce message de la part du framework qui indiquait clairement qu’une méthode n’avait pas d’implémentation. Cette méthode est portée par une assembly C# tout à fait standard.

Hop, un coup de Reflector nous permet de valider que la méthode existe bien et que du code y est bien présent.

Je vous la fait courte : Cette méthode référençait une assembly C++/CLI qui était compilée en x86 alors que le code appelant était en C# compilé en “Any CPU”. Or, ce code s’exécutait sur une machine 64 bits, donc le code C# était exécuté dans un process 64 bits. Toutefois le class loader au moment de jitter la méthode incriminée se retrouvait en train de charger une assembly qui n’etait donc pas dans le bon format (x64 vs x86) et de ce fait n’arrivait pas à fournir une implémentation.

 

Donc voila, pour tous ceux qui tomberont sur cette erreur…

Bookmark and Share

.Net

Le truc à la con du jour : Problème de signature et de manifeste d’une application C++/CLI sous Visual Studio 2010

18. avril 2010

Je viens de basculer plusieurs projets Visual Studio C++ vers la version 2010. Hors alors que tout se passait bien j’ai pris un vilain warning du compilateur :
“mt.exe : general warning 810100b3: is a strong-name signed assembly and embedding a manifest invalidates the signature. You will need to re-sign this file to make it a valid assembly”

Oui je sais c’est bien moche comme on aime.

Alors, bon selon moi c’est un bug mais soyons sympas, le message est quand même clair. En effet, j’ai demandé à VS2010 de signer mon assembly mais il semblerait que le Manifest Tool vienne faire sauter cette signature en injectant le manifeste. Soit, je suis pas contrariant alors que si je voulais faire chier je pourrais dire que si les compilateurs de VS 2010 ne sont pas foutus de bosser ensemble c’est un peu la honte. Mais je ne suis pas comme ça. Ouh la non…

Au final, il faut donc se faire une petite post build event à l’ancienne comme on aime avec des petits bouts de “je te la bidouille” dedans.

Comme je suis sympa voila la commande en question: sn -Ra "$(TargetPath)" $(ProjectName).snk

Bookmark and Share

Visual Studio, .Net

Le truc à la con du jour : Sélectionner un item dans une treeview WPF

11. avril 2010

C’est ballot mais contrairement à Windows Forms, il n’y a pas un support direct de la sélection d’un item dans une treeview WPF.

Toutefois, la solution est relativement simple, puisqu’elle passe par la fonctionnalité des treeviews qui permet de récupérer les TreeViewItems depuis les objets liés : ItemContainerGenerator.

Ainsi, en respectant la hierarchie de la treeview on peut parcourir l’arbre à la recherche du container de l’item.

Cela donne donc le code suivant:

public static bool SetSelected(ItemsControl parent, object child)
{
    if (parent == null || child == null)
    {
        return false;
    }

    TreeViewItem childNode = parent.ItemContainerGenerator.ContainerFromItem(child) as TreeViewItem;

    if (childNode != null)
    {
        childNode.Focus();
        return childNode.IsSelected = true;
    }

    if (parent.Items.Count > 0)
    {
        foreach (object childItem in parent.Items)
        {
            ItemsControl childControl = parent.ItemContainerGenerator.ContainerFromItem(childItem) 
as ItemsControl; if (SetSelected(childControl, child)) { return true; } } } return false; }

Et le tour est joué.

Bookmark and Share

.Net, WPF

On remet le couvert!!!

9. avril 2010

Après une BDC mémorable à Toulouse, on remet ça sur Aix le 18 Mai.

N’hésitez pas à vous inscrire…

Au menu : Du Sharepoint 2010, du Visual Studio 2010, du Silverlight 4, de l’ergonomie, du Windows Phone 7 et bien d’autres choses encore…

clip_image001

Bookmark and Share

.Net, Divers