SQL Server : une contrainte conditionnelle

Mon besoin d’une contrainte conditionnelle s’était exprimé sur une contrainte de type UNIQUE, que je voulais restreindre à certaines lignes, c’est à dire les lignes pour lesquelles une condition du type MaColonne = X est satisfaite. Ce billet montre 2 manières différente de les créer.

Prenons l’exemple suivant :

CREATE TABLE Moron (
	Id INT NOT NULL,
	Name NVARCHAR(25),
	IsDeleted BIT NOT NULL,
	CONSTRAINT UQ_Moron_Name UNIQUE(Name)
)

On définit cette table comme étant les abrutis présents dans une classe.
On sait qu’il ne peut pas y avoir 2 abrutis avec le même nom dans une classe, on tape donc dans le marbre la contrainte UQ_Moron_Name  pour que cette valeur de vérité soit toujours vérifiée.

Supposons maintenant qu’un abruti Aurélien s’en aille, on va le marquer IsDeleted = 1 .
Supposons ensuite, qu’un autre abruti arrive en classe et qu’il s’appelle aussi Aurélien. Boum, on ne peut pas l’insérer dans la table à cause de la contrainte d’intégrité.
L’idéal serait donc de modifier cette contrainte (ça serait vraiment dommage de la supprimer, non ?), et ainsi prendre en compte cette nouvelle réalité, à savoir que ce qui nous intéresse est : il ne peut pas y avoir 2 abrutis avec le même nom dans une classe, c’est à dire que c’est la contrainte précédente mais uniquement lorsque IsDeleted = 0 .

Et donc ?

Il faut trouver le moyen de réaliser une contrainte conditionnelle, et deux moyens au moins s’offrent à nous :

La vue

On peut créer une vue telle que définie:

CREATE View ActiveMoron
AS
	SELECT Name
	FROM Moron
	WHERE IsDeleted = 0
GO
CREATE UNIQUE CLUSTERED INDEX IDX_Active_Moron ON ActiveMoron(Name)

On raffine ainsi la table aux colonnes essentielles à la condition (avec le SELECT ), et aux lignes sur lesquelles on doit l’appliquer (avec le WHERE ).
Il n’y a plus qu’à poser la contrainte, ici via un index UNIQUE .

L’index conditionnel

Disponible à partir de SQL Server 2008, il est possible de poser des index contenant une condition. Ainsi

CREATE UNIQUE INDEX IDX_Active_Moron ON ActiveMoron(Name) WHERE IsDeleted = 0

est complètement équivalent à la solution précédente, du point de vue de la contrainte, mais la vue en moins.

Source

LINQ perd la mémoire

Il existe un cas d’usage où le comportement de LINQ to Entities n’est pas naturel et peut entraîner des erreurs peu faciles à détecter. La seule chose qu’on observe est que certaines conditions ne sont pas prises en compte, seule la dernière étant prise en considération.

Je vais notamment détailler le cas où l’on construit une requête LINQ (donc un SELECT en base de données) dynamiquement et surtout itérativement, c’est à dire où la requête LINQ doit être construite en utilisant une boucle. Ce genre de requête peut être très fréquente lorsqu’on réalise une recherche, ou un moteur de recherche assez souple ou à spectre large par exemple.

Voici en exemple le comportement bizazrre relatif à LINQ ; prenons le code suivant:

var myList = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var query = myList.Select(l => l);
foreach (var el in new int[] { 1, 2, 3 })
{
	query = query.Where(q => q <= el);
}

En principe, on s’attend à ce que ce code soit identique à :

var myList = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var query = myList.Select(l => l);
query = query.Where(q => q <= 1 && q <= 2 && q <= 3);

Or si l’on exécute les extraits de code, on observe que dans le premier extrait query renvoie un ensemble contenant 1, 2 et 3, alors qu’avec le second extrait, query renvoie un ensemble ne contenant que 1.
En réalité, le premier extrait de code est équivalent à ceci :

var myList = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var query = myList.Select(l => l);
var query1 = query.Where(q => q <= 3 && q <= 3 && q <= 3);
// ce qui revient à
var query2 = query.Where(q => q <= 3);

Mais que s’est-t-il passé ?! On observe que lors de la construction d’une requête LINQ to Entities dans une boucle, la variable de boucle est considérée comme identique. C’est-à-dire que dans mon exemple, LINQ considère que el  est le même à chaque itération, ou plus exactement LINQ ajoute la condition telle quelle textuellement, et en associant à el  une valeur qui est la dernière valeur connue de el .
A la fin de la dernière itération, la requête LINQ contient donc 3 fois la même condition, (q <= el ), et y a associé pour el , sa dernière valeur, soit 3.

Ce comportement est logique lorsqu’on sait comment LINQ fonctionne en interne, mais paraît particulièrement anti-naturel la première fois qu’on rencontre ce comportement.

L’astuce pour contourner ce défaut (présent au moins dans la version 4 du .NET Framework), consiste à stocker el  dans une variable à portée locale, donc à définir une variable alpha  dans la boucle foreach  et à y affecter el . Dans ce cas, il n’y aura aucune ambiguïté sur la variable et chaque alpha  sera différent dans la requête.

Explication

Lorsqu’on construit la requête à coup d’appel successif à Where , la requête n’est pas exécutée et rien n’est évaluée, elle est construite tout comme on ferait une concaténation de chaîne.
A chaque itération, un appel à Where  génère l’expression q => q <= el (même si fonctionnellement on confond parfois les notions, cette lambda expression n’est pas un délégué ou une méthode, c’est une expression, un objet qui peut être parsé, au même titre qu’une formule mathématique peut être lu pour être interprété), où q  est une variable complètement définit puisque paramètre de l’expression, en revanche la question se pose pour el . Il faut considérer la variable comme une référence (au sens C++) vers la valeur, étant donné que el  est la variable de boucle, à chaque itération sa valeur change mais sa référence reste la même (c’est à dire que la la boite contenant la valeur ne change pas, mais la valeur dans la boite change à chaque tour de boucle).

A l’inverse, lorsqu’on déclare une variable alpha  à l’intérieur de la boucle, on s’assure que la référence de alpha  entre deux itérations est différente (puisqu’on la redéclare à chaque fois).

D’une itération à une autre, on peut alors se demander pourquoi lorsqu’on redéclare alpha , le runtime n’utilise jamais une référence précédemment affecté ? En effet on peut se dire que lorsqu’on passe à l’itération suivante, le alpha  d’avant n’est plus utilisé. C’est faux, les alpha  des itérations précédentes sont utilisés : la requête query  détient une référence vers chaque valeur prises par alpha . Du point de vue du runtime, les blocs mémoires sont donc non libre et le runtime affecte donc un autre bloc mémoire pour alpha  à chaque nouvelle itération.

Voir aussi

Construction d’une chaîne de compilation pour Android

Il existe plusieurs méthodes pour avoir une chaîne de compilation pour le système Android. Mais pourquoi une toolchain ? Android est un système d’exploitation basé sur le noyau Linux et dédié aux processeurs de type ARM (actuellement, c’est le cas). Il ne s’agit donc pas de processeurs « classiques » de type x86 ou ia64, et par conséquent, les chaînes de compilation fournies avec les distributions Linux ne sont pas adaptées, car elles ne peuvent générer que du code x86 ou ia64…

Avertissement : Pour l’instant, j’ai réussi à compiler un kernel qui boot, mais j’ai perdu la radio, j’ai seulement le wifi.
Cet article est donc présent surtout à des fins théoriques ou pour les plus persévérant à commencer les bidouilles de bas niveau :p.

Quelques chaînes de compilation pour architecture arm

  • Gentoo, avec son paquet crossdev. Actuellement non fonctionnel chez moi.
  • la chaîne de compilation pré-compilé fourni par le SDK d’Android. Actuellement non fonctionnel chez moi.
  • la chaîne de compilation fournie par crosstool-ng. La plus configurable, la plus chiante à mettre en place, mais la seule qui a marché.

Il est possible que les autres chaînes de compilation marchent parfaitement bien, j’ai juste du louper un truc sur les paramètres à passer… c’est dire que contrairement au monde x86, des architectures ARM, il y en a à la pelle et pas souvent compatibles entre elles (pas sans recompilation).

Cette article détaillera uniquement la dernière chaîne de compilation, qui a l’avantage d’avoir beaucoup de paramètres que l’on peut utiliser pour optimiser sa chaîne.

Les étapes décrites ci-dessous s’appliquent à Gentoo, mais peuvent s’appliquer à n’importe quel UNIX-like avec quelques modifications mineures.

# si ce n'est pas déjà fait, on installe git
emerge -v git

# récupération du kernel cyanogen
export KERNELDIR=/home/kernel-cyanogen
export CROSSTOOLDIR=/home/crosstool-ng
mkdir -p $KERNELDIR
cd $KERNELDIR
#git clone git://github.com/CyanogenMod/cm-kernel.git
# pour certains appareils htc dont le vision, un autre repo est utilisé
git clone git://github.com/CyanogenMod/htc-kernel-msm7x30.git
# aller boire un café, ça va prendre 10bonnes minutes...
#cd cm-kernel
cd htc-kernel-msm7x30
make headers_install ARCH=arm INSTALL_HDR_PATH=../kern_h/

# construction de la toolchain
echo 'sys-devel/ct-ng' >> /etc/portage/package.keywords
emerge -va ct-ng
eselect bashcomp enable ct-ng --global
mkdir -p $CROSSTOOLDIR
cd $CROSSTOOLDIR
# placez le fichier uClibc.config ici, depuis l'archive à télécharger
#mv XVilka-uClibc-msm7200a.config uClibc.config
ct-ng menuconfig

Ci-dessous les changements à effectuer pour le menuconfig de crosstool-ng

Paths and misc options --->
(1) Number of parallel jobs
Target options --->
Target Architecture (arm)
(armv7-a) Architecture level
(cortex-a8) Emit assembly for CPU
(vfpv3) Use specific FPU
(-O2) Target CFLAGS
Toolchain options --->
(android) Tuple's vendor string
Operating System --->
Target OS (linux)
Linux kernel version (2.6.37.6) // à adapter en fonction de la version du kernel du cyanogenmod
C compiler --->
[*] C++
[*] Java
C-library --->
C library (uClibc)
(uClibc.config) Configuration file
Threading implementation to use: (linuxthreads)

Et on retourne à la ligne de commande…

ct-ng build
# là on reprend une tasse de café

# récupération des sources du cyanogenmod
export CYANODIR=/home/cyanogen
cd $CYANODIR
repo sync
make adb
export PATH=/out/host/linux-x86/bin:$PATH

# compilation d'un nouveau kernel
export _XXCFLAGS=" -march=armv7-a -mtune=cortex-a8 -mfpu-abi=softfp -mfpu=vfpv3 -O2"
export PATH=~/x-tools/arm-android-linux-uclibcgnueabi/bin:$PATH
export CCOMPILER=~/x-tools/arm-android-linux-uclibcgnueabi/bin/arm-android-linux-uclibcgnueabi-
cd $KERNELDIR/htc-kernel-msm7x30
# branchez le téléphone au pc
adb pull /proc/config.gz .
zcat config.gz > .config
# menuconfig pour changer deux ou trois choses avant de compiler (à faire avec beaucoup de précautions)
CFLAGS=$_XXCFLAGS make ARCH=arm CROSS_COMPILE=$CCOMPILER menuconfig
CFLAGS=$_XXCFLAGS make ARCH=arm CROSS_COMPILE=$CCOMPILER oldconfig
CFLAGS=$_XXCFLAGS make ARCH=arm CROSS_COMPILE=$CCOMPILER -j`grep 'processor' /proc/cpuinfo | wc -l` all
cd $CYANODIR/device/htc/vision
mv kernel kernel.bak
mv modules/bcm4329.ko modules/bcm4329.ko.bak
ln -s $KERNELDIR/htc-kernel-msm7x30/arch/arm/boot/zImage kernel
ln -s $KERNELDIR/htc-kernel-msm7x30/drivers/net/wireless/bcm4329/bcm4329.ko modules/
cd $CYANODIR
. build/envsetup.sh && brunch cyanogen_vision-eng

Et voilà, plus qu’à flasher.

Tous les crédits vont à XVilka, pour avoir décrit sa méthode pour le milestone.

Voir aussi

Personnaliser le WebDeploy

WebDeploy est un outil dans Visual Studio permettant de compiler puis déployer un projet web sur un serveur IIS. Il est très simple d’usage mais… la personnalisation est inexistante.

Compte tenu de la complexité du processus de build des projets .NET et de la qualité de la documentation fournit, ça n’a pas été évident mais j’ai pu finalement cracher ce bout de XML. Il est à ajouter dans le fichier csproj du projet web à la fin.

Voici ce qu’il fait :

  1. On renseigne en paramètre l’exécutable signtool, qui permet de signer numériquement un fichier PE (ça correspond à l’onglet « Signatures numériques » dans les propriétés d’un fichier signé
  2. On renseigne le certificat à utiliser par sa somme de contrôle SHA1
  3. On indique avec un nom de variable particulier le fichier snk contenant les clés pour ajouter un nom fort aux assemblies. Les variables sont KeyOriginatorFile  et _FullKeyFile  et sont reconnus par le processus de compilation.
  4. Après la compilation normale du projet, on signe le fichier *.dll  et les fichiers *.ressources.dll généré par le projet web
  5. Après l’étape 4, on lance la cible MvcBuildViews qui va compiler toutes les vues dans une assembly qui se nomme {nom du projet web}.WebUI.dll. Une fois que c’est fait, on signe également la nouvelle assembly et ses ressources.
  6. Le processus normal reprend et pousser le résultat sur le serveur IIS
<PropertyGroup Condition="Exists('C:\Users\veovis')">
 <Sha1Thumbnail>6b30be2f5fd6cb22225191b803c9232755dc07b8</Sha1Thumbnail>
 <SignAssembly>true</SignAssembly>
 <SignToolLocation>C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe</SignToolLocation>
 <AssemblyOriginatorKeyFile>C:\Users\sebastien\Documents\Security\euphor.snk</AssemblyOriginatorKeyFile>
 <_FullKeyFile>$(AssemblyOriginatorKeyFile)</_FullKeyFile>
 <KeyOriginatorFile>$(AssemblyOriginatorKeyFile)</KeyOriginatorFile>
 <DelaySign>false</DelaySign>
 <OnAfterCopyAllFilesToSingleFolderForPackage>
MvcBuildViews;
 </OnAfterCopyAllFilesToSingleFolderForPackage>
</PropertyGroup>
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release' And '$(SignToolLocation)' != ''">
 <ItemGroup>
 <OutputFiles Include="$(TargetPath)"/>
 <OutputFiles Include="$(TargetDir)\*\$(TargetFileName).resources.dll" />
 </ItemGroup>
 <Exec Command="&quot;$(SignToolLocation)&quot; sign /sha1 $(Sha1Thumbnail) /tr &quot;http://www.startssl.com/timestamp&quot; /ph &quot;%(OutputFiles.FullPath)&quot;" />
</Target>
<Target Name="MvcBuildViews">
 <RemoveDir Directories="$(_PackageTempDir)2" />
 <AspNetCompiler VirtualPath="temp" PhysicalPath="$(_PackageTempDir)" Force="true" Updateable="false" FixedNames="true" TargetPath="$(_PackageTempDir)2" />
 <AspNetMerge ExePath="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools" Nologo="true" SingleAssemblyName="$(TargetName).WebUI" ApplicationPath="$(_PackageTempDir)2" CopyAttributes="true" AssemblyInfo="$(TargetPath)" KeyFile="$(_FullKeyFile)" />
 <ItemGroup>
 <DllFiles Include="$(_PackageTempDir)2\bin\$(TargetName).WebUI.dll" />
 <DllFiles Include="$(_PackageTempDir)2\bin\*\$(TargetName).WebUI.resources.dll" />
 </ItemGroup>
 <Exec Command="&quot;$(SignToolLocation)&quot; sign /sha1 $(Sha1Thumbnail) /tr &quot;http://www.startssl.com/timestamp&quot; /ph &quot;%(DllFiles.FullPath)&quot;" />
 <RemoveDir Directories="$(_PackageTempDir)" />
 <Exec Command="rename &quot;$(_PackageTempDir)2&quot; &quot;PackageTmp&quot;" />
</Target>

Toutes ces étapes ne sont évidemment pas obligatoires et ce fichier DOIT être personnalisé en fonction de son environnement.

Conserver la position du défilement de la page

Depuis ASP.NET 2, il existe une directive bien sympathique qui permet de repositionner la barre de défilement d’une page après un postback.
Il suffit simplement de positionner la directive MaintainScrollPositionOnPostBack  à true  dans la balise de la page :

<%@ Page ... MaintainScrollPositionOnPostBack="true" %>

C’est quand même mieux que de se farcir un script javascript, non ?

Voir aussi

Détecter proprement l’UTF-8

Je me suis toujours demandé s’il était possible (et si c’est le cas, comment) de détecter un texte UTF-8 (sans le BOM, ça serait trop facile sinon) d’un autre texte ISO-8859.

Mon ami W3C m’a répondu et m’a donné le script perl ci-dessous :

$field =~
  m/\A(
     [\x09\x0A\x0D\x20-\x7E]            # ASCII
   | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
   |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
   | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
   |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
   |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
   | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
   |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
  )*\z/x;

Si l’expression régulière du dessus trouve une occurrence dans le texte à tester, alors bingo c’est de l’utf !