Mon blog change d’adresse

Catégories :Uncategorized

[Techdays] Dernière ligne droite ! Rejoignez nous :)

Dernière ligne droite avant les techdays! Si ce n’est pas déjà fait, je vous encourage fortement à vous inscrire et à venir nombreux à cet évènement. Outre le fait que ce soit gratuit, que vous allez pouvoir assister à de nombreuses conférences de qualité, vous allez aussi pouvoir rencontrer les gens qui font la communauté et mettre enfin des visages sur des pseudos ou auteurs de vos blogs préférés.

Pour ma part, je serais au stand technet pour animer la partie ATE (ask the expert) : http://www.microsoft.com/france/mstechdays/espace-projet/default.aspx. Si vous avez des questions sur Commerce Server, n’hésitez pas! Vous savez ou me trouver 🙂

Concernant notre technologie préférée, Commerce Server aura droit à trois sessions:

  1. AVANADE – Le Digital Marketing montré sur la plateforme Microsoft  (PAR202)
    • lundi 8 février 2010, 11:00 – 12:00
  2. Optimisez les investissements de votre plateforme Web et E-Commerce  (RDI209), par Jean-François Gomez
    • mardi 9 février 2010, 11:00 – 12:00
  3. Intégration de Commerce Server 2009 dans le Système d’Information avec le cas client JM Bruneau  (OWB202)
    • mardi 9 février 2010, 14:30 – 15:30

       

Au plaisir de vous y rencontrer 🙂

Catégories :Events

[Commerce Server] Apart Fashion – Prêt a porter femme, boutique mode et collection tendance

La team Commerce Server d’Altima est heureuse de vous annoncer la sortie du nouveau site APART – Fashion. Cette marque de vêtements haut de gamme pour femmes propose des collections chics et originales en vente à distance exclusivement ! Mesdames, à vos cartes bleues!

A noter aussi, qu’APART-Fashion est aussi présent sur FaceBook: c’est par ici!

 

Catégories :Commerce Server

[Commerce Server] Créer des rapports simples et efficaces grâce à PowerPivot (aka Gemini)

Dans un de mes précédents post, j’ai expliqué comment mettre en place la solution analytics de Commerce Server, et je pense qu’il a dû y avoir de la déception chez certains (qui a dit tous?) d’entre vous!

Même si les cubes fournis peuvent être un bon point de départ, le process d’import est lui complètement obsolète et s’apparente plus à une boite noire où il est quasi impossible d’y “tunner” (comprendre?) quelque chose (je laisse sous silence les rapports qui sont eux plus que périmé)…

Bref, vous l’aurez compris, il est temps que la team Commerce Server se penche sur cette fonctionnalité pour lui donner un bon coup de peinture!

En attendant, que faire?:

  • Développez de nouveaux rapports sur la base des cubes/process import
    • Avantage:
      • Pas de cube à développer, on ne part pas de 0 
      • En fonction de vos besoins peu de temps de développement pour ajouter quelques informations
    • Inconvénient
      • Espérer que le process d’import se passe bien et corresponde à vos besoins
      • Long travail de redesign des rapports si vous souhaitez un layout plus moderne
      • La structure des cubes ne correspond pas forcément à votre besoin
      • Nécessite une intervention de l’IT pour toutes adaptations
  • Développez vos propres cubes
    • Avantage
      • Vous collez à vos besoins
    • Inconvénient
      • Nécessite une intervention de l’IT pour toutes modifications/évolutions
      • Temps de mise en place pouvant être assez long
  • Ne rien faire
    • 🙂

Dans tous les cas, un passage par l’IT s’avère obligatoire et vous n’avez pas forcément la patience d’attendre pour exploiter votre site internet?

Bonjour et bienvenue à Gemini, pardon Power Pivot 🙂

Power Pivot est le nouvel outil développé par Microsoft pour faciliter et vulgariser la BI sur le poste de travail (rien que çà). Il se présente sous la forme d’un add-in pour Excel 2010 (attention ne fonctionne qu’avec la version d’office 2010):

Je me suis donc amusé à le tester sur un site Commerce Server d’un de mes clients et j’avoue que je suis sidéré par sa facilité de prise en main et la rapidité d’exécution.

L’ajout de source de données se fait via un onglet dédié et comme toujours dans office, la manipulation est largement facilitée et guidée:

          

Et voilà, en 5 minutes montre en main, j’ai importé une base transactions de Commerce Server de 2Go dans un fichier Excel! Et cerise sur le gâteau, le fichier fait à peine 40mo! Pour les sceptiques:

Grâce à la structure relationnel et multi dimensionnel de Power Pivot, je vais pouvoir créer simplement et rapidement des tableaux croisés dynamiques ou graphiques en quelques minutes. Prenons par exemple une représentation graphique du nombre de commande par jour

  • Tout d’abord, spécifier qu’on veut un graphique

 

  • Ensuite, on ajoute en valeur le nombre de tracking number et en axis la date de création

  • Pas vraiment lisible je vous l’accorde 🙂 En effet, la colonne created contient la date et l’heure! Pour regrouper par mois, il nous faut donc une colonne ne contenant que le mois et l’année du champ ‘”created”. Pour cela, rien de plus facile! Il nous suffit de créer une nouvelle colonne dans notre onglet “Purchase Orders” en utilisant le langage DAX  (qui est très proche du langage de formule d’Excel): RIGHT("0" &MONTH([Created]),2) &"/" &year([Created])

  • On rafraichit la source et notre nouvelle colonne est maintenant disponible. Résultat:

Facile non 🙂

Vous comprenez qu’il est donc très simple de mettre en place des dashboards puissant grâce au couplage PowertPivot (pour les data) et Excel (pour le formatage), exemple:

 

Pour plus d’informations:

Catégories :Commerce Server

[Commerce Server] Bienvenue chez Mathon : Mathon, ustensiles de cuisine et accessoires de cuisine

Je suis intervenu, avec mon collègue Anoir, sur le site e-Commerce de la société Mathon afin de leur apporter notre expertise autour des solutions Commerce Server et du développement .Net.

En effet, victime de son succès, le site connaissait de temps en temps des problèmes de performances et surtout de capacité à prendre un gros nombre d’utilisateurs dans une faible période de temps (comme par exemple lors de l’envoi d’une newsletter ou du début de la période des soldes).

Notre intervention s’est donc surtout portée sur l’optimisation du code .Net (mise en place d’un caching puissant, optimisation des controles .Net utilisés, …) ainsi que sur les bonnes pratiques d’utilisation du framework de Commerce Server (comme systématiser le CatalogItemConfiguration).

Le site est maintenant très fluide et près à recevoir vos nombreuses commandes :). A vos cartes bleues!

Je vous invite par la même à suivre le blog de Cyril, qui est un réel passionné des technologies Microsoft 🙂

 

Catégories :Commerce Server

[Commerce Server] Secretaire ou Assistante de direction, Kalligo est l’outil en ligne fait pour vous

La team Commerce Server d’Altima est heureuse de vous annoncer la naissance du portail des secrétaires et assistantes de direction répondant au doux nom de Kalligo. A vos cartes bleues!

Catégories :Commerce Server

[IIS] Découvrez le module de réécriture d’URL d’IIS7

Les différentes VPC ou templates de site distribués par l’équipe Commerce Server ne disposent pas par défaut d’une réécriture d’url. Et c’est actuellement la meilleure excuse que j’ai trouvé pour vous présenter le nouveau module de réécriture intégré à IIS: URL Rewrite Module :).

L’installation de ce module est très simple, vous pouvez le faire soit via un exécutable soit avec le nouvel auto installeur “Microsoft Web Plateform Installer”. Une fois installé, une nouvelle icone est présente dans votre IIS manager sous la section IIS:

Ce module vous propose plusieurs actions:

  • Création manuelle d’une règle de réécriture
  • Création assisté à partir d’une url brute (j’en connais qui vont adorer)
  • Le connu url map (url statique de votre site)
  • Le blocage

Commençons par la création assistée

=> Là je suis sûr de faire mouche auprès de nombreuses personnes réfractaire à l’expression régulière 🙂

Prenez simplement votre url brut du style: http://adventureworks.spgael.poc/Pages/Category.aspx?cat=Adventure Works Catalog&category=Boots et collez la dans le premier textbox. Là le module va vous générez une liste de template puis sur la sélection d’un des templates, votre expression régulière se génère automatiquement! Je vous avais dit que je ferais mouche 🙂

Résultat:

Passons maintenant à la méthode manuelle

Ici, pas question de se passer de nos bonnes vieilles expressions régulières. Tout est maitrisé et vous pouvez ainsi coller aux besoins de vos clients. Dans cet exemple, je vais réécrire: http://adventureworks.spgael.poc/Pages/Product.aspx?category=Boots&cat=Adventure Works Catalog&id=AW074-04 en http://adventureworks.spgael.poc/Pages/Adventure%20Works%20Catalog/Boots/PID-AW074-04.aspx. Pour cela, je rentre mon expression régulière et chose “cool”, je peux la tester et vérifier que la génération de mes paramètres est correcte (notez ici que les paramètres sont à récupérer via {R:x} ou x et la place du paramètre dans la collection). Vous pouvez ensuite paramétrer les différentes conditions à vérifier (ex si vous souhaitez vérifier que ce n’est pas un fichier)

   

Résultat:

 

Les url mappings

Rien de sorcier ici, les url mapping ne sont pas les choses les plus complexes à gérer 🙂

Le blocage d’url

La non plus rien de bien compliqué, si ce n’est que comme pour l’url mapping, vous êtes en mode assisté 🙂

Le web.config

Rendez-vous maintenant dans le web.config et vous verrez les règles créer apparaitre dans la section system.webServer/rewrite

<system.webServer>
    <rewrite>
        <rules>
            <rule name="RewriteUserFriendlyURL1" stopProcessing="true">
                <match url="^Pages/Category/([^/]+)/([^/]+)/?$" />
                <conditions>
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                </conditions>
                <action type="Rewrite" url="Pages/Category.aspx?cat={R:1}&amp;category={R:2}" />
            </rule>
            <rule name="Product Url" stopProcessing="true">
                <match url="^Pages/([_0-9a-z- ]+)/([_0-9a-z- ]+)/PID-([_0-9a-z- ]+)\.aspx" />
                <action type="Rewrite" url="Pages/Product.aspx?category={R:2}&amp;cat={R:1}&amp;id={R:3}" />
            </rule>
        </rules>
    </rewrite>
</system.webServer>

Voilà, donc de quoi rendre vos chers référenceurs heureux (coucou Renaud) et plus d’excuses à ne pas le mettre en place lors de vos démos ou sur les sites live (si, si il y a encore des gens qui ne réécrivent pas les urls).

Technorati Tags: ,,,

[Commerce Server 2009] La dernière CTP de Commerce Server 2009 R2 est disponible

Récemment, la team Commerce Server a publié la dernière CTP de Commerce Server 2009 R2.  Comme vous vous en doutez, je me suis empressé de l’installer pour découvrir les nouvelles features et les partager avec vous.

Tout d’abord, vous pouvez dire adieu à vos vieux processeurs 32 bits puisque Commerce Server 2009 R2 ne tournera que sur du 64bits! Idem pour vos windows 2003 ou SQL 2005. La R2 ne supporte que Windows 2008 (R2 ou non) et SQL Server 2008 (R2 ou non).

Mon premier retour est plus que positif! De nombreux points ont été considérablement amélioré. Commençons par l’installation où tout a été repensé. Nous avons maintenant beaucoup plus de souplesse sur la sélection des composants à installer:

Sur la configuration de Commerce Server, un effort a aussi été fait pour vous guider au mieux. 2 choses à dire ici:

  • Pour le moment, seule la configuration de base est activée. L’option avancée viendra dans une future version
  • Il y a un bug si vous souhaitez utiliser un serveur SQL distant. Pour le moment, seul une installation en mode SQL locale fonctionne via l’outil. Pour contourner le problème, il vous faut lancer vous même la configuration via une ligne de commande et un fichier XML.
C:\Program Files (x86)\Microsoft Commerce Server 9.0>CSConfigWizard.exe /s ConfigLaunch.xml
<Configuration>
  <SQL ID="CommerceAdminDB">
    <Server>RemoteSQLServer</Server>
    <Database>MSCS_Admin</Database>
    <WindowsSecurity>yes</WindowsSecurity>
  </SQL>
  <SQL ID="DirectMailerDB">
    <Server>RemoteSQLServer</Server>
    <Database>DirectMailer</Database>
    <WindowsSecurity>yes</WindowsSecurity>
  </SQL>
  <NTService ID="DirectMailerService">
    <UserName>CSService</UserName>
    <Domain>SPGAEL</Domain>
    <Password>password</Password>
  </NTService>
  <VirtualDirectory ID="Publishing" Create="False" />
  <NTService ID="StagingService">
    <UserName>CSService</UserName>
    <Domain>SPGAEL</Domain>
    <Password>password</Password>
  </NTService>
</Configuration>

Concernant la mise en place du template Storefront, rien de particulier à dire pour les habitués de Commerce Server:

  • Méthode manuelle:
    • Unpup
    • Gestion de la sécurité et des utilisateurs
    • Configuration des webservices
    • Deployer les solutions SharePoint
  • Méthode assistée:
    • %commerce_server_root%\SharePointServices\Site\SharePointCommerceServicesConfiguration.exe
      • j’ai eu pas mal de problèmes avec le SharePointCommerceServicesConfiguration dans la version de Commerce Server 2009, qui m’ont fait privilegié la méthode déploiement/configuration manuelle. Sur la R2, le process a été amélioré car j’ai tenté plusieurs déploiement qui ceux sont tous déroulés avec succès 🙂 Encore un bon point pour la R2

Notez au passage, que layout du site n’a pas beaucoup évolué mais propose quelques trucs sympa comme la configuration des catalogues utilisés/channel

Attention de nombreuses assemblies ont été modifiées. Consultez bien la documentation de la R2 qui explique cela en détail! Ne vous étonnez donc pas des effets de bord lors des déploiements de votre environnement Commerce Server 2009.

Technorati Tags: ,,

Catégories :Commerce Server

[Commerce Server] ProductConfiguration / CategoryConfiguration ou les oubliés de Commerce Server

Ces derniers temps, j’ai été sollicité sur des problématiques d’expertise Commerce Server qui m’ont permis de me rendre compte que 2 classes fondamentales pour tous développements Commerce Server étaient totalement méconnue, j’ai nommé:

Ces deux classes héritent toutes de la classe Microsoft.CommerceServer.Catalog.CatalogItemConfiguration, et elles sont utilisées pour ne charger que les éléments que vous avez besoin d’utiliser. En effet, il n’est pas rare lorsque vous souhaitez afficher une liste de produits, de ne vouloir afficher que certaines propriétés du produit sans avoir à utiliser ses variants, ses catégories parentes, etc., etc….

D’ailleurs, si vous jetez un œil sur l’API via Reflector, vous vous apercevez que la classe CategoryConfiguration ou ProductConfiguration est instanciée si vous n’en spécifiez aucune (ie, le framework choisit pour vous les choses à charger):

Pour illustrer l’importance de ces classes, prenons l’exemple de la récupération d’un produit ou nous n’avons besoin d’afficher que son prix et son descriptif:

  • Sans ProductConfiguration:
Product product = catalogCtx.GetProduct("Adventure Works Catalog", "AW029-03");
Console.WriteLine("Pid: " + product.ProductId + " - " + product.DisplayName + " - " + product.ListPrice);

Grâce au SQL Profiler, nous pouvons récupérer les appels SQL Server et se rendre compte qu’ici 2 procédures stockées sont appelées:

exec dbo.ctlg_GetProductProperties
     @BCName=N'Adventure Works Catalog'
    ,@fVirtualCatalog=0
    ,@oid=30
    ,@ProductUID_tmp=N'ProductCode'
    ,@VariantUID_tmp=N'VariantCode'
    ,@catalogname=N'Adventure Works Catalog'
    ,@Language=N'en-US'
    ,@eJoinType=-1
    ,@TargetTableName=N''
    ,@SourceJoinKey=N''
    ,@TargetJoinKey=N''
    ,@Columns_tmp=N'P.BaseCatalogName,CategoryName,P.oid,DefinitionName,IsSearchable,cy_list_price,UseCategoryPricing,i_ClassType,CatalogName,ProductId,OrigProductId,VariantId,OrigVariantId,PrimaryParentCategory,DisplayName,Timestamp,OriginalPrice,LastModified,ParentOId'
    ,@EnableInventory=1
    ,@InventoryServerName=NULL
    ,@InventoryDatabaseName=NULL
    ,@FilterOutOfStockProducts=0
    ,@FilterBackOrderableProducts=0
    ,@filterPreorderedSkus=0
    ,@UseThresholdAsFloor=1
    ,@TreatmissingAsOutOfStock=0
    ,@StockHandling=0

declare @p15 int
set @p15=3
exec dbo.ctlg_GetProductVariantProperties
    @fVirtualCatalog=0
    ,@catalogname=N'Adventure Works Catalog'
    ,@Language=N'en-US'
    ,@ProductID=N'AW029-03'
    ,@VariantID=N''
    ,@eJoinType=-1
    ,@TargetTableName=N''
    ,@SourceJoinKey=N''
    ,@TargetJoinKey=N''
    ,@BuiltInColumnsToReturn=N'BaseCatalogName,CategoryName,OrigCategoryName,P.oid,DefinitionName,IsSearchable,cy_list_price,UseCategoryPricing,i_ClassType,P.CatalogName,ProductId,OrigProductId,VariantId,OrigVariantId,PrimaryParentCategory,DisplayName,Timestamp,OriginalPrice,LastModified,rank,ParentOId'
    ,@OrderBy=N'[Rank]'
    ,@StartingRec=1
    ,@NumRecords=2147483646
    ,@SortAscending=1
    ,@RecordCount=@p15 output
    ,@ProductOID=30
    ,@EnableInventory=1
    ,@InventoryServerName=default
    ,@InventoryDatabaseName=default
    ,@FilterOutOfStockProducts=0
    ,@FilterBackOrderableProducts=0
    ,@FilterPreorderedSkus=0
    ,@UseThresholdAsFloor=1
    ,@TreatmissingAsOutOfStock=0
    ,@StockHandling=0
    ,@VariantUID=N'VariantCode'
select @p15

  • Via un ProductConfiguration:
ProductConfiguration pdtConf = new ProductConfiguration();
pdtConf.InventoryOptions = new Microsoft.CommerceServer.Inventory.InventoryOptions();
pdtConf.InventoryOptions.FilterBackorderableSkus = false;
pdtConf.InventoryOptions.FilterOutOfStockSkus = false;
pdtConf.InventoryOptions.FilterPreorderableSkus = false;
pdtConf.LoadAncestorCategories = false;
pdtConf.LoadCanonicalCategories = false;
pdtConf.LoadParentCategories = false;
pdtConf.LoadRelatedCategories = false;
pdtConf.LoadRelatedProducts = false;
pdtConf.LoadVariants = false;
Product product = catalogCtx.GetProduct("Adventure Works Catalog", "AW029-03", "en-US", pdtConf);
Console.WriteLine("Pid: " + product.ProductId + " - " + product.DisplayName + " - " + product.ListPrice);

Cette fois-ci nous pouvons constater qu’une seule procédure stockée est appelée (car nous ne remontons pas les variants):

exec dbo.ctlg_GetProductProperties
     @BCName=N'Adventure Works Catalog'
    ,@fVirtualCatalog=0
    ,@oid=30
    ,@ProductUID_tmp=N'ProductCode'
    ,@VariantUID_tmp=N'VariantCode'
    ,@catalogname=N'Adventure Works Catalog'
    ,@Language=N'en-US'
    ,@eJoinType=-1
    ,@TargetTableName=N''
    ,@SourceJoinKey=N''
    ,@TargetJoinKey=N''
    ,@Columns_tmp=N'P.BaseCatalogName,CategoryName,P.oid,DefinitionName,IsSearchable,cy_list_price,UseCategoryPricing,i_ClassType,CatalogName,ProductId,OrigProductId,VariantId,OrigVariantId,PrimaryParentCategory,DisplayName,Timestamp,OriginalPrice,LastModified,ParentOId'
    ,@EnableInventory=1
    ,@InventoryServerName=NULL
    ,@InventoryDatabaseName=NULL
    ,@FilterOutOfStockProducts=0
    ,@FilterBackOrderableProducts=0
    ,@filterPreorderedSkus=0
    ,@UseThresholdAsFloor=1
    ,@TreatmissingAsOutOfStock=0
    ,@StockHandling=0

En apparence , le ProductConfiguration nous a permi d’éviter l’appel à la procédure stockée qui nous remontait les variants. Mais jetez un oeil du côté des statistiques SQL pour vous apercevoir qu’il y a bien plus que cela:

  • Sans ProductConfiguration

    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘InventoryCatalogMap’. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_en-US_Catalog’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘InventoryCatalogMap’. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CtlInv__Variants1__for_spid__60’. Scan count 0, logical reads 7, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 3, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_CatalogHierarchy’. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_en-US_Catalog’. Scan count 0, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 3, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CtlInv__Variants1__for_spid__60’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

  • Avec ProductConfiguration

    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘InventoryCatalogMap’. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_en-US_Catalog’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Impressionnant non? 🙂 3 fois moins d’exécution côté SQL 🙂

Attaquons nous maintenant à la catégorie. C’est là ou les résultats sont les plus impressionnant (si si :))

  • Sans CategoryConfiguration
Category category = catalogCtx.GetCategory("Adventure Works Catalog", "Tents");
Console.WriteLine("Cid: " + category .Name+ " - " + category .DisplayName);
Côté SQL, nous constatons que 3 procédures stockées sont appelées :
exec dbo.ctlg_GetCategoryProperties
     @BCName=N'Adventure Works Catalog'
    ,@oid=3
    ,@catalogname=N'Adventure Works Catalog'
    ,@Language=N'en-US'
    ,@Columns_tmp=N'BaseCatalogName,CategoryName,OrigCategoryName,oid,DefinitionName,IsSearchable,cy_list_price,UseCategoryPricing,i_ClassType,CatalogName,ProductId,OrigProductId,VariantId,OrigVariantId,PrimaryParentCategory,DisplayName,Timestamp,OriginalPrice,LastModified,ParentOId'
    ,@fVirtualCatalog=0

declare @p13 int
set @p13=4
exec dbo.ctlg_GetProductsForCategory
     @CatalogName=N'Adventure Works Catalog'
    ,@language=N'en-US'
    ,@BCName=N'Adventure Works Catalog'
    ,@CategoryOid=3
    ,@ClassType=12
    ,@StartingRecord=1
    ,@NumRecords=500
    ,@OrderBy=N'[Rank]'
    ,@eJoinType=-1
    ,@TargetTableName=N''
    ,@SourceJoinKey=N''
    ,@TargetJoinKey=N''
    ,@RecordCount=@p13 output
    ,@PropertiesToReturn=N'*'
    ,@SortAscending=1
    ,@EnableInventory=1
    ,@InventoryServerName=NULL
    ,@InventoryDatabaseName=NULL
    ,@FilterOutOfStockProducts=0
    ,@FilterBackOrderableProducts=0
    ,@FilterPreOrderableProducts=0
    ,@UseThresholdAsFloor=1
    ,@TreatmissingAsOutOfStock=0
    ,@StockHandling=0
    ,@InventoryPropertiesToReturn=N'*'
    ,@fVirtualCatalog=0
select @p13

declare @p10 int
set @p10=0
exec dbo.ctlg_GetChildCategories
     @CatalogName=N'Adventure Works Catalog'
    ,@Language=N'en-US'
    ,@BCName=N'Adventure Works Catalog'
    ,@oid=3
    ,@StartingRec=1
    ,@NumRecords=500
    ,@PropertiesToReturn=N'*'
    ,@OrderBy=N'[Rank]'
    ,@SortAscending=1
    ,@RecordCount=@p10 output
    ,@fVirtualCatalog=0
select @p10

  • Avec un CategoryConfiguration: 
CategoryConfiguration catConf = new CategoryConfiguration();
catConf.InventoryOptions = new Microsoft.CommerceServer.Inventory.InventoryOptions();
catConf.InventoryOptions.FilterBackorderableSkus = false;
catConf.InventoryOptions.FilterOutOfStockSkus = false;
catConf.InventoryOptions.FilterPreorderableSkus = false;
catConf.LoadAncestorCategories = false;
catConf.LoadCanonicalCategories = false;
catConf.LoadParentCategories = false;
catConf.LoadRelatedCategories = false;
catConf.LoadRelatedProducts = false;
Category category = catalogCtx.GetCategory("Adventure Works Catalog", "Tents", "en-US", catConf);
Console.WriteLine("Cid: " + category .Name+ " - " + category .DisplayName);

Côté base de données, plus qu’un seul appel à une procédure stockée:

exec dbo.ctlg_GetCategoryProperties
     @BCName=N'Adventure Works Catalog'
    ,@oid=3
    ,@catalogname=N'Adventure Works Catalog'
    ,@Language=N'en-US'
    ,@Columns_tmp=N'BaseCatalogName,CategoryName,OrigCategoryName,oid,DefinitionName,IsSearchable,cy_list_price,UseCategoryPricing,i_ClassType,CatalogName,ProductId,OrigProductId,VariantId,OrigVariantId,PrimaryParentCategory,DisplayName,Timestamp,OriginalPrice,LastModified,ParentOId'
    ,@fVirtualCatalog=0

Grâce à la classe CategoryConfiguration correctement configurée, on a évité 2 exécutions de procédure stockée, mais vu côté statistique SQL c’est impressionnant! Jugé plutôt:

  • Sans CategoryConfiguration

Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_en-US_Catalog’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CtlInv__FirstLevelProducts__for_spid__59’. Scan count 0, logical reads 27, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 4, logical reads 8, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogHierarchy’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘InventoryCatalogMap’. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CtlInv__SecondLevelProducts__for_spid__59’. Scan count 0, logical reads 18, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CtlInv__FirstLevelProducts__for_spid__59’. Scan count 5, logical reads 10, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_en-US_Catalog’. Scan count 0, logical reads 8, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 4, logical reads 8, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CtlInv__SecondLevelProducts__for_spid__59’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CtlInv__ChildCategories__for_spid__59’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 4, logical reads 8, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogHierarchy’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CtlInv__ChildCategories__for_spid__59’. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

  • Avec CategoryConfiguration

Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CatalogDefinitionPropertyList’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_en-US_Catalog’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Adventure Works Catalog_CatalogProducts’. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Quasiment 5 fois moins d’exécution SQL, pour un même résultat!!!!

Vous comprennez donc maintenant pourquoi il est très important de spécifier un CatalogItemConfiguration: il peut changer drastiquement les performances de votre site e-Commerce (et ceci même si vous utilisez le cache).

Pour ma part, si vous ne savez pas à l’avance quoi loader (c’est rarement le cas… j’espère ;)), je vous recommande plutôt d’opter pour du lazy loading, c’est à dire de configurer au minima (en gros je mets toutes les options à false) mon GetProduct ou GetCategory et laisse l’API charger les objets dont j’ai besoin.

Pour terminer, j’ai mis un petit graphique illustrant mes propos ou je me suis amusé à boucler 50 fois sur une récupération de Produit via un getProduct avec différentes options (pour info le cache était activé):

En bleu:

ctx.RefreshCache(); //refresh the catalogue cache system
for (int i = 0; i < 50; i++)
{
     chrono.Start();
     foreach (String pId in Constants.PRODUCTS)
     {
          Product p = ctx.GetProduct(Constants.CATALOGNAME, pId, "en-US");
     }
     chrono.Stop();
     long spent = chrono.ElapsedMilliseconds;
     chrono.Reset();
}

En rouge:

ctx.RefreshCache(); //refresh the catalogue cache system
ProductConfiguration pdtConf = new ProductConfiguration();
pdtConf.InventoryOptions = new Microsoft.CommerceServer.Inventory.InventoryOptions();
pdtConf.InventoryOptions.FilterBackorderableSkus = true;
pdtConf.InventoryOptions.FilterOutOfStockSkus = true;
pdtConf.InventoryOptions.FilterPreorderableSkus = true;
pdtConf.InventoryOptions.PropertiesToReturn = propertiesToReturn;

pdtConf.LoadAncestorCategories = true;
pdtConf.LoadCanonicalCategories = true;
pdtConf.LoadParentCategories = true;
pdtConf.LoadRelatedCategories = true;
pdtConf.LoadRelatedProducts = true;
pdtConf.LoadVariants = true;
pdtConf.ParentPropertiesToReturn = "*";
pdtConf.Variants.SearchOptions = new CatalogSearchOptions();
pdtConf.Variants.SearchOptions.PropertiesToReturn = "*";
for (int i = 0; i < 50; i++)
{
     chrono.Start();
     foreach (String pId in Constants.PRODUCTS)
     {
          Product p = ctx.GetProduct(Constants.CATALOGNAME, pId, "en-US", pdtConf);
     }
     chrono.Stop();
     long spent = chrono.ElapsedMilliseconds;
     chrono.Reset();
}

En vert:

ctx.RefreshCache(); //refresh the catalogue cache system
ProductConfiguration pdtConf = new ProductConfiguration();
pdtConf.InventoryOptions = new Microsoft.CommerceServer.Inventory.InventoryOptions();
pdtConf.InventoryOptions.FilterBackorderableSkus = false;
pdtConf.InventoryOptions.FilterOutOfStockSkus = false;
pdtConf.InventoryOptions.FilterPreorderableSkus = false;
pdtConf.InventoryOptions.PropertiesToReturn = propertiesToReturn;
pdtConf.LoadAncestorCategories = false;
pdtConf.LoadCanonicalCategories = false;
pdtConf.LoadParentCategories = false;
pdtConf.LoadRelatedCategories = false;
pdtConf.LoadRelatedProducts = false;
pdtConf.LoadVariants = false;
pdtConf.ParentPropertiesToReturn = "*";
pdtConf.Variants.SearchOptions = new CatalogSearchOptions();
pdtConf.Variants.SearchOptions.PropertiesToReturn = "*";

for (int i = 0; i < 50; i++)
{
     chrono.Start();
     foreach (String pId in Constants.PRODUCTS)
     {
          Product p = ctx.GetProduct(Constants.CATALOGNAME, pId, "en-US", pdtConf);
     }
     chrono.Stop();
     long spent = chrono.ElapsedMilliseconds;
     chrono.Reset();
}
 

Catégories :Commerce Server

[Commerce Server] Trier les propriétés Commerce Server selon des ordres de tri différents

Pour faire suite à une discussion sur le forum Commerce Server: How could i sort by property1 ASC and property2 DESC in a catalog search?, je me suis aperçu qu’une fonctionnalité de base de Commerce Server était mal comprise.

En effet, il n’est pas rare sur une liste de produits (ou de catégorie) de vouloir trier le résultat sur plusieurs critères et dans des sens différents tout en gardant la fonctionnalité de paging du CatalogSearch.

Ravi proposait pour solutionner cela d’utiliser un dataview. Le problème est que le dataview ne peut trier que sur le résultat de la recherche! Quid donc si vous avez plusieurs pages? Sans compter les piètres performances d’un dataview.

Pour solutionner cela, il suffit simplement de mettre le nom de votre propriété entre crochet [] puis de coller à la suite l’ordre du tri ex:

CatalogSearch catalogSearch = catalogContext.GetCatalogSearch();
catalogSearch.CatalogNames = "MyCatalog";
catalogSearch.SearchOptions.ClassTypes = CatalogClassTypes.ProductFamilyClass;
catalogSearch.SearchOptions.PropertiesToReturn = "ProductId, cy_list_price";
catalogSearch.CategoriesClause = "CategoryName = 'CatId";
catalogSearch.SqlWhereClause = "Display = 'OK'";
catalogSearch.SearchOptions.SortProperty = "[ProductId]ASC, [cy_list_price]DESC";

Attention, il est important qu’il n’y ait pas d’espace entre le crochet fermant et l’ordre de tri.

Pour info, notre expert en développement Commerce Server, Anoir, avait déjà écrit un article à ce sujet: Recherche d’un produit avec plusieurs colonnes de tris différenciés sur le blog d’Altima.

Catégories :Commerce Server