diff --git a/.gitignore b/.gitignore index 1e6cfd2..314e089 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,14 @@ ApplicationHub/bin ApplicationHub/obj ApplicationHub/*.db +ApplicationHub/*.db-shm +ApplicationHub/*.db-wal ApplicationHub.Data.EF/bin ApplicationHub.Data.EF/obj ApplicationHub.Data.EF/*.db +ApplicationHub.Data.EF/*.db-shm +ApplicationHub.Data.EF/*.db-wal ApplicationHub.Data.InMemory/bin ApplicationHub.Data.InMemory/obj diff --git a/ApplicationHub.Data.EF/ApplicationHub.Data.EF.csproj b/ApplicationHub.Data.EF/ApplicationHub.Data.EF.csproj index a174a13..f8afb37 100644 --- a/ApplicationHub.Data.EF/ApplicationHub.Data.EF.csproj +++ b/ApplicationHub.Data.EF/ApplicationHub.Data.EF.csproj @@ -21,4 +21,8 @@ + + + + diff --git a/ApplicationHub.Data.EF/Authentication/Repositories/GroupRepository.cs b/ApplicationHub.Data.EF/Authentication/Repositories/GroupRepository.cs new file mode 100644 index 0000000..58f4bf9 --- /dev/null +++ b/ApplicationHub.Data.EF/Authentication/Repositories/GroupRepository.cs @@ -0,0 +1,8 @@ +using ApplicationHub.Data.EF.Authentication.Entities; +using ApplicationHub.Data.EF.Utils; +using ApplicationHub.Domain.Contracts.Authentication.Models; +using AutoMapper; + +namespace ApplicationHub.Data.EF.Authentication.Repositories; + +public class GroupRepository(AuthenticationDataContext authenticationDataContext, IMapper mapper) : BaseRepository(authenticationDataContext.Groups, mapper); \ No newline at end of file diff --git a/ApplicationHub.Data.EF/Authentication/Repositories/RightRepository.cs b/ApplicationHub.Data.EF/Authentication/Repositories/RightRepository.cs new file mode 100644 index 0000000..0606d53 --- /dev/null +++ b/ApplicationHub.Data.EF/Authentication/Repositories/RightRepository.cs @@ -0,0 +1,8 @@ +using ApplicationHub.Data.EF.Authentication.Entities; +using ApplicationHub.Data.EF.Utils; +using ApplicationHub.Domain.Contracts.Authentication.Models; +using AutoMapper; + +namespace ApplicationHub.Data.EF.Authentication.Repositories; + +public class RightRepository(AuthenticationDataContext authenticationDataContext, IMapper mapper) : BaseRepository(authenticationDataContext.Rights, mapper); \ No newline at end of file diff --git a/ApplicationHub.Data.EF/Authentication/Repositories/UserRepository.cs b/ApplicationHub.Data.EF/Authentication/Repositories/UserRepository.cs index e1d868a..7efd598 100644 --- a/ApplicationHub.Data.EF/Authentication/Repositories/UserRepository.cs +++ b/ApplicationHub.Data.EF/Authentication/Repositories/UserRepository.cs @@ -1,20 +1,8 @@ -using ApplicationHub.Domain.Contracts.Authentication; +using ApplicationHub.Data.EF.Authentication.Entities; +using ApplicationHub.Data.EF.Utils; using ApplicationHub.Domain.Contracts.Authentication.Models; using AutoMapper; -using Microsoft.EntityFrameworkCore; namespace ApplicationHub.Data.EF.Authentication.Repositories; -public class UserRepository(AuthenticationDataContext authenticationDataContext, IMapper mapper) : IUserRepository -{ - public User? GetUserByUserName(string userName) - { - return mapper.Map>( - authenticationDataContext.Users - .Include(x => x.Groups) - .Where(x => x.UserName == userName && x.Active) - .ToList() - ) - .FirstOrDefault(); - } -} \ No newline at end of file +public class UserRepository(AuthenticationDataContext authenticationDataContext, IMapper mapper) : BaseRepository(authenticationDataContext.Users, mapper); \ No newline at end of file diff --git a/ApplicationHub.Data.EF/Utils/BaseRepository.cs b/ApplicationHub.Data.EF/Utils/BaseRepository.cs new file mode 100644 index 0000000..3bb0b4e --- /dev/null +++ b/ApplicationHub.Data.EF/Utils/BaseRepository.cs @@ -0,0 +1,16 @@ +using System.Linq.Expressions; +using ApplicationHub.Domain.Contracts; +using AutoMapper; +using Microsoft.EntityFrameworkCore; + +namespace ApplicationHub.Data.EF.Utils; + +public class BaseRepository(DbSet dbSet, IMapper mapper) : IRepository where TEntity : class +{ + public IEnumerable Find(Expression>? where = null, int? limit = null, int? offset = null, List>, OrderDirection>>? order = null) + { + var resolver = new QueryResolver(); + var result = resolver.Find(dbSet, where, limit, offset, order).ToList(); + return mapper.Map>(result); + } +} \ No newline at end of file diff --git a/ApplicationHub.Data.EF/Utils/QueryResolver.cs b/ApplicationHub.Data.EF/Utils/QueryResolver.cs new file mode 100644 index 0000000..9f0ad2e --- /dev/null +++ b/ApplicationHub.Data.EF/Utils/QueryResolver.cs @@ -0,0 +1,57 @@ +using System.Linq.Expressions; +using ApplicationHub.Domain.Contracts; +using ApplicationHub.Domain.Contracts.Linq; +using Microsoft.EntityFrameworkCore; + +namespace ApplicationHub.Data.EF.Utils; + +public class QueryResolver where TEntity : class +{ + public IQueryable Find(DbSet dbSet, Expression>? where = null, int? limit = null, int? offset = null, List>, OrderDirection>>? order = null) + { + IQueryable? expression = null; + if (where != null) + { + var whereConverter = new EntityExpressionConverter(where.Parameters.Single()); + expression = dbSet.Where(whereConverter.Convert(where)); + } + + if (offset.HasValue) + { + expression = expression == null + ? dbSet.Skip(offset.Value) + : expression.Skip(offset.Value); + } + + if (limit.HasValue) + { + expression = expression == null + ? dbSet.Take(limit.Value) + : expression.Take(limit.Value); + } + + if (order != null && order.Any()) + { + foreach (var orderDescription in order) + { + var orderConverter = new EntityExpressionConverter(orderDescription.Key.Parameters.Single()); + var convertedOrder = orderConverter.Convert(orderDescription.Key); + switch (orderDescription.Value) + { + case OrderDirection.Asc: + expression = expression == null + ? dbSet.OrderBy(convertedOrder) + : expression.OrderBy(convertedOrder); + break; + case OrderDirection.Desc: + expression = expression == null + ? dbSet.OrderByDescending(convertedOrder) + : expression.OrderByDescending(convertedOrder); + break; + } + } + } + + return expression ?? dbSet.Where(x => true); + } +} \ No newline at end of file diff --git a/ApplicationHub.Data.InMemory/Authentication/UserDataContext.cs b/ApplicationHub.Data.InMemory/Authentication/UserDataContext.cs index 53fab88..84d5148 100644 --- a/ApplicationHub.Data.InMemory/Authentication/UserDataContext.cs +++ b/ApplicationHub.Data.InMemory/Authentication/UserDataContext.cs @@ -1,10 +1,11 @@ -using ApplicationHub.Domain.Contracts.Authentication; +using System.Linq.Expressions; +using ApplicationHub.Domain.Contracts; using ApplicationHub.Domain.Contracts.Authentication.Models; using AutoMapper; namespace ApplicationHub.Data.InMemory.Authentication; -public class UserDataContext(IMapper mapper) : IUserRepository +public class UserDataContext(IMapper mapper) : IRepository { public User? GetUserByUserName(string userName) { @@ -13,5 +14,10 @@ public class UserDataContext(IMapper mapper) : IUserRepository .ToList(); return mapper.Map>(result).FirstOrDefault(); } + + public IEnumerable Find(Expression>? where = null, int? limit = null, int? offset = null, List>, OrderDirection>>? order = null) + { + throw new NotImplementedException(); + } } diff --git a/ApplicationHub.Domain.Contracts/Authentication/IUserRepository.cs b/ApplicationHub.Domain.Contracts/Authentication/IUserRepository.cs deleted file mode 100644 index 2a2e3dc..0000000 --- a/ApplicationHub.Domain.Contracts/Authentication/IUserRepository.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ApplicationHub.Domain.Contracts.Authentication.Models; - -namespace ApplicationHub.Domain.Contracts.Authentication; - -public interface IUserRepository -{ - public User? GetUserByUserName(string userName); -} \ No newline at end of file diff --git a/ApplicationHub.Domain.Contracts/IRepository.cs b/ApplicationHub.Domain.Contracts/IRepository.cs new file mode 100644 index 0000000..8d13552 --- /dev/null +++ b/ApplicationHub.Domain.Contracts/IRepository.cs @@ -0,0 +1,8 @@ +using System.Linq.Expressions; + +namespace ApplicationHub.Domain.Contracts; + +public interface IRepository +{ + IEnumerable Find(Expression>? where = null, int? limit = null, int? offset = null, List>, OrderDirection>>? order = null); +} \ No newline at end of file diff --git a/ApplicationHub.Domain.Contracts/Linq/EntityExpressionConverter.cs b/ApplicationHub.Domain.Contracts/Linq/EntityExpressionConverter.cs new file mode 100644 index 0000000..1dc6e2a --- /dev/null +++ b/ApplicationHub.Domain.Contracts/Linq/EntityExpressionConverter.cs @@ -0,0 +1,36 @@ +using System.Linq.Expressions; + +namespace ApplicationHub.Domain.Contracts.Linq; + +public class EntityExpressionConverter : ExpressionVisitor +{ + private readonly ParameterExpression _entityParameter; + private readonly ParameterExpression _modelParameter; + + public EntityExpressionConverter(ParameterExpression modelParameter) + { + _modelParameter = modelParameter; + _entityParameter = Expression.Parameter(typeof(TEntity), "entity"); + } + + public Expression> Convert(Expression> expression) + { + var body = Visit(expression.Body); + return Expression.Lambda>(body, _entityParameter); + } + + protected override Expression VisitParameter(ParameterExpression node) + { + return node == _modelParameter ? _entityParameter : base.VisitParameter(node); + } + + protected override Expression VisitMember(MemberExpression node) + { + if (node.Expression == _modelParameter) + { + return Expression.Property(_entityParameter, node.Member.Name); + } + + return base.VisitMember(node); + } +} \ No newline at end of file diff --git a/ApplicationHub.Domain.Contracts/OrderDirection.cs b/ApplicationHub.Domain.Contracts/OrderDirection.cs new file mode 100644 index 0000000..a330792 --- /dev/null +++ b/ApplicationHub.Domain.Contracts/OrderDirection.cs @@ -0,0 +1,7 @@ +namespace ApplicationHub.Domain.Contracts; + +public enum OrderDirection +{ + Asc, + Desc +} \ No newline at end of file diff --git a/ApplicationHub.Domain/Authentication/Services/AuthenticationService.cs b/ApplicationHub.Domain/Authentication/Services/AuthenticationService.cs index 7fa04fd..3799b61 100644 --- a/ApplicationHub.Domain/Authentication/Services/AuthenticationService.cs +++ b/ApplicationHub.Domain/Authentication/Services/AuthenticationService.cs @@ -1,12 +1,12 @@ -using ApplicationHub.Domain.Contracts.Authentication; +using ApplicationHub.Domain.Contracts; using ApplicationHub.Domain.Contracts.Authentication.Models; namespace Adapter.Authentication.Services; -public class AuthenticationService(IUserRepository userRepository) +public class AuthenticationService(IRepository userRepository) { public User? GetUserByUserName(string userName) { - return userRepository.GetUserByUserName(userName); + return userRepository.Find(x => x.UserName == userName && x.Active).FirstOrDefault(); } } \ No newline at end of file diff --git a/ApplicationHub/Configuration/AdapterConfiguration.cs b/ApplicationHub/Configuration/AdapterConfiguration.cs index f0129f6..17201ea 100644 --- a/ApplicationHub/Configuration/AdapterConfiguration.cs +++ b/ApplicationHub/Configuration/AdapterConfiguration.cs @@ -1,8 +1,7 @@ using ApplicationHub.Data.EF.Authentication.Entities; using ApplicationHub.Data.EF.Authentication.Repositories; -using ApplicationHub.Data.InMemory.Authentication; -using ApplicationHub.Data.InMemory.Authentication.Entities; -using ApplicationHub.Domain.Contracts.Authentication; +using ApplicationHub.Domain.Contracts; +using ApplicationHub.Domain.Contracts.Authentication.Models; namespace ApplicationHub.Configuration; @@ -16,7 +15,9 @@ public static class AdapterConfiguration { // use EF implementation builder.Services.AddAutoMapper(typeof(UserEntity)); - builder.Services.AddScoped(); + builder.Services.AddScoped, UserRepository>(); + builder.Services.AddScoped, GroupRepository>(); + builder.Services.AddScoped, RightRepository>(); // use InMemory implementation // builder.Services.AddScoped(); diff --git a/README.md b/README.md index e69de29..89c9e5a 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +# Application Hub + +A Web Application to manage multiple REST Applications and call it. \ No newline at end of file