Skip to content

Commit 1fe10fd

Browse files
committed
Refactor and Enhance Data Handling and Mapping
- Replaced database mocks with actual database implementatino. - Introduced the AutoMap utility class to facilitate simple and efficient mapping of entities with identical properties. -- Updated business logic, repository layers, and database provider implementations.
1 parent cfe2bd0 commit 1fe10fd

File tree

11 files changed

+185
-61
lines changed

11 files changed

+185
-61
lines changed

Poc.TextProcessor.Business.Logic.Abstractions/ITextLogic.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace Poc.TextProcessor.Business.Logic.Abstractions
44
{
55
public interface ITextLogic
66
{
7+
Text Get(int id);
78
Text GetRandom();
89
Statistics GetStatistics(string textContent);
910
}

Poc.TextProcessor.Business.Logic/Poc.TextProcessor.Business.Logic.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="AutoFixture" Version="4.18.1" />
11+
</ItemGroup>
12+
913
<ItemGroup>
1014
<ProjectReference Include="..\Poc.TextProcessor.Business.Logic.Abstractions\Poc.TextProcessor.Business.Logic.Abstractions.csproj" />
1115
<ProjectReference Include="..\Poc.TextProcessor.CrossCutting.Exceptions\Poc.TextProcessor.CrossCutting.Exceptions.csproj" />

Poc.TextProcessor.Business.Logic/TextLogic.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Poc.TextProcessor.Business.Logic.Abstractions;
1+
using AutoFixture;
2+
using Poc.TextProcessor.Business.Logic.Abstractions;
23
using Poc.TextProcessor.Business.Logic.Base;
34
using Poc.TextProcessor.CrossCutting.Utils.Constants;
45
using Poc.TextProcessor.ResourceAccess.Contracts;
@@ -11,6 +12,7 @@ public class TextLogic : TextLogicBase, ITextLogic
1112
{
1213
private readonly ITextRepository _textRepository;
1314
private readonly ITextMapper _textMapper;
15+
private readonly Fixture _fixture = new();
1416

1517
public TextLogic(ITextRepository textRepository,
1618
ITextMapper textMapper)
@@ -19,13 +21,23 @@ public TextLogic(ITextRepository textRepository,
1921
_textMapper = textMapper;
2022
}
2123

24+
public Text Get(int id)
25+
{
26+
var textDomain = _textRepository.Get(id);
27+
return _textMapper.Map(textDomain);
28+
}
29+
2230
public Text GetRandom()
2331
{
24-
var random = new Random();
25-
var randomInteger = random.Next(0, 100);
26-
var textDomain = _textRepository.Get(randomInteger);
32+
Random random = new Random();
33+
var randomLength = random.Next(5, 120);
34+
var longString = string.Join(TextConstants.Space, _fixture.CreateMany<string>(randomLength));
2735

28-
return _textMapper.Map(textDomain);
36+
return _fixture
37+
.Build<Text>()
38+
.With(x => x.Id)
39+
.With(x => x.Content, longString)
40+
.Create();
2941
}
3042

3143
public Statistics GetStatistics(string textContent)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using AutoMapper;
2+
3+
namespace Poc.TextProcessor.CrossCutting.Utils
4+
{
5+
/// <summary>
6+
/// The AutoMap class provides methods for mapping objects of one type to objects of another type.
7+
/// It uses AutoMapper under the hood to handle the actual mapping process. This class abstracts
8+
/// away the complexities of configuring AutoMapper and provides a simple, straightforward interface
9+
/// for object-to-object mapping. It supports both individual objects and collections of objects.
10+
/// </summary>
11+
public static class AutoMap
12+
{
13+
/// <summary>
14+
/// Generates a simple mapping from a source to a destination.
15+
/// </summary>
16+
/// <typeparam name="TSource">Origin source class</typeparam>
17+
/// <typeparam name="TDestination">Destination class</typeparam>
18+
/// <param name="source"></param>
19+
/// <returns></returns>
20+
public static TDestination Map<TSource, TDestination>(TSource source)
21+
{
22+
var mapper = CreateMapper<TSource, TDestination>();
23+
return mapper.Map<TSource, TDestination>(source);
24+
}
25+
26+
/// <summary>
27+
/// Generates a simple mapping from a source collection to a destination collection.
28+
/// </summary>
29+
/// <typeparam name="TSource">Origin source class</typeparam>
30+
/// <typeparam name="TDestination">Destination class</typeparam>
31+
/// <param name="source"></param>
32+
/// <returns></returns>
33+
public static IEnumerable<TDestination> Map<TSource, TDestination>(IEnumerable<TSource> source)
34+
{
35+
if (source == null || !source.Any())
36+
return new List<TDestination>();
37+
38+
var mapper = CreateMapper<TSource, TDestination>();
39+
var destinations = source.Select(x => mapper.Map<TSource, TDestination>(x));
40+
return destinations;
41+
}
42+
43+
private static IMapper CreateMapper<TSource, TDestination>()
44+
{
45+
var configuration = SetupConfiguration<TSource, TDestination>();
46+
var mapper = configuration.CreateMapper();
47+
return mapper;
48+
}
49+
50+
private static MapperConfiguration SetupConfiguration<TSource, TDestination>()
51+
=> new(cfg => cfg.CreateMap<TSource, TDestination>());
52+
}
53+
}

Poc.TextProcessor.CrossCutting.Utils/Poc.TextProcessor.CrossCutting.Utils.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="AutoMapper" Version="12.0.1" />
11+
</ItemGroup>
12+
913
</Project>
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
namespace Poc.TextProcessor.ResourceAccess.Database.Abstractions
1+
using System.Linq.Expressions;
2+
3+
namespace Poc.TextProcessor.ResourceAccess.Database.Abstractions
24
{
35
public interface IDatabaseProvider
46
{
7+
IEnumerable<T> Query<T>(string sql, object parameters = null) where T : class;
58
Task<IEnumerable<T>> QueryAsync<T>(string sql, object parameters = null) where T : class;
9+
int Execute(string sql, object parameters = null);
610
Task<int> ExecuteAsync(string sql, object parameters = null);
11+
IEnumerable<T> Get<T>(Expression<Func<T, bool>> predicate) where T : class;
12+
IEnumerable<T> Get<T>() where T : class;
13+
Task<IEnumerable<T>> GetAsync<T>(Expression<Func<T, bool>> predicate) where T : class;
14+
Task<IEnumerable<T>> GetAsync<T>() where T : class;
15+
T Save<T>(T entity) where T : class;
716
Task<T> SaveAsync<T>(T entity) where T : class;
817
}
918
}
Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.EntityFrameworkCore;
22
using Poc.TextProcessor.ResourceAccess.Database.Abstractions;
3+
using System.Linq.Expressions;
34

45
namespace Poc.TextProcessor.ResourceAccess.Database.Providers.EntityFramework
56
{
@@ -12,39 +13,82 @@ public EntityFrameworkDatabaseProvider(PocContext context)
1213
_context = context;
1314
}
1415

15-
public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object parameters = null)
16-
where T : class
16+
public IEnumerable<T> Query<T>(string sql, object parameters = null) where T : class
17+
{
18+
return _context.Set<T>().FromSqlRaw(sql, parameters).ToList();
19+
}
20+
21+
public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object parameters = null) where T : class
1722
{
1823
return await _context.Set<T>().FromSqlRaw(sql, parameters).ToListAsync();
1924
}
2025

26+
public int Execute(string sql, object parameters = null)
27+
{
28+
return _context.Database.ExecuteSqlRaw(sql, parameters);
29+
}
30+
2131
public async Task<int> ExecuteAsync(string sql, object parameters = null)
2232
{
23-
var result = await _context.Database.ExecuteSqlRawAsync(sql, parameters);
24-
return result;
33+
return await _context.Database.ExecuteSqlRawAsync(sql, parameters);
34+
}
35+
36+
public IEnumerable<T> Get<T>(Expression<Func<T, bool>> predicate) where T : class
37+
{
38+
return _context.Set<T>().Where(predicate);
39+
}
40+
41+
public IEnumerable<T> Get<T>() where T : class
42+
{
43+
return _context.Set<T>();
44+
}
45+
46+
public async Task<IEnumerable<T>> GetAsync<T>() where T : class
47+
{
48+
return await _context.Set<T>().ToListAsync();
49+
}
50+
51+
public async Task<IEnumerable<T>> GetAsync<T>(Expression<Func<T, bool>> predicate) where T : class
52+
{
53+
return await _context.Set<T>().Where(predicate).ToListAsync();
54+
}
55+
56+
public T Save<T>(T entity) where T : class
57+
{
58+
AddOrUpdate(entity);
59+
_context.SaveChanges();
60+
return entity;
2561
}
2662

2763
public async Task<T> SaveAsync<T>(T entity) where T : class
2864
{
29-
var dbSet = _context.Set<T>();
30-
var entry = _context.Entry(entity);
65+
await AddOrUpdateAsync(entity);
66+
await _context.SaveChangesAsync();
67+
return entity;
68+
}
3169

70+
private void AddOrUpdate<T>(T entity) where T : class
71+
{
72+
var entry = _context.Entry(entity);
3273
if (entry.State == EntityState.Detached)
3374
{
34-
dbSet.Attach(entity);
75+
_context.Set<T>().Attach(entity);
3576
}
77+
entry.State = entry.IsKeySet ? EntityState.Modified : EntityState.Added;
78+
}
3679

37-
if (entry.IsKeySet)
80+
private async Task AddOrUpdateAsync<T>(T entity) where T : class
81+
{
82+
var entry = _context.Entry(entity);
83+
if (entry.State == EntityState.Detached)
3884
{
39-
entry.State = EntityState.Modified;
85+
_context.Set<T>().Attach(entity);
4086
}
41-
else
87+
entry.State = entry.IsKeySet ? EntityState.Modified : EntityState.Added;
88+
if (entry.State == EntityState.Added)
4289
{
43-
await dbSet.AddAsync(entity);
90+
await _context.Set<T>().AddAsync(entity);
4491
}
45-
46-
await _context.SaveChangesAsync();
47-
return entity;
4892
}
4993
}
50-
}
94+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Poc.TextProcessor.ResourceAccess.Database.Abstractions;
2+
3+
namespace Poc.TextProcessor.ResourceAccess.Repositories.Base
4+
{
5+
public class RepositoryBase
6+
{
7+
protected readonly IDatabaseProvider _databaseProvider;
8+
9+
public RepositoryBase(IDatabaseProvider databaseProvider)
10+
{
11+
_databaseProvider = databaseProvider;
12+
}
13+
}
14+
}

Poc.TextProcessor.ResourceAccess.Repositories/Poc.TextProcessor.ResourceAccess.Repositories.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
88

9-
<ItemGroup>
10-
<PackageReference Include="AutoFixture" Version="4.18.1" />
11-
</ItemGroup>
12-
139
<ItemGroup>
1410
<ProjectReference Include="..\Poc.TextProcessor.CrossCutting.Utils\Poc.TextProcessor.CrossCutting.Utils.csproj" />
11+
<ProjectReference Include="..\Poc.TextProcessor.ResourceAccess.Database\Poc.TextProcessor.ResourceAccess.Database.csproj" />
1512
<ProjectReference Include="..\Poc.TextProcessor.ResourceAccess.Repositories.Abstractions\Poc.TextProcessor.ResourceAccess.Repositories.Abstractions.csproj" />
1613
</ItemGroup>
1714

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,22 @@
1-
using AutoFixture;
2-
using Poc.TextProcessor.CrossCutting.Utils.Constants;
1+
using Poc.TextProcessor.CrossCutting.Utils;
2+
using Poc.TextProcessor.ResourceAccess.Database.Abstractions;
3+
using Poc.TextProcessor.ResourceAccess.Domains;
4+
using Poc.TextProcessor.ResourceAccess.Entities;
35
using Poc.TextProcessor.ResourceAccess.Repositories.Abstractions;
6+
using Poc.TextProcessor.ResourceAccess.Repositories.Base;
47

58
namespace Poc.TextProcessor.ResourceAccess.Repositories
69
{
7-
public class TextRepository : ITextRepository
10+
public class TextRepository : RepositoryBase, ITextRepository
811
{
9-
// TODO: This class currently uses AutoFixture for mock data generation as a placeholder.
10-
// The actual database implementation is pending. Implement TextProcessorContext for database operations.
11-
// This mock setup is representative for the purposes of this exercise.
12-
13-
private readonly Fixture _fixture = new Fixture();
14-
15-
public Domains.Text Get(int id)
12+
public TextRepository(IDatabaseProvider databaseProvider) : base(databaseProvider)
1613
{
17-
// Generating random text to test the application functionality.
18-
// A random number is created to represent the length of the text, ranging from 5 to 120 words.
19-
// AutoFixture is then used to generate that number of random strings (words),
20-
// and they are joined using a space as a separator to form a long string simulating a text.
21-
Random random = new Random();
22-
var randomLength = random.Next(5, 120);
23-
var longString = string.Join(TextConstants.Space, _fixture.CreateMany<string>(randomLength));
14+
}
2415

25-
return _fixture
26-
.Build<Domains.Text>()
27-
.With(x => x.Id, id)
28-
.With(x => x.Content, longString)
29-
.Create();
16+
public Text Get(int id)
17+
{
18+
var text = _databaseProvider.Get<TextEntity>(x => x.Id == id).Single();
19+
return AutoMap.Map<TextEntity, Text>(text);
3020
}
3121
}
3222
}
Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
1-
using Poc.TextProcessor.CrossCutting.Enums;
1+
using Poc.TextProcessor.CrossCutting.Utils;
2+
using Poc.TextProcessor.ResourceAccess.Database.Abstractions;
23
using Poc.TextProcessor.ResourceAccess.Domains;
4+
using Poc.TextProcessor.ResourceAccess.Entities;
35
using Poc.TextProcessor.ResourceAccess.Repositories.Abstractions;
6+
using Poc.TextProcessor.ResourceAccess.Repositories.Base;
47

58
namespace Poc.TextProcessor.ResourceAccess.Repositories
69
{
7-
public class TextSortRepository : ITextSortRepository
10+
public class TextSortRepository : RepositoryBase, ITextSortRepository
811
{
9-
// TODO: Currently, this method generates a list of sort options directly from the SortOption enum.
10-
// This approach serves as a representative example for the technical exercise.
11-
// In a real-world scenario, this data might come from a database or a configuration file.
12+
public TextSortRepository(IDatabaseProvider databaseProvider) : base(databaseProvider)
13+
{
14+
}
1215

1316
public IEnumerable<TextSort> List()
1417
{
15-
return Enum
16-
.GetValues(typeof(SortOption))
17-
.Cast<SortOption>()
18-
.Select((option, index) => new TextSort
19-
{
20-
Id = index,
21-
Option = option
22-
})
23-
.ToList();
18+
var textSorts = _databaseProvider.Get<TextSortEntity>().ToList();
19+
return AutoMap.Map<TextSortEntity, TextSort>(textSorts);
2420
}
2521
}
2622
}

0 commit comments

Comments
 (0)