Skip to content

Commit 0c958da

Browse files
authored
6.4.3 - Service DB Transaction and Any (#89)
* closes #88 * closes #87 * Unit Test for Any methods * OltContextTransactionExtensions Unit Test * unit test/code coverage * code coverage
1 parent 2ba5e6c commit 0c958da

17 files changed

Lines changed: 627 additions & 7 deletions

src/OLT.Core.Services/Services/IOltEntityIdService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ Task<TResponseModel> UpdateAsync<TResponseModel, TSaveModel>(int id, TSaveModel
2424
bool SoftDelete(int id);
2525
Task<bool> SoftDeleteAsync(int id);
2626

27+
bool Any(int id);
28+
Task<bool> AnyAsync(int id);
2729
}
2830
}

src/OLT.Core.Services/Services/IOltEntityService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,10 @@ Task<TResponseModel> UpdateAsync<TResponseModel, TSaveModel>(IOltSearcher<TEntit
7979
int Count(Expression<Func<TEntity, bool>> predicate);
8080
Task<int> CountAsync(IOltSearcher<TEntity> searcher);
8181
Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);
82+
83+
bool Any(IOltSearcher<TEntity> searcher);
84+
bool Any(Expression<Func<TEntity, bool>> predicate);
85+
Task<bool> AnyAsync(IOltSearcher<TEntity> searcher);
86+
Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate);
8287
}
8388
}

src/OLT.Core.Services/Services/IOltEntityUniqueIdService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@ Task<TResponseModel> UpdateAsync<TResponseModel, TSaveModel>(Guid uid, TSaveMode
2323

2424
bool SoftDelete(Guid uid);
2525
Task<bool> SoftDeleteAsync(Guid uid);
26+
27+
bool Any(Guid uid);
28+
Task<bool> AnyAsync(Guid uid);
2629
}
2730
}

src/OLT.EF.Core.Services/OltContextService.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,22 @@ protected virtual async Task<int> CountAsync<TEntity>(IQueryable<TEntity> querya
180180

181181
#endregion
182182

183+
#region [ Any ]
184+
185+
protected virtual bool Any<TEntity>(IQueryable<TEntity> queryable)
186+
where TEntity : class, IOltEntity
187+
{
188+
return queryable.Any();
189+
}
190+
191+
protected virtual async Task<bool> AnyAsync<TEntity>(IQueryable<TEntity> queryable)
192+
where TEntity : class, IOltEntity
193+
{
194+
return await queryable.AnyAsync();
195+
}
196+
197+
#endregion
198+
183199
#region [ Mapping ]
184200

185201
protected virtual IOltPaged<TModel> MapPaged<TEntity, TModel>(IQueryable<TEntity> queryable, IOltPagingParams pagingParams, Func<IQueryable<TEntity>, IQueryable<TEntity>> orderBy = null)
@@ -262,5 +278,51 @@ protected virtual async Task<TModel> MapFirstAsync<TEntity, TModel>(IQueryable<T
262278
}
263279

264280
#endregion
281+
282+
#region [ DB Transaction ]
283+
284+
protected virtual async Task<TResult> WithDbTransactionAsync<TResult>(Func<Task<TResult>> action)
285+
{
286+
if (Context.Database.CurrentTransaction == null)
287+
{
288+
using var transaction = await Context.Database.BeginTransactionAsync();
289+
try
290+
{
291+
return await OltContextTransactionExtensions.CreateSubTransactionAsync(transaction, action);
292+
}
293+
catch (Exception)
294+
{
295+
await transaction.RollbackAsync();
296+
throw;
297+
}
298+
}
299+
else
300+
{
301+
return await OltContextTransactionExtensions.CreateSubTransactionAsync(Context.Database.CurrentTransaction, action);
302+
}
303+
}
304+
305+
protected virtual async Task WithDbTransactionAsync(Func<Task> action)
306+
{
307+
if (Context.Database.CurrentTransaction == null)
308+
{
309+
using var transaction = await Context.Database.BeginTransactionAsync();
310+
try
311+
{
312+
await OltContextTransactionExtensions.CreateSubTransactionAsync(transaction, action);
313+
}
314+
catch (Exception)
315+
{
316+
await transaction.RollbackAsync();
317+
throw;
318+
}
319+
}
320+
else
321+
{
322+
await OltContextTransactionExtensions.CreateSubTransactionAsync(Context.Database.CurrentTransaction, action);
323+
}
324+
}
325+
326+
#endregion
265327
}
266328
}

src/OLT.EF.Core.Services/OltEntityIdService.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections;
1+
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Linq.Expressions;
@@ -33,6 +34,12 @@ protected OltEntityIdService(
3334

3435
#region [ Build Result List ]
3536

37+
/// <summary>
38+
/// Builds result from Adding a list of entities
39+
/// </summary>
40+
/// <typeparam name="TModel"></typeparam>
41+
/// <param name="entities"></param>
42+
/// <returns></returns>
3643
protected override List<TModel> BuildResultList<TModel>(List<TEntity> entities)
3744
{
3845
var returnList = new List<TModel>();
@@ -146,5 +153,18 @@ public virtual async Task<bool> SoftDeleteAsync(int id)
146153

147154
#endregion
148155

156+
#region [ Any ]
157+
158+
public virtual bool Any(int id)
159+
{
160+
return Any(GetQueryable(id));
161+
}
162+
163+
public virtual async Task<bool> AnyAsync(int id)
164+
{
165+
return await AnyAsync(GetQueryable(id));
166+
}
167+
168+
#endregion
149169
}
150170
}

src/OLT.EF.Core.Services/OltEntityService.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ protected virtual async Task<IOltPaged<TModel>> GetPagedAsync<TModel>(IQueryable
190190

191191
#region [ Build Result List ]
192192

193+
/// <summary>
194+
/// Builds result from Adding a list of entities
195+
/// </summary>
196+
/// <typeparam name="TModel"></typeparam>
197+
/// <param name="entities"></param>
198+
/// <returns></returns>
193199
protected virtual List<TModel> BuildResultList<TModel>(List<TEntity> entities) where TModel : class, new()
194200
{
195201
var returnList = new List<TModel>();
@@ -362,7 +368,7 @@ public virtual int Count(IOltSearcher<TEntity> searcher)
362368
return Count(GetQueryable(searcher));
363369
}
364370

365-
public int Count(Expression<Func<TEntity, bool>> predicate)
371+
public virtual int Count(Expression<Func<TEntity, bool>> predicate)
366372
{
367373
return Count(this.GetQueryable().Where(predicate));
368374
}
@@ -372,12 +378,36 @@ public virtual async Task<int> CountAsync(IOltSearcher<TEntity> searcher)
372378
return await CountAsync(GetQueryable(searcher));
373379
}
374380

375-
public async Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate)
381+
public virtual async Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate)
376382
{
377383
return await CountAsync(this.GetQueryable().Where(predicate));
378384
}
379385

380386
#endregion
381387

388+
#region [ Any ]
389+
390+
public virtual bool Any(IOltSearcher<TEntity> searcher)
391+
{
392+
return Any(GetQueryable(searcher));
393+
}
394+
395+
public virtual bool Any(Expression<Func<TEntity, bool>> predicate)
396+
{
397+
return Any(this.GetQueryable().Where(predicate));
398+
}
399+
400+
public virtual async Task<bool> AnyAsync(IOltSearcher<TEntity> searcher)
401+
{
402+
return await AnyAsync(GetQueryable(searcher));
403+
}
404+
405+
public virtual async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate)
406+
{
407+
return await AnyAsync(this.GetQueryable().Where(predicate));
408+
}
409+
410+
#endregion
411+
382412
}
383413
}

src/OLT.EF.Core.Services/OltEntityUniqueIdService.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ protected OltEntityUniqueIdService(
3434

3535
#region [ Build Result List ]
3636

37+
/// <summary>
38+
/// Builds result from Adding a list of entities
39+
/// </summary>
40+
/// <typeparam name="TModel"></typeparam>
41+
/// <param name="entities"></param>
42+
/// <returns></returns>
3743
protected override List<TModel> BuildResultList<TModel>(List<TEntity> entities)
3844
{
3945
var returnList = new List<TModel>();
@@ -147,5 +153,18 @@ public virtual async Task<bool> SoftDeleteAsync(Guid uid)
147153

148154
#endregion
149155

156+
#region [ Any ]
157+
158+
public virtual bool Any(Guid uid)
159+
{
160+
return Any(GetQueryable(uid));
161+
}
162+
163+
public virtual async Task<bool> AnyAsync(Guid uid)
164+
{
165+
return await AnyAsync(GetQueryable(uid));
166+
}
167+
168+
#endregion
150169
}
151170
}

src/OLT.EF.Core/Extensions/OltContextExtensions.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Data;
43
using System.Linq;
5-
using System.Linq.Expressions;
64
using Microsoft.EntityFrameworkCore;
75
using Microsoft.EntityFrameworkCore.Metadata;
8-
using Microsoft.EntityFrameworkCore.Query;
6+
97

108
// ReSharper disable once CheckNamespace
119
namespace OLT.Core
1210
{
11+
1312
public static class OltContextExtensions
1413
{
1514

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Microsoft.EntityFrameworkCore.Storage;
2+
using System.Threading.Tasks;
3+
using System;
4+
5+
namespace OLT.Core
6+
{
7+
public static class OltContextTransactionExtensions
8+
{
9+
public static async Task<TResult> CreateSubTransactionAsync<TResult>(this IDbContextTransaction dbTransaction, Func<Task<TResult>> action)
10+
{
11+
var savePointName = Guid.NewGuid().ToString().Left(32);
12+
try
13+
{
14+
await dbTransaction.CreateSavepointAsync(savePointName);
15+
var result = await action();
16+
await dbTransaction.ReleaseSavepointAsync(savePointName);
17+
return result;
18+
}
19+
catch (Exception)
20+
{
21+
await dbTransaction.RollbackToSavepointAsync(savePointName);
22+
throw;
23+
}
24+
}
25+
26+
public static async Task CreateSubTransactionAsync(this IDbContextTransaction dbTransaction, Func<Task> action)
27+
{
28+
var savePointName = Guid.NewGuid().ToString().Left(32);
29+
try
30+
{
31+
await dbTransaction.CreateSavepointAsync(savePointName);
32+
await action();
33+
await dbTransaction.ReleaseSavepointAsync(savePointName);
34+
}
35+
catch (Exception)
36+
{
37+
await dbTransaction.RollbackToSavepointAsync(savePointName);
38+
throw;
39+
}
40+
}
41+
}
42+
}

tests/OLT.EF.Core.Services.Tests/Assets/Services/ContextService.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public interface IContextService : IOltCoreService
4949

5050
bool Delete<TEntity>(int id) where TEntity : class, IOltEntityId;
5151
Task<bool> DeleteAsync<TEntity>(int id) where TEntity : class, IOltEntityId;
52+
53+
54+
Task DbTransactionAsync(bool throwError);
55+
Task<TModel> DbTransactionAsync<TModel>(bool throwError) where TModel : class, new();
5256
}
5357

5458
public class ContextService : OltContextService<UnitTestContext>, IContextService
@@ -119,5 +123,32 @@ public async Task<bool> DeleteAsync<TEntity>(int id) where TEntity : class, IOlt
119123
var entity = await GetQueryable(new OltSearcherGetById<TEntity>(id)).FirstOrDefaultAsync();
120124
return await MarkDeletedAsync(entity);
121125
}
126+
127+
public async Task DbTransactionAsync(bool throwError)
128+
{
129+
await WithDbTransactionAsync(() =>
130+
{
131+
if (throwError)
132+
{
133+
throw new Exception("TestError");
134+
}
135+
136+
return Task.CompletedTask;
137+
});
138+
}
139+
140+
public async Task<TModel> DbTransactionAsync<TModel>(bool throwError) where TModel : class, new()
141+
{
142+
return await WithDbTransactionAsync<TModel>(() =>
143+
{
144+
if (throwError)
145+
{
146+
throw new Exception("TestError");
147+
}
148+
149+
return Task.FromResult(new TModel());
150+
});
151+
}
152+
122153
}
123154
}

0 commit comments

Comments
 (0)