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