Skip to content

Commit 2dda8b3

Browse files
committed
(Perf) Operating on large sets of AttributeCombinations (> 10.000) MUCH faster now
1 parent 53fd0cc commit 2dda8b3

23 files changed

Lines changed: 459 additions & 246 deletions

src/Libraries/SmartStore.Core/Caching/AspNetCache.cs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,6 @@ public partial class AspNetCache : ICache
1313
{
1414
private const string REGION_NAME = "$$SmartStoreNET$$";
1515

16-
// AspNetCache object does not have a ContainsKey() method:
17-
// Therefore we put a special string into cache if value is null,
18-
// otherwise our 'Contains()' would always return false,
19-
// which is bad if we intentionally wanted to save NULL values.
20-
private const string FAKE_NULL = "__[NULL]__";
21-
2216
public IEnumerable<KeyValuePair<string, object>> Entries
2317
{
2418
get
@@ -40,12 +34,7 @@ public object Get(string key)
4034
if (HttpRuntime.Cache == null)
4135
return null;
4236

43-
var value = HttpRuntime.Cache.Get(BuildKey(key));
44-
45-
if (value.Equals(FAKE_NULL))
46-
return null;
47-
48-
return value;
37+
return HttpRuntime.Cache.Get(BuildKey(key));
4938
}
5039

5140
public void Set(string key, object value, int? cacheTime)
@@ -62,7 +51,7 @@ public void Set(string key, object value, int? cacheTime)
6251
absoluteExpiration = DateTime.UtcNow + TimeSpan.FromMinutes(cacheTime.Value);
6352
}
6453

65-
HttpRuntime.Cache.Insert(key, value ?? FAKE_NULL, null, absoluteExpiration, Cache.NoSlidingExpiration);
54+
HttpRuntime.Cache.Insert(key, value, null, absoluteExpiration, Cache.NoSlidingExpiration);
6655
}
6756

6857
public bool Contains(string key)

src/Libraries/SmartStore.Core/Caching/DefaultCacheManager.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ public partial class CacheManager<TCache> : ICacheManager where TCache : ICache
2121
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
2222
private readonly ICache _cache;
2323

24-
public CacheManager(Func<Type, ICache> fn)
24+
// Wwe put a special string into cache if value is null,
25+
// otherwise our 'Contains()' would always return false,
26+
// which is bad if we intentionally wanted to save NULL values.
27+
private const string FAKE_NULL = "__[NULL]__";
28+
29+
public CacheManager(Func<Type, ICache> fn)
2530
{
2631
this._cache = fn(typeof(TCache));
2732
}
@@ -32,7 +37,7 @@ public T Get<T>(string key, Func<T> acquirer, int? cacheTime = null)
3237

3338
if (_cache.Contains(key))
3439
{
35-
return (T)_cache.Get(key);
40+
return GetExisting<T>(key);
3641
}
3742

3843
using (EnterReadLock())
@@ -46,19 +51,26 @@ public T Get<T>(string key, Func<T> acquirer, int? cacheTime = null)
4651
}
4752
}
4853

54+
return GetExisting<T>(key);
55+
}
56+
57+
private T GetExisting<T>(string key)
58+
{
59+
var value = _cache.Get(key);
60+
61+
if (value.Equals(FAKE_NULL))
62+
return default(T);
63+
4964
return (T)_cache.Get(key);
50-
}
65+
}
5166

5267
public void Set(string key, object value, int? cacheTime = null)
5368
{
5469
Guard.ArgumentNotEmpty(() => key);
55-
56-
if (value == null)
57-
return;
5870

5971
using (EnterWriteLock())
6072
{
61-
_cache.Set(key, value, cacheTime);
73+
_cache.Set(key, value ?? FAKE_NULL, cacheTime);
6274
}
6375
}
6476

src/Libraries/SmartStore.Core/Caching/RequestCache.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,10 @@ public void Set(string key, object value, int? cacheTime)
6464

6565
key = BuildKey(key);
6666

67-
if (value != null)
68-
{
69-
if (items.Contains(key))
70-
items[key] = value;
71-
else
72-
items.Add(key, value);
73-
}
67+
if (items.Contains(key))
68+
items[key] = value;
69+
else
70+
items.Add(key, value);
7471
}
7572

7673
public bool Contains(string key)

src/Libraries/SmartStore.Core/Data/DbContextScope.cs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
using System;
22
using SmartStore.Core.Infrastructure;
3+
using System.Linq.Expressions;
4+
using System.Collections.Generic;
5+
using System.Linq;
36

47
namespace SmartStore.Core.Data
58
{
69
public class DbContextScope : IDisposable
710
{
8-
private readonly bool _autoDetectChangesEnabled;
11+
private readonly IDbContext _ctx;
12+
private readonly bool _autoDetectChangesEnabled;
913
private readonly bool _proxyCreationEnabled;
1014
private readonly bool _validateOnSaveEnabled;
1115
private readonly bool _forceNoTracking;
1216
private readonly bool _hooksEnabled;
1317
private readonly bool _autoCommit;
14-
private readonly IDbContext _ctx;
18+
private readonly bool _lazyLoading;
19+
1520

1621
public DbContextScope(IDbContext ctx = null,
1722
bool? autoDetectChanges = null,
1823
bool? proxyCreation = null,
1924
bool? validateOnSave = null,
2025
bool? forceNoTracking = null,
2126
bool? hooksEnabled = null,
22-
bool? autoCommit = null)
27+
bool? autoCommit = null,
28+
bool? lazyLoading = null)
2329
{
2430
_ctx = ctx ?? EngineContext.Current.Resolve<IDbContext>();
2531
_autoDetectChangesEnabled = _ctx.AutoDetectChangesEnabled;
@@ -28,6 +34,7 @@ public DbContextScope(IDbContext ctx = null,
2834
_forceNoTracking = _ctx.ForceNoTracking;
2935
_hooksEnabled = _ctx.HooksEnabled;
3036
_autoCommit = _ctx.AutoCommitEnabled;
37+
_lazyLoading = _ctx.LazyLoadingEnabled;
3138

3239
if (autoDetectChanges.HasValue)
3340
_ctx.AutoDetectChangesEnabled = autoDetectChanges.Value;
@@ -46,7 +53,36 @@ public DbContextScope(IDbContext ctx = null,
4653

4754
if (autoCommit.HasValue)
4855
_ctx.AutoCommitEnabled = autoCommit.Value;
49-
}
56+
57+
if (lazyLoading.HasValue)
58+
_ctx.LazyLoadingEnabled = lazyLoading.Value;
59+
}
60+
61+
public IDbContext DbContext
62+
{
63+
get { return _ctx; }
64+
}
65+
66+
public void LoadCollection<TEntity, TCollection>(
67+
TEntity entity,
68+
Expression<Func<TEntity, ICollection<TCollection>>> navigationProperty,
69+
bool force = false,
70+
Func<IQueryable<TCollection>, IQueryable<TCollection>> queryAction = null)
71+
where TEntity : BaseEntity
72+
where TCollection : BaseEntity
73+
{
74+
_ctx.LoadCollection(entity, navigationProperty, force, queryAction);
75+
}
76+
77+
public void LoadReference<TEntity, TProperty>(
78+
TEntity entity,
79+
Expression<Func<TEntity, TProperty>> navigationProperty,
80+
bool force = false)
81+
where TEntity : BaseEntity
82+
where TProperty : BaseEntity
83+
{
84+
_ctx.LoadReference(entity, navigationProperty, force);
85+
}
5086

5187
public int Commit()
5288
{
@@ -61,6 +97,7 @@ public void Dispose()
6197
_ctx.ForceNoTracking = _forceNoTracking;
6298
_ctx.HooksEnabled = _hooksEnabled;
6399
_ctx.AutoCommitEnabled = _autoCommit;
100+
_ctx.LazyLoadingEnabled = _lazyLoading;
64101
}
65102

66103
}

src/Libraries/SmartStore.Core/Data/IDbContext.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Data;
34
using System.Data.Common;
45
using System.Data.Entity;
6+
using System.Linq;
7+
using System.Linq.Expressions;
58
using System.Threading.Tasks;
69

710
namespace SmartStore.Core.Data
@@ -42,7 +45,8 @@ IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params ob
4245

4346
// increasing performance on bulk operations
4447
bool ProxyCreationEnabled { get; set; }
45-
bool AutoDetectChangesEnabled { get; set; }
48+
bool LazyLoadingEnabled { get; set; }
49+
bool AutoDetectChangesEnabled { get; set; }
4650
bool ValidateOnSaveEnabled { get; set; }
4751
bool HooksEnabled { get; set; }
4852
bool HasChanges { get; }

src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
using System.Collections.Generic;
33
using SmartStore.Core;
44
using SmartStore.Core.Data;
5+
using System.Linq;
6+
using System.Linq.Expressions;
7+
using System.Data.Entity;
58

69
namespace SmartStore
710
{
@@ -45,5 +48,124 @@ public static bool SetToUnchanged<TEntity>(this IDbContext ctx, TEntity entity)
4548
}
4649
}
4750

51+
public static IQueryable<TCollection> QueryForCollection<TEntity, TCollection>(
52+
this IDbContext ctx,
53+
TEntity entity,
54+
Expression<Func<TEntity, ICollection<TCollection>>> navigationProperty)
55+
where TEntity : BaseEntity
56+
where TCollection : BaseEntity
57+
{
58+
Guard.ArgumentNotNull(() => entity);
59+
Guard.ArgumentNotNull(() => navigationProperty);
60+
61+
var dbContext = ctx as DbContext;
62+
if (dbContext == null)
63+
{
64+
throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)");
65+
}
66+
67+
return dbContext.Entry(entity).Collection(navigationProperty).Query();
68+
}
69+
70+
public static IQueryable<TProperty> QueryForReference<TEntity, TProperty>(
71+
this IDbContext ctx,
72+
TEntity entity,
73+
Expression<Func<TEntity, TProperty>> navigationProperty)
74+
where TEntity : BaseEntity
75+
where TProperty : BaseEntity
76+
{
77+
Guard.ArgumentNotNull(() => entity);
78+
Guard.ArgumentNotNull(() => navigationProperty);
79+
80+
var dbContext = ctx as DbContext;
81+
if (dbContext == null)
82+
{
83+
throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)");
84+
}
85+
86+
return dbContext.Entry(entity).Reference(navigationProperty).Query();
87+
}
88+
89+
public static void LoadCollection<TEntity, TCollection>(
90+
this IDbContext ctx,
91+
TEntity entity,
92+
Expression<Func<TEntity, ICollection<TCollection>>> navigationProperty,
93+
bool force = false,
94+
Func<IQueryable<TCollection>, IQueryable<TCollection>> queryAction = null)
95+
where TEntity : BaseEntity
96+
where TCollection : BaseEntity
97+
{
98+
Guard.ArgumentNotNull(() => entity);
99+
Guard.ArgumentNotNull(() => navigationProperty);
100+
101+
var dbContext = ctx as DbContext;
102+
if (dbContext == null)
103+
{
104+
throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)");
105+
}
106+
107+
var entry = dbContext.Entry(entity);
108+
var collection = entry.Collection(navigationProperty);
109+
110+
if (force)
111+
{
112+
collection.IsLoaded = false;
113+
}
114+
115+
if (!collection.IsLoaded)
116+
{
117+
if (queryAction != null || ctx.ForceNoTracking)
118+
{
119+
var query = !ctx.ForceNoTracking
120+
? collection.Query()
121+
: collection.Query().AsNoTracking();
122+
123+
var myQuery = queryAction != null
124+
? queryAction(query)
125+
: query;
126+
127+
collection.CurrentValue = myQuery.ToList();
128+
}
129+
else
130+
{
131+
collection.Load();
132+
}
133+
134+
collection.IsLoaded = true;
135+
}
136+
}
137+
138+
public static void LoadReference<TEntity, TProperty>(
139+
this IDbContext ctx,
140+
TEntity entity,
141+
Expression<Func<TEntity, TProperty>> navigationProperty,
142+
bool force = false)
143+
where TEntity : BaseEntity
144+
where TProperty : BaseEntity
145+
{
146+
Guard.ArgumentNotNull(() => entity);
147+
Guard.ArgumentNotNull(() => navigationProperty);
148+
149+
var dbContext = ctx as DbContext;
150+
if (dbContext == null)
151+
{
152+
throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)");
153+
}
154+
155+
var entry = dbContext.Entry(entity);
156+
var reference = entry.Reference(navigationProperty);
157+
158+
if (force)
159+
{
160+
reference.IsLoaded = false;
161+
}
162+
163+
if (!reference.IsLoaded)
164+
{
165+
reference.Load();
166+
reference.IsLoaded = true;
167+
}
168+
}
169+
48170
}
49171
}

src/Libraries/SmartStore.Data/ObjectContextBase.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,18 @@ public bool ProxyCreationEnabled
422422
}
423423
}
424424

425+
public bool LazyLoadingEnabled
426+
{
427+
get
428+
{
429+
return this.Configuration.LazyLoadingEnabled;
430+
}
431+
set
432+
{
433+
this.Configuration.LazyLoadingEnabled = value;
434+
}
435+
}
436+
425437
public bool ForceNoTracking { get; set; }
426438

427439
public bool AutoCommitEnabled { get; set; }
@@ -551,7 +563,7 @@ public void ReloadEntity<TEntity>(TEntity entity) where TEntity : BaseEntity
551563
this.Entry(entity).Reload();
552564
}
553565

554-
private string FormatValidationExceptionMessage(IEnumerable<DbEntityValidationResult> results)
566+
private string FormatValidationExceptionMessage(IEnumerable<DbEntityValidationResult> results)
555567
{
556568
var sb = new StringBuilder();
557569
sb.Append("Entity validation failed" + Environment.NewLine);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private void DeleteAllCategories(IList<Category> categories, bool delete)
120120

121121
var childCategories = GetAllCategoriesByParentCategoryId(category.Id, true);
122122
DeleteAllCategories(childCategories, delete);
123-
}
123+
}
124124
}
125125

126126
#endregion

0 commit comments

Comments
 (0)