Skip to content

Commit fe8e8ff

Browse files
committed
Reduce database round trips initiated by AreProductAttributesEqual
1 parent 4e6cc3c commit fe8e8ff

9 files changed

Lines changed: 114 additions & 150 deletions

File tree

src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder)
5151
"Minimal realisierbarer Preis");
5252

5353
builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.PriceDisplayType.PreSelectedPrice",
54-
"Preselected price",
55-
"Vorgewählter Preis");
54+
"Price preselected on detail page",
55+
"Auf der Detailseite vorgewählter Preis");
5656

5757
builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.PriceDisplayType.PriceWithoutDiscountsAndAttributes",
5858
"Price without discounts and attributes",

src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ public partial interface IProductAttributeParser
2525
/// <returns>Selected product variant attributes</returns>
2626
IList<ProductVariantAttribute> ParseProductVariantAttributes(string attributes);
2727

28-
/// <summary>
29-
/// Gets selected product variant attributes
30-
/// </summary>
31-
/// <param name="ids">The attribute ids</param>
32-
/// <returns>Selected product variant attributes</returns>
33-
IEnumerable<ProductVariantAttribute> ParseProductVariantAttributes(ICollection<int> ids);
34-
3528
/// <summary>
3629
/// Get product variant attribute values
3730
/// </summary>
@@ -59,19 +52,29 @@ public partial interface IProductAttributeParser
5952
/// <summary>
6053
/// Are attributes equal
6154
/// </summary>
62-
/// <param name="attributes1">The attributes of the first product</param>
63-
/// <param name="attributes2">The attributes of the second product</param>
55+
/// <param name="attributeXml1">The attributes of the first product</param>
56+
/// <param name="attributeXml2">The attributes of the second product</param>
57+
/// <param name="attributes">Collection of already loaded product attribute mappings to reduce database rountrips</param>
6458
/// <returns>Result</returns>
65-
bool AreProductAttributesEqual(string attributes1, string attributes2);
59+
bool AreProductAttributesEqual(string attributeXml1, string attributeXml2, IEnumerable<ProductVariantAttribute> attributes = null);
6660

6761
/// <summary>
6862
/// Finds a product variant attribute combination by attributes stored in XML
6963
/// </summary>
7064
/// <param name="product">Product</param>
7165
/// <param name="attributesXml">Attributes in XML format</param>
66+
/// <param name="attributes">Collection of already loaded product attribute mappings to reduce database rountrips</param>
7267
/// <returns>Found product variant attribute combination</returns>
73-
ProductVariantAttributeCombination FindProductVariantAttributeCombination(Product product, string attributesXml);
74-
ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml);
68+
ProductVariantAttributeCombination FindProductVariantAttributeCombination(Product product, string attributesXml, IEnumerable<ProductVariantAttribute> attributes = null);
69+
70+
/// <summary>
71+
/// Finds a product variant attribute combination by attributes stored in XML
72+
/// </summary>
73+
/// <param name="productId">Product identifier</param>
74+
/// <param name="attributesXml">Attributes in XML format</param>
75+
/// <param name="attributes">Collection of already loaded product attribute mappings to reduce database rountrips</param>
76+
/// <returns>Found product variant attribute combination</returns>
77+
ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml, IEnumerable<ProductVariantAttribute> attributes = null);
7578

7679
/// <summary>
7780
/// Deserializes attribute data from an URL query string

src/Libraries/SmartStore.Services/Catalog/IProductAttributeService.cs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,14 @@ public partial interface IProductAttributeService
7474
/// <returns>Product variant attribute mapping</returns>
7575
ProductVariantAttribute GetProductVariantAttributeById(int productVariantAttributeId);
7676

77-
/// <summary>
78-
/// Gets multiple product variant attribute mappings by their keys
79-
/// </summary>
80-
/// <param name="ids">a list of keys</param>
81-
/// <returns>Product variant attribute mappings</returns>
82-
IEnumerable<ProductVariantAttribute> GetProductVariantAttributesByIds(params int[] ids);
77+
78+
/// <summary>
79+
/// Gets a product variant attribute mappings
80+
/// </summary>
81+
/// <param name="productVariantAttributeIds">Enumerable of product variant attribute mapping identifiers</param>
82+
/// <param name="attributes">Collection of already loaded product attribute mappings to reduce database rountrips</param>
83+
/// <returns>List of product variant attribute mapping</returns>
84+
IList<ProductVariantAttribute> GetProductVariantAttributesByIds(IEnumerable<int> productVariantAttributeIds, IEnumerable<ProductVariantAttribute> attributes = null);
8385

8486
/// <summary>
8587
/// Inserts a product variant attribute mapping
@@ -136,13 +138,6 @@ public partial interface IProductAttributeService
136138
/// <param name="productVariantAttributeValue">The product variant attribute value</param>
137139
void UpdateProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue);
138140

139-
/// <summary>
140-
/// Gets a list of product identifiers which have price adjustments
141-
/// </summary>
142-
/// <param name="productIds">Array of product identifiers</param>
143-
/// <returns>List of product identifiers</returns>
144-
IList<int> GetProductIdsWithPriceAdjustments(int[] productIds);
145-
146141
#endregion
147142

148143
#region Product variant attribute combinations (ProductVariantAttributeCombination)

src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ protected virtual decimal GetPreselectedPrice(Product product, PriceCalculationC
214214

215215
var combinations = context.AttributeCombinations.Ensure(product.Id);
216216

217-
var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml));
217+
var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml, attributes));
218218

219219
if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue)
220220
{

src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs

Lines changed: 58 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using System;
2-
using System.Linq;
32
using System.Collections.Generic;
43
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Web;
56
using System.Xml;
6-
using SmartStore.Core.Domain.Catalog;
7-
using SmartStore.Collections;
87
using Newtonsoft.Json;
9-
using System.Web;
8+
using SmartStore.Collections;
9+
using SmartStore.Core.Domain.Catalog;
1010

1111
namespace SmartStore.Services.Catalog
1212
{
@@ -104,33 +104,9 @@ public virtual Multimap<int, string> DeserializeProductVariantAttributes(string
104104
/// <returns>Selected product variant attributes</returns>
105105
public virtual IList<ProductVariantAttribute> ParseProductVariantAttributes(string attributes)
106106
{
107-
var pvaCollection = new List<ProductVariantAttribute>();
108-
109-
// codehint: sm-edit
110107
var ids = ParseProductVariantAttributeIds(attributes);
111-
return this.ParseProductVariantAttributes(ids.ToList()).ToList();
112-
}
113-
114-
public virtual IEnumerable<ProductVariantAttribute> ParseProductVariantAttributes(ICollection<int> ids)
115-
{
116108

117-
if (ids != null)
118-
{
119-
if (ids.Count == 1)
120-
{
121-
var pva = _productAttributeService.GetProductVariantAttributeById(ids.ElementAt(0));
122-
if (pva != null)
123-
{
124-
return new ProductVariantAttribute[] { pva };
125-
}
126-
}
127-
else
128-
{
129-
return _productAttributeService.GetProductVariantAttributesByIds(ids.ToArray()).ToList();
130-
}
131-
}
132-
133-
return Enumerable.Empty<ProductVariantAttribute>();
109+
return _productAttributeService.GetProductVariantAttributesByIds(ids);
134110
}
135111

136112
/// <summary>
@@ -143,17 +119,20 @@ public virtual IEnumerable<ProductVariantAttributeValue> ParseProductVariantAttr
143119
var pvaValues = Enumerable.Empty<ProductVariantAttributeValue>();
144120

145121
var attrs = DeserializeProductVariantAttributes(attributes);
146-
var pvaCollection = ParseProductVariantAttributes(attrs.Keys);
122+
var pvaCollection = _productAttributeService.GetProductVariantAttributesByIds(attrs.Keys);
147123

148124
foreach (var pva in pvaCollection)
149125
{
150126
if (!pva.ShouldHaveValues())
151127
continue;
152128

153-
var pvaValuesStr = attrs[pva.Id]; //ParseValues(attributes, pva.Id);
154-
var ids = from id in pvaValuesStr
155-
where id.HasValue()
156-
select id.ToInt();
129+
var pvaValuesStr = attrs[pva.Id];
130+
131+
var ids =
132+
from id in pvaValuesStr
133+
where id.HasValue()
134+
select id.ToInt();
135+
157136
var values = _productAttributeService.GetProductVariantAttributeValuesByIds(ids.ToArray());
158137

159138
pvaValues = pvaValues.Concat(values);
@@ -218,82 +197,72 @@ public virtual string AddProductAttribute(string attributes, ProductVariantAttri
218197
return pva.AddProductAttribute(attributes, value);
219198
}
220199

221-
/// <summary>
222-
/// Are attributes equal
223-
/// </summary>
224-
/// <param name="attributes1">The attributes of the first product</param>
225-
/// <param name="attributes2">The attributes of the second product</param>
226-
/// <returns>Result</returns>
227-
public virtual bool AreProductAttributesEqual(string attributes1, string attributes2)
200+
public virtual bool AreProductAttributesEqual(string attributeXml1, string attributeXml2, IEnumerable<ProductVariantAttribute> attributes = null)
228201
{
229-
var attrs1 = DeserializeProductVariantAttributes(attributes1);
230-
var attrs2 = DeserializeProductVariantAttributes(attributes2);
202+
if (attributeXml1.IsCaseInsensitiveEqual(attributeXml2))
203+
return true;
231204

232-
if (attrs1.Count == attrs2.Count)
233-
{
234-
var pva1Collection = ParseProductVariantAttributes(attrs2.Keys);
235-
var pva2Collection = ParseProductVariantAttributes(attrs1.Keys);
236-
foreach (var pva1 in pva1Collection)
237-
{
238-
foreach (var pva2 in pva2Collection)
239-
{
240-
if (pva1.Id == pva2.Id)
241-
{
242-
var pvaValues1Str = attrs2[pva1.Id]; // ParseValues(attributes2, pva1.Id);
243-
var pvaValues2Str = attrs1[pva2.Id]; // ParseValues(attributes1, pva2.Id);
244-
if (pvaValues1Str.Count == pvaValues2Str.Count)
245-
{
246-
foreach (string str1 in pvaValues1Str)
247-
{
248-
bool hasAttribute = pvaValues2Str.Any(x => x.IsCaseInsensitiveEqual(str1));
249-
if (!hasAttribute)
250-
{
251-
return false;
252-
}
253-
}
254-
}
255-
else
256-
{
257-
return false;
258-
}
259-
}
260-
}
261-
}
262-
}
263-
else
264-
{
265-
return false;
266-
}
205+
var attributes1 = DeserializeProductVariantAttributes(attributeXml1);
206+
var attributes2 = DeserializeProductVariantAttributes(attributeXml2);
207+
208+
if (attributes1.Count != attributes2.Count)
209+
return false;
210+
211+
IEnumerable<ProductVariantAttribute> pvaCollection1 = null;
212+
IEnumerable<ProductVariantAttribute> pvaCollection2 = null;
213+
214+
pvaCollection1 = _productAttributeService.GetProductVariantAttributesByIds(attributes2.Keys, attributes);
215+
216+
if (attributes2.Keys.SequenceEqual(attributes1.Keys)) // often the case
217+
pvaCollection2 = pvaCollection1;
218+
else
219+
pvaCollection2 = _productAttributeService.GetProductVariantAttributesByIds(attributes1.Keys, attributes);
220+
221+
foreach (var pva1 in pvaCollection1)
222+
{
223+
foreach (var pva2 in pvaCollection2)
224+
{
225+
if (pva1.Id == pva2.Id)
226+
{
227+
var pvaValues1 = attributes2[pva1.Id];
228+
var pvaValues2 = attributes1[pva2.Id];
229+
230+
if (pvaValues1.Count != pvaValues2.Count)
231+
return false;
232+
233+
foreach (string value1 in pvaValues1)
234+
{
235+
string str1 = value1.TrimSafe();
236+
237+
if (!pvaValues2.Any(x => x.TrimSafe().IsCaseInsensitiveEqual(str1)))
238+
return false;
239+
}
240+
}
241+
}
242+
}
267243

268244
return true;
269245
}
270246

271-
/// <summary>
272-
/// Finds a product variant attribute combination by attributes stored in XML
273-
/// </summary>
274-
/// <param name="product">Product</param>
275-
/// <param name="attributesXml">Attributes in XML format</param>
276-
/// <returns>Found product variant attribute combination</returns>
277-
public virtual ProductVariantAttributeCombination FindProductVariantAttributeCombination(Product product, string attributesXml)
247+
public virtual ProductVariantAttributeCombination FindProductVariantAttributeCombination(Product product, string attributesXml, IEnumerable<ProductVariantAttribute> attributes = null)
278248
{
279249
if (product == null)
280250
throw new ArgumentNullException("product");
281251

282-
return FindProductVariantAttributeCombination(product.Id, attributesXml);
252+
return FindProductVariantAttributeCombination(product.Id, attributesXml, attributes);
283253
}
284254

285-
public virtual ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml)
255+
public virtual ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml, IEnumerable<ProductVariantAttribute> attributes = null)
286256
{
287257
if (attributesXml.HasValue())
288258
{
289-
//existing combinations
290259
var combinations = _productAttributeService.GetAllProductVariantAttributeCombinations(productId);
291260
if (combinations.Count == 0)
292261
return null;
293262

294263
foreach (var combination in combinations)
295264
{
296-
bool attributesEqual = AreProductAttributesEqual(combination.AttributesXml, attributesXml);
265+
bool attributesEqual = AreProductAttributesEqual(combination.AttributesXml, attributesXml, attributes);
297266
if (attributesEqual)
298267
return combination;
299268
}

src/Libraries/SmartStore.Services/Catalog/ProductAttributeService.cs

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -210,20 +210,36 @@ public virtual ProductVariantAttribute GetProductVariantAttributeById(int produc
210210
return null;
211211

212212
string key = string.Format(PRODUCTVARIANTATTRIBUTES_BY_ID_KEY, productVariantAttributeId);
213-
return _cacheManager.Get(key, () => {
213+
214+
return _cacheManager.Get(key, () =>
215+
{
214216
return _productVariantAttributeRepository.GetById(productVariantAttributeId);
215217
});
216218
}
217219

218-
public virtual IEnumerable<ProductVariantAttribute> GetProductVariantAttributesByIds(params int[] ids)
219-
{
220-
if (ids == null || ids.Length == 0)
221-
{
222-
return Enumerable.Empty<ProductVariantAttribute>();
223-
}
220+
public virtual IList<ProductVariantAttribute> GetProductVariantAttributesByIds(IEnumerable<int> productVariantAttributeIds, IEnumerable<ProductVariantAttribute> attributes = null)
221+
{
222+
var result = new List<ProductVariantAttribute>();
224223

225-
return _productVariantAttributeRepository.GetMany(ids);
226-
}
224+
if (productVariantAttributeIds != null)
225+
{
226+
foreach (var id in productVariantAttributeIds)
227+
{
228+
ProductVariantAttribute pva = null;
229+
230+
if (attributes != null)
231+
pva = attributes.FirstOrDefault(x => x.Id == id);
232+
233+
if (pva == null)
234+
pva = GetProductVariantAttributeById(id);
235+
236+
if (pva != null)
237+
result.Add(pva);
238+
}
239+
}
240+
241+
return result;
242+
}
227243

228244
public virtual IEnumerable<ProductVariantAttributeValue> GetProductVariantAttributeValuesByIds(params int[] productVariantAttributeValueIds)
229245
{
@@ -340,25 +356,6 @@ public virtual void UpdateProductVariantAttributeValue(ProductVariantAttributeVa
340356
_eventPublisher.EntityUpdated(productVariantAttributeValue);
341357
}
342358

343-
public virtual IList<int> GetProductIdsWithPriceAdjustments(int[] productIds)
344-
{
345-
var queryMappings =
346-
from m in _productVariantAttributeRepository.TableUntracked
347-
where productIds.Contains(m.ProductId)
348-
select new { m.Id, m.ProductId };
349-
350-
var query =
351-
from v in _productVariantAttributeValueRepository.TableUntracked
352-
join m in queryMappings on v.ProductVariantAttributeId equals m.Id
353-
where v.PriceAdjustment != 0
354-
group v by m.ProductId into grp
355-
select grp.Key;
356-
357-
var result = query.ToList();
358-
359-
return result;
360-
}
361-
362359
#endregion
363360

364361
#region Product variant attribute combinations (ProductVariantAttributeCombination)

src/Libraries/SmartStore.Services/Orders/ShoppingCartService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ public virtual IList<string> GetShoppingCartItemAttributeWarnings(Customer custo
548548
{
549549
var combination = product
550550
.ProductVariantAttributeCombinations
551-
.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, selectedAttributes));
551+
.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, selectedAttributes, pva1Collection));
552552

553553
if (combination != null && !combination.IsActive)
554554
{

0 commit comments

Comments
 (0)