Skip to content

Commit 7b1592d

Browse files
committed
Get and Find select before query, even in mongodb
1 parent c7d74d7 commit 7b1592d

File tree

12 files changed

+136
-33
lines changed

12 files changed

+136
-33
lines changed

SharpRepository.EfCoreRepository/EfCoreRepositoryBase.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ protected override void UpdateItem(T entity)
5555
{
5656
if (entry.State == EntityState.Detached)
5757
{
58-
TKey key;
5958

60-
if (GetPrimaryKey(entity, out key))
59+
if (GetPrimaryKey(entity, out TKey key))
6160
{
6261
// check to see if this item is already attached
6362
// if it is then we need to copy the values to the attached value instead of changing the State to modified since it will throw a duplicate key exception
@@ -93,7 +92,7 @@ protected override IQueryable<T> BaseQuery(IFetchStrategy<T> fetchStrategy = nul
9392
}
9493

9594
// we override the implementation fro LinqBaseRepository becausee this is built in and doesn't need to find the key column and do dynamic expressions, etc.
96-
// this also provides the EF5 first level caching out of the box
95+
// this also provides the EF5 first level caching out of the box
9796
protected override T GetQuery(TKey key, IFetchStrategy<T> fetchStrategy)
9897
{
9998
return fetchStrategy == null ? DbSet.Find(key) : base.GetQuery(key, fetchStrategy);

SharpRepository.MongoDbRepository/MongoDbRepositoryBase.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,20 @@ protected override T GetQuery(TKey key, IFetchStrategy<T> fetchStrategy)
144144
else return default(T);
145145
}
146146

147+
protected override TResult GetQuery<TResult>(TKey key, IFetchStrategy<T> fetchStrategy, Expression<Func<T, TResult>> selector)
148+
{
149+
var keyBsonType = ((StringSerializer)BsonClassMap.LookupClassMap(typeof(T)).IdMemberMap.GetSerializer()).Representation;
150+
var keyMemberName = BsonClassMap.LookupClassMap(typeof(T)).IdMemberMap.MemberName;
151+
if (IsValidKey(key))
152+
{
153+
var keyBsonValue = BsonTypeMapper.MapToBsonValue(key, keyBsonType);
154+
var filter = Builders<T>.Filter.Eq(keyMemberName, keyBsonValue);
155+
return BaseCollection().Find(filter).Project(selector).FirstOrDefault();
156+
}
157+
else return default(TResult);
158+
}
159+
160+
147161
#region Math
148162
public override int Sum(ISpecification<T> criteria, Expression<Func<T, int>> selector)
149163
{

SharpRepository.Repository/Caching/CachingStrategyBase.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,34 @@ public virtual void SaveGetResult(TKey key, T item)
7070
}
7171
}
7272

73+
public bool TryGetResult<TResult>(TKey key, Expression<Func<T, TResult>> selector, out TResult result)
74+
{
75+
result = default(TResult);
76+
try
77+
{
78+
return IsInCache(GetWriteThroughCacheKey(key, selector), out result);
79+
}
80+
catch (Exception)
81+
{
82+
// don't let an error trying to find results stop everything, it should continue and then just go get the results from the DB itself
83+
return false;
84+
}
85+
}
86+
87+
public void SaveGetResult<TResult>(TKey key, Expression<Func<T, TResult>> selector, TResult item)
88+
{
89+
try
90+
{
91+
SetCache(GetWriteThroughCacheKey(key, selector), item);
92+
}
93+
catch (Exception)
94+
{
95+
// don't let an error saving cache stop everything else
96+
}
97+
}
98+
99+
100+
73101
public virtual bool TryGetAllResult<TResult>(IQueryOptions<T> queryOptions, Expression<Func<T, TResult>> selector, out IEnumerable<TResult> result)
74102
{
75103
result = null;
@@ -469,6 +497,12 @@ protected string GetWriteThroughCacheKey(TKey key)
469497
return String.Format("{0}/{1}/{2}", FullCachePrefix, TypeFullName, key);
470498
}
471499

500+
protected string GetWriteThroughCacheKey<TResult>(TKey key, Expression<Func<T, TResult>> selector)
501+
{
502+
return String.Format("{0}/{1}/{2}/{3}", FullCachePrefix, TypeFullName, key, Md5Helper.CalculateMd5("Get::" + (selector != null ? selector.ToString() : "null")));
503+
}
504+
505+
472506
protected virtual string GetAllCacheKey<TResult>(IQueryOptions<T> queryOptions, Expression<Func<T, TResult>> selector)
473507
{
474508
return String.Format("{0}/{1}/{2}", FullCachePrefix, TypeFullName, Md5Helper.CalculateMd5("All::" + (queryOptions != null ? queryOptions.ToString() : "null") + "::" + (selector != null ? selector.ToString() : "null")));

SharpRepository.Repository/Caching/ICachingStrategy.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ public interface ICachingStrategy<T, TKey>
1212
bool TryGetResult(TKey key, out T result);
1313
void SaveGetResult(TKey key, T result);
1414

15+
bool TryGetResult<TResult>(TKey key, Expression<Func<T, TResult>> selector, out TResult result);
16+
void SaveGetResult<TResult>(TKey key, Expression<Func<T, TResult>> selector, TResult result);
17+
1518
bool TryGetAllResult<TResult>(IQueryOptions<T> queryOptions, Expression<Func<T, TResult>> selector, out IEnumerable<TResult> result);
1619
void SaveGetAllResult<TResult>(IQueryOptions<T> queryOptions, Expression<Func<T, TResult>> selector, IEnumerable<TResult> result);
1720

SharpRepository.Repository/Caching/NoCachingStrategyBase.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ public void SaveGetResult(TKey key, T result)
2424

2525
}
2626

27+
public bool TryGetResult<TResult>(TKey key, Expression<Func<T, TResult>> selector, out TResult result)
28+
{
29+
result = default(TResult);
30+
return false;
31+
}
32+
33+
public void SaveGetResult<TResult>(TKey key, Expression<Func<T, TResult>> selector, TResult result)
34+
{
35+
36+
}
37+
2738
public bool TryGetAllResult<TResult>(IQueryOptions<T> queryOptions, Expression<Func<T, TResult>> selector, out IEnumerable<TResult> result)
2839
{
2940
result = default(IEnumerable<TResult>);

SharpRepository.Repository/LinqRepositoryBase.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ protected override T GetQuery(TKey key, IFetchStrategy<T> fetchStrategy)
2626
return FindQuery(ByPrimaryKeySpecification(key, fetchStrategy));
2727
}
2828

29+
protected override TResult GetQuery<TResult>(TKey key, IFetchStrategy<T> fetchStrategy, Expression<Func<T, TResult>> selector)
30+
{
31+
return FindQuery(ByPrimaryKeySpecification(key, fetchStrategy), selector);
32+
}
33+
2934
protected override T FindQuery(ISpecification<T> criteria)
3035
{
3136
var query = BaseQuery(criteria.FetchStrategy);
@@ -35,6 +40,16 @@ protected override T FindQuery(ISpecification<T> criteria)
3540
return criteria.SatisfyingEntityFrom(query);
3641
}
3742

43+
protected TResult FindQuery<TResult>(ISpecification<T> criteria, Expression<Func<T, TResult>> selector)
44+
{
45+
var query = BaseQuery(criteria.FetchStrategy);
46+
47+
SetTraceInfo("Find", query);
48+
49+
return criteria.SatisfyingEntitiesFrom(query).Select(selector).FirstOrDefault();
50+
}
51+
52+
3853
protected override T FindQuery(ISpecification<T> criteria, IQueryOptions<T> queryOptions)
3954
{
4055
if (queryOptions == null)
@@ -46,6 +61,19 @@ protected override T FindQuery(ISpecification<T> criteria, IQueryOptions<T> quer
4661

4762
return criteria.SatisfyingEntityFrom(query);
4863
}
64+
65+
protected override TResult FindQuery<TResult>(ISpecification<T> criteria, Expression<Func<T, TResult>> selector, IQueryOptions<T> queryOptions)
66+
{
67+
if (queryOptions == null)
68+
return FindQuery(criteria, selector);
69+
70+
var query = queryOptions.Apply(BaseQuery(criteria.FetchStrategy));
71+
72+
SetTraceInfo("Find", query);
73+
74+
return criteria.SatisfyingEntitiesFrom(query).Select(selector).FirstOrDefault();
75+
}
76+
4977

5078
protected override IQueryable<T> GetAllQuery(IFetchStrategy<T> fetchStrategy)
5179
{

SharpRepository.Repository/Queries/QueryManager.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,23 @@ public T ExecuteGet(Func<T> query, TKey key)
4444
return result;
4545
}
4646

47+
public TResult ExecuteGet<TResult>(Func<TResult> query, TKey key, Expression<Func<T, TResult>> selector)
48+
{
49+
if (CacheEnabled && _cachingStrategy.TryGetResult(key, selector, out TResult result))
50+
{
51+
CacheUsed = true;
52+
return result;
53+
}
54+
55+
CacheUsed = false;
56+
57+
result = query.Invoke();
58+
59+
_cachingStrategy.SaveGetResult(key, selector, result);
60+
61+
return result;
62+
}
63+
4764
public IEnumerable<TResult> ExecuteGetAll<TResult>(Func<IEnumerable<TResult>> query, Expression<Func<T, TResult>> selector, IQueryOptions<T> queryOptions)
4865
{
4966
if (CacheEnabled && _cachingStrategy.TryGetAllResult(queryOptions, selector, out IEnumerable<TResult> result))

SharpRepository.Repository/RepositoryBase.cs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,12 @@ public IEnumerable<TResult> GetAll<TResult>(Expression<Func<T, TResult>> selecto
309309
{
310310
return GetAll(selector, queryOptions, RepositoryHelper.BuildFetchStrategy(includePaths));
311311
}
312-
313-
314-
312+
315313
// These are the actual implementation that the derived class needs to implement
316314
protected abstract T GetQuery(TKey key, IFetchStrategy<T> fetchStrategy);
317315

316+
protected abstract TResult GetQuery<TResult>(TKey key, IFetchStrategy<T> fetchStrategy, Expression<Func<T, TResult>> selector);
317+
318318
public abstract IRepositoryQueryable<TResult> Join<TJoinKey, TInner, TResult>(IRepositoryQueryable<TInner> innerRepository, Expression<Func<T, TJoinKey>> outerKeySelector, Expression<Func<TInner, TJoinKey>> innerKeySelector, Expression<Func<T, TInner, TResult>> resultSelector)
319319
where TInner : class
320320
where TResult : class;
@@ -392,16 +392,12 @@ public TResult Get<TResult>(TKey key, Expression<Func<T, TResult>> selector, IFe
392392

393393
// get the full entity, possibly from cache
394394
var result = QueryManager.ExecuteGet(
395-
() => GetQuery(context.Id, fetchStrategy),
396-
context.Id
395+
() => GetQuery(context.Id, fetchStrategy, selector),
396+
context.Id,
397+
selector
397398
);
398-
399-
// return the entity with the selector applied to it
400-
var selectedResult = result == null
401-
? default(TResult)
402-
: new[] { result }.AsQueryable().Select(selector).First();
403-
404-
context.Result = selectedResult;
399+
400+
context.Result = result;
405401
RunAspect(attribute => attribute.OnGetExecuted(context));
406402

407403
return context.Result;
@@ -589,6 +585,7 @@ public IEnumerable<TResult> FindAll<TResult>(Expression<Func<T, bool>> predicate
589585
// These are the actual implementation that the derived class needs to implement
590586
protected abstract T FindQuery(ISpecification<T> criteria);
591587
protected abstract T FindQuery(ISpecification<T> criteria, IQueryOptions<T> queryOptions);
588+
protected abstract TResult FindQuery<TResult>(ISpecification<T> criteria, Expression<Func<T, TResult>> selector, IQueryOptions<T> queryOptions);
592589

593590
public T Find(ISpecification<T> criteria, IQueryOptions<T> queryOptions = null)
594591
{
@@ -633,12 +630,7 @@ public TResult Find<TResult>(ISpecification<T> criteria, Expression<Func<T, TRes
633630
var item = QueryManager.ExecuteFind(
634631
() =>
635632
{
636-
var result = FindQuery(context.Specification, context.QueryOptions);
637-
if (result == null)
638-
return default(TResult);
639-
640-
var results = new[] { result };
641-
return results.AsQueryable().Select(context.Selector).First();
633+
return FindQuery(context.Specification, context.Selector, context.QueryOptions);
642634
},
643635

644636
context.Specification,

SharpRepository.Samples.CoreMvc/SharpRepository.Samples.CoreMvc.csproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.0</TargetFramework>
4+
<TargetFramework>netcoreapp2.1</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
9-
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" />
8+
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.5" />
9+
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.5" />
1010
</ItemGroup>
1111

1212
<ItemGroup>
1313
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
14-
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
1514
</ItemGroup>
1615

1716
<ItemGroup>

SharpRepository.Tests.Integration/Spikes/StandardCachingSpikes.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ public void Get_With_Selector_Should_Not_Use_Cache_If_Entity_Updated()
113113

114114
const string contactId = "1";
115115

116-
var contactName = repository.Get(contactId, c => c.Name);
117-
contactName.ShouldBe("Contact 1");
118-
119116
var contact = repository.Get(contactId);
120-
contact.Name = "Contact 1 - EDITED";
117+
contact.Name.ShouldBe("Contact 1");
121118

122-
contactName = repository.Get(contactId, c => c.Name);
119+
var contact2 = repository.Get(contactId);
120+
contact2.Name = "Contact 1 - EDITED";
121+
122+
var contactName = repository.Get(contactId, c => c.Name);
123123
contactName.ShouldBe("Contact 1 - EDITED");
124124
}
125125

0 commit comments

Comments
 (0)