1 using MyCommerceApplication.Entities;
2
3 namespace MyCommerceApplication
4 {
5
6 /// <summary>
7 /// Provider used to get products from the catalog.
8 /// </summary>
9 public interface IProductProvider
10 {
11 #region Methods
12
13 /// <summary>
14 /// Gets a single product from a Commerce Server catalog.
15 /// </summary>
16 /// <param name="catalogId">The catalog identifier <see cref="String"/> of the catalog that
17 /// the product is to be retrieved from.</param>
18 /// <param name="productId">The product identifier <see cref="String"/> of the product that
19 /// is to be retrieved.</param>
20 /// <param name="cultureName">The name of the culture to use to retrieve culture-specific
21 /// resources.</param>
22 /// <returns>A <see cref="ProductEntityBase"/> if the specified product exists and could be
23 /// retrieved, or <see langword="null"/> if the product doesn't exist or couldn't be
24 /// retrieved.</returns>
25 ProductEntityBase GetProduct(string catalogId, string productId, string cultureName);
26
27 #endregion // Methods
28 }
29
30 }
1 using System;
2 using System.Globalization;
3 using Microsoft.CommerceServer;
4 using Microsoft.CommerceServer.Catalog;
5 using Microsoft.Practices.Unity;
6 using MyCommerceApplication.Entities;
7 using MyCommerceApplication.Mappings;
8
9 namespace MyCommerceApplication
10 {
11
12 /// <summary>
13 /// An <see cref="IProductProvider"/> implementation that gets products from the Commerce
14 /// Server catalog.
15 /// </summary>
16 public class ProductProvider : IProductProvider
17 {
18 #region Properties
19
20 /// <summary>
21 /// Gets or sets the <see cref="ICatalogContextFactory"/> to use to create the catalog
22 /// context.
23 /// </summary>
24 [Dependency]
25 public ICatalogContextFactory CatalogContextFactory { get; set; }
26
27 /// <summary>
28 /// Gets or sets the <see cref="ProductMappingFactory"/> to use to create product mappings.
29 /// </summary>
30 [Dependency]
31 public IProductMappingFactory ProductMappingFactory { get; set; }
32
33 #endregion // Properties
34
35 #region Methods
36
37 /// <summary>
38 /// Gets the specified product from the specified catalog.
39 /// </summary>
40 /// <param name="catalogId">The catalog identifier.</param>
41 /// <param name="productId">The product identifier.</param>
42 /// <returns>A <see cref="ProductEntityBase"/> representing the product.</returns>
43 #region IProductProvider Methods
44
45 /// <summary>
46 /// Gets a single product from a Commerce Server catalog.
47 /// </summary>
48 /// <param name="catalogId">The catalog identifier <see cref="String"/> of the catalog that
49 /// the product is to be retrieved from.</param>
50 /// <param name="productId">The product identifier <see cref="String"/> of the product that
51 /// is to be retrieved.</param>
52 /// <param name="cultureName">The name of the culture to use to retrieve culture-specific
53 /// resources.</param>
54 /// <returns>A <see cref="ProductEntityBase"/> if the specified product exists and could be
55 /// retrieved, or <see langword="null"/> if the product doesn't exist or couldn't be
56 /// retrieved.</returns>
57 public ProductEntityBase GetProduct(string catalogId, string productId, string cultureName)
58 {
59 CatalogContext catalogContext;
60 Product product;
61 IProductMapping mapping;
62 ProductEntityBase entity;
63
64 catalogContext = CatalogContextFactory.CreateCatalogContext();
65 try
66 {
67 product = catalogContext.GetProduct(catalogId, productId, cultureName);
68 }
69 catch (EntityDoesNotExistException)
70 {
71 // If the catalog doesn't exist, throw an exception.
72 try
73 {
74 catalogContext.GetCatalog(catalogId, cultureName);
75 }
76 catch (EntityDoesNotExistException ex)
77 {
78 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Catalog '{0}' does not exist.", catalogId), ex);
79 }
80 // If the product doesn't exist, return null.
81 return null;
82 }
83 // If the product's active language isn't the requested culture, throw an exception.
84 if (!product.ActiveLanguage.Equals(cultureName, StringComparison.OrdinalIgnoreCase))
85 {
86 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Catalog '{0}' does not support culture '{1}', returning culture '{2}' instead.", catalogId, cultureName, product.ActiveLanguage));
87 }
88 try
89 {
90 mapping = ProductMappingFactory.CreateProductMapping(product.DefinitionName);
91 entity = mapping.Map(product);
92 }
93 catch (Exception ex)
94 {
95 // If the mapping fails, either because we couldn't get a mapping for the specified
96 // definition name or because the mapping threw an exception when trying to perform
97 // the map, throw an exception.
98 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Product '{0}' in catalog '{1}' is malformed.", productId, catalogId), ex);
99 }
100 return entity;
101 }
102
103 #endregion // IProductProvider Methods
104
105 #endregion // Methods
106 }
107
108 }
This implementation has a few key improvements over the one in the original post:
1. It takes the culture name as a parameter.
2. It returns null if the product doesn't exist. (It could just as easily throw a different exception or return a special type of product entity indicating that the product doesn't exist.)
3. It throws a meaningful exception if the catalog doesn't exist. (This is nice to have, but it could hurt performance depending on your caching strategy and on how many requests you get for products that don't exist.)
4. It throws a meaningful exception if the product isn't returned with the requested culture.
5. It throws a meaningful exception if the product can't be mapped to a business entity.
It's still a very simple implementation (e.g. we normally put caching here, we normally throw specific exceptions for each error condition, etc), but it's better than the original.