|
|
| Sorry, your entry can't be deleted right now. Please try again later. |
October 26 [Commerce Server] ProductConfiguration / CategoryConfiguration ou les oubliés de Commerce ServerCes 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
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:
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.
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();
}
Trackbacks
The trackback URL for this entry is: http://gaelduhamel.spaces.live.com/blog/cns!A6E8B6350CC655C7!2012.trak Weblogs that reference this entry
|