From 110663456de701cb708cfe74ab91db7137628402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20B=C3=B6rchers?= Date: Sat, 25 Jul 2020 22:15:58 +0200 Subject: [PATCH] reorganization of database layer --- .../DataModels/ModelBuilderExtensions.cs | 73 +++++++++++ .../DataModels/Node.cs | 31 ++--- .../DataModels/UserServiceDbContext.cs | 3 +- .../20200725195658_initial.Designer.cs | 74 +++++++---- .../Migrations/20200725195658_initial.cs | 30 ++++- .../UserServiceDbContextModelSnapshot.cs | 72 ++++++---- .../Repository/BaseRepository.cs | 53 ++++++++ .../Repository/IRepository.cs | 14 +- .../Repository/Repository.cs | 97 ++++++++++++++ .../UserService.DatabaseLayer.csproj | 17 +++ UserService.Test/UnitTest1.cs | 29 +++- UserService.Test/UserService.Test.csproj | 4 +- UserService.db | Bin 40960 -> 40960 bytes UserService.sln | 6 + UserService/Data/WeatherForecast.cs | 15 --- UserService/Data/WeatherForecastService.cs | 25 ---- .../DataModels/ModelBuilderExtensions.cs | 40 ------ UserService/Pages/Counter.razor | 38 ++++-- UserService/Pages/FetchData.razor | 46 ------- UserService/Pages/Index.razor | 8 +- UserService/Repository/IGroupsRepository.cs | 124 ------------------ UserService/Shared/NavMenu.razor | 5 - UserService/Shared/OrgUnitItem.razor | 15 +++ UserService/Startup.cs | 13 +- UserService/UserService.csproj | 9 +- 25 files changed, 471 insertions(+), 370 deletions(-) create mode 100644 UserService.DatabaseLayer/DataModels/ModelBuilderExtensions.cs rename {UserService => UserService.DatabaseLayer}/DataModels/Node.cs (65%) rename {UserService => UserService.DatabaseLayer}/DataModels/UserServiceDbContext.cs (94%) rename UserService/Migrations/20200724180034_initial.Designer.cs => UserService.DatabaseLayer/Migrations/20200725195658_initial.Designer.cs (63%) rename UserService/Migrations/20200724180034_initial.cs => UserService.DatabaseLayer/Migrations/20200725195658_initial.cs (77%) rename {UserService => UserService.DatabaseLayer}/Migrations/UserServiceDbContextModelSnapshot.cs (63%) create mode 100644 UserService.DatabaseLayer/Repository/BaseRepository.cs rename {UserService => UserService.DatabaseLayer}/Repository/IRepository.cs (62%) create mode 100644 UserService.DatabaseLayer/Repository/Repository.cs create mode 100644 UserService.DatabaseLayer/UserService.DatabaseLayer.csproj delete mode 100644 UserService/Data/WeatherForecast.cs delete mode 100644 UserService/Data/WeatherForecastService.cs delete mode 100644 UserService/DataModels/ModelBuilderExtensions.cs delete mode 100644 UserService/Pages/FetchData.razor delete mode 100644 UserService/Repository/IGroupsRepository.cs create mode 100644 UserService/Shared/OrgUnitItem.razor diff --git a/UserService.DatabaseLayer/DataModels/ModelBuilderExtensions.cs b/UserService.DatabaseLayer/DataModels/ModelBuilderExtensions.cs new file mode 100644 index 0000000..d06cd66 --- /dev/null +++ b/UserService.DatabaseLayer/DataModels/ModelBuilderExtensions.cs @@ -0,0 +1,73 @@ +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; + +namespace UserService.DatabaseLayer.DataModels +{ + public static class ModelBuilderExtensions + { + public static void Seed(this ModelBuilder modelBuilder) + { + var groups = new OrganizationUnit { CommonName = "Groups", Id = -1 }; + var users = new OrganizationUnit { CommonName = "Users", Id = -2 }; + var germany = new OrganizationUnit{CommonName = "Germany", Id = -6, ParentId = -2}; + var usa = new OrganizationUnit{CommonName = "USA", Id = -5, ParentId = -2}; + var arizona = new OrganizationUnit{CommonName = "Arizona" , Id = -4, ParentId = -5 }; + var france = new OrganizationUnit{CommonName = "France" , Id = -3, ParentId = -2 }; + modelBuilder.Entity().HasData(users, groups, germany, usa, arizona, france); + var user = new User { CommonName = "holger", IsActive = true, Id = -7, ParentId = germany.Id }; + modelBuilder.Entity().HasData(user); + var secGroup = new SecurityGroup { CommonName = "Global Admin", Id = -8, ParentId = groups.Id }; + modelBuilder.Entity().HasData(secGroup); + + modelBuilder.Entity() + .HasData(new UserMember { MemberId = secGroup.Id, UserId = user.Id }); + } + + public static void CreateRelations(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(bc => new { bc.MemberId, bc.UserId }); + modelBuilder.Entity() + .HasOne(bc => bc.User) + .WithMany(b => b!.MemberOf) + .HasForeignKey(bc => bc.UserId); + modelBuilder.Entity() + .HasOne(bc => bc.Member) + .WithMany(c => c!.Members) + .HasForeignKey(bc => bc.MemberId); + + modelBuilder.Entity() + .HasMany(c => c.Children) + .WithOne(e => e.Parent!) + .HasForeignKey(bc => bc.ParentId); + } + } + + public static class UserExtensions + { + public static IEnumerable GetSecurityGroups(this User user) + { + foreach (var userMember in user.MemberOf) + { + if (userMember.Member is SecurityGroup securityGroup) + { + yield return securityGroup; + } + } + } + + } + + public static class SecurityGroupExtensions + { + public static IEnumerable GetUsers(this SecurityGroup securityGroup) + { + foreach (var userMember in securityGroup.Members) + { + if (userMember.User is null) continue; + yield return userMember.User; + } + } + + } +} \ No newline at end of file diff --git a/UserService/DataModels/Node.cs b/UserService.DatabaseLayer/DataModels/Node.cs similarity index 65% rename from UserService/DataModels/Node.cs rename to UserService.DatabaseLayer/DataModels/Node.cs index 4348481..cc43df7 100644 --- a/UserService/DataModels/Node.cs +++ b/UserService.DatabaseLayer/DataModels/Node.cs @@ -1,7 +1,8 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace UserService.DataModels +namespace UserService.DatabaseLayer.DataModels { public class OrganizationUnit : Node { @@ -23,24 +24,7 @@ namespace UserService.DataModels public IEnumerable MemberOf { get; set; } = new List(); - public User Clone() - { - return new User - { - Children = Children, - CommonName = CommonName, - Description = Description, - EMail = EMail, - FirstName = FirstName, - Id = Id, - IsActive = IsActive, - LastName = LastName, - MemberOf = MemberOf, - Members = Members, - Parent = Parent, - ParentId = ParentId - }; - } + public User Clone() => (User)MemberwiseClone(); } public class UserMember @@ -64,10 +48,15 @@ namespace UserService.DataModels public abstract class Node { public int Id { get; set; } - [Required] public string CommonName { get; set; } = "commonName"; + [Required] public string CommonName { get; set; } = null!; public string? Description { get; set; } public ICollection Children { get; set; } = new List(); public Node? Parent { get; set; } //Parent public int? ParentId { get; set; } + + public override string ToString() + { + return $"[{GetType().Name}] {Id:D5} {CommonName}"; + } } } \ No newline at end of file diff --git a/UserService/DataModels/UserServiceDbContext.cs b/UserService.DatabaseLayer/DataModels/UserServiceDbContext.cs similarity index 94% rename from UserService/DataModels/UserServiceDbContext.cs rename to UserService.DatabaseLayer/DataModels/UserServiceDbContext.cs index c7a356c..68c5567 100644 --- a/UserService/DataModels/UserServiceDbContext.cs +++ b/UserService.DatabaseLayer/DataModels/UserServiceDbContext.cs @@ -1,13 +1,12 @@ using Microsoft.EntityFrameworkCore; -namespace UserService.DataModels +namespace UserService.DatabaseLayer.DataModels { public class UserServiceDbContext : DbContext { public DbSet Users { get; set; } = null!; public DbSet SecurityGroups { get; set; } = null!; public DbSet UserMembers { get; set; } = null!; - public DbSet OrganizationUnits { get; set; } = null!; protected override void OnConfiguring(DbContextOptionsBuilder options) diff --git a/UserService/Migrations/20200724180034_initial.Designer.cs b/UserService.DatabaseLayer/Migrations/20200725195658_initial.Designer.cs similarity index 63% rename from UserService/Migrations/20200724180034_initial.Designer.cs rename to UserService.DatabaseLayer/Migrations/20200725195658_initial.Designer.cs index 56f4115..56a7dcf 100644 --- a/UserService/Migrations/20200724180034_initial.Designer.cs +++ b/UserService.DatabaseLayer/Migrations/20200725195658_initial.Designer.cs @@ -4,12 +4,12 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using UserService.DataModels; +using UserService.DatabaseLayer.DataModels; -namespace UserService.Migrations +namespace UserService.DatabaseLayer.Migrations { [DbContext(typeof(UserServiceDbContext))] - [Migration("20200724180034_initial")] + [Migration("20200725195658_initial")] partial class initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -18,7 +18,7 @@ namespace UserService.Migrations modelBuilder .HasAnnotation("ProductVersion", "3.1.6"); - modelBuilder.Entity("UserService.DataModels.Node", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.Node", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -47,7 +47,7 @@ namespace UserService.Migrations b.HasDiscriminator("Discriminator").HasValue("Node"); }); - modelBuilder.Entity("UserService.DataModels.UserMember", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.UserMember", b => { b.Property("MemberId") .HasColumnType("INTEGER"); @@ -64,14 +64,14 @@ namespace UserService.Migrations b.HasData( new { - MemberId = -3, - UserId = -4 + MemberId = -8, + UserId = -7 }); }); - modelBuilder.Entity("UserService.DataModels.Member", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.Member", b => { - b.HasBaseType("UserService.DataModels.Node"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Node"); b.Property("EMail") .HasColumnType("TEXT"); @@ -79,9 +79,9 @@ namespace UserService.Migrations b.HasDiscriminator().HasValue("Member"); }); - modelBuilder.Entity("UserService.DataModels.OrganizationUnit", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.OrganizationUnit", b => { - b.HasBaseType("UserService.DataModels.Node"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Node"); b.Property("ManagerId") .HasColumnType("INTEGER"); @@ -100,27 +100,51 @@ namespace UserService.Migrations { Id = -1, CommonName = "Groups" + }, + new + { + Id = -6, + CommonName = "Germany", + ParentId = -2 + }, + new + { + Id = -5, + CommonName = "USA", + ParentId = -2 + }, + new + { + Id = -4, + CommonName = "Arizona", + ParentId = -5 + }, + new + { + Id = -3, + CommonName = "France", + ParentId = -2 }); }); - modelBuilder.Entity("UserService.DataModels.SecurityGroup", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.SecurityGroup", b => { - b.HasBaseType("UserService.DataModels.Member"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Member"); b.HasDiscriminator().HasValue("SecurityGroup"); b.HasData( new { - Id = -3, + Id = -8, CommonName = "Global Admin", ParentId = -1 }); }); - modelBuilder.Entity("UserService.DataModels.User", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.User", b => { - b.HasBaseType("UserService.DataModels.Member"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Member"); b.Property("FirstName") .HasColumnType("TEXT"); @@ -136,38 +160,38 @@ namespace UserService.Migrations b.HasData( new { - Id = -4, + Id = -7, CommonName = "holger", - ParentId = -2, + ParentId = -6, IsActive = true }); }); - modelBuilder.Entity("UserService.DataModels.Node", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.Node", b => { - b.HasOne("UserService.DataModels.Node", "Parent") + b.HasOne("UserService.DatabaseLayer.DataModels.Node", "Parent") .WithMany("Children") .HasForeignKey("ParentId"); }); - modelBuilder.Entity("UserService.DataModels.UserMember", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.UserMember", b => { - b.HasOne("UserService.DataModels.Member", "Member") + b.HasOne("UserService.DatabaseLayer.DataModels.Member", "Member") .WithMany("Members") .HasForeignKey("MemberId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("UserService.DataModels.User", "User") + b.HasOne("UserService.DatabaseLayer.DataModels.User", "User") .WithMany("MemberOf") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("UserService.DataModels.OrganizationUnit", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.OrganizationUnit", b => { - b.HasOne("UserService.DataModels.Member", "Manager") + b.HasOne("UserService.DatabaseLayer.DataModels.Member", "Manager") .WithMany() .HasForeignKey("ManagerId"); }); diff --git a/UserService/Migrations/20200724180034_initial.cs b/UserService.DatabaseLayer/Migrations/20200725195658_initial.cs similarity index 77% rename from UserService/Migrations/20200724180034_initial.cs rename to UserService.DatabaseLayer/Migrations/20200725195658_initial.cs index 48000a4..bfb6a17 100644 --- a/UserService/Migrations/20200724180034_initial.cs +++ b/UserService.DatabaseLayer/Migrations/20200725195658_initial.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace UserService.Migrations +namespace UserService.DatabaseLayer.Migrations { public partial class initial : Migration { @@ -75,18 +75,38 @@ namespace UserService.Migrations migrationBuilder.InsertData( table: "Node", - columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "EMail", "FirstName", "IsActive", "LastName" }, - values: new object[] { -4, "holger", null, "User", -2, null, null, true, null }); + columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "ManagerId" }, + values: new object[] { -6, "Germany", null, "OrganizationUnit", -2, null }); + + migrationBuilder.InsertData( + table: "Node", + columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "ManagerId" }, + values: new object[] { -5, "USA", null, "OrganizationUnit", -2, null }); + + migrationBuilder.InsertData( + table: "Node", + columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "ManagerId" }, + values: new object[] { -3, "France", null, "OrganizationUnit", -2, null }); migrationBuilder.InsertData( table: "Node", columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "EMail" }, - values: new object[] { -3, "Global Admin", null, "SecurityGroup", -1, null }); + values: new object[] { -8, "Global Admin", null, "SecurityGroup", -1, null }); + + migrationBuilder.InsertData( + table: "Node", + columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "EMail", "FirstName", "IsActive", "LastName" }, + values: new object[] { -7, "holger", null, "User", -6, null, null, true, null }); + + migrationBuilder.InsertData( + table: "Node", + columns: new[] { "Id", "CommonName", "Description", "Discriminator", "ParentId", "ManagerId" }, + values: new object[] { -4, "Arizona", null, "OrganizationUnit", -5, null }); migrationBuilder.InsertData( table: "UserMembers", columns: new[] { "MemberId", "UserId" }, - values: new object[] { -3, -4 }); + values: new object[] { -8, -7 }); migrationBuilder.CreateIndex( name: "IX_Node_ParentId", diff --git a/UserService/Migrations/UserServiceDbContextModelSnapshot.cs b/UserService.DatabaseLayer/Migrations/UserServiceDbContextModelSnapshot.cs similarity index 63% rename from UserService/Migrations/UserServiceDbContextModelSnapshot.cs rename to UserService.DatabaseLayer/Migrations/UserServiceDbContextModelSnapshot.cs index b0cb2fc..4d03a6e 100644 --- a/UserService/Migrations/UserServiceDbContextModelSnapshot.cs +++ b/UserService.DatabaseLayer/Migrations/UserServiceDbContextModelSnapshot.cs @@ -3,9 +3,9 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using UserService.DataModels; +using UserService.DatabaseLayer.DataModels; -namespace UserService.Migrations +namespace UserService.DatabaseLayer.Migrations { [DbContext(typeof(UserServiceDbContext))] partial class UserServiceDbContextModelSnapshot : ModelSnapshot @@ -16,7 +16,7 @@ namespace UserService.Migrations modelBuilder .HasAnnotation("ProductVersion", "3.1.6"); - modelBuilder.Entity("UserService.DataModels.Node", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.Node", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -45,7 +45,7 @@ namespace UserService.Migrations b.HasDiscriminator("Discriminator").HasValue("Node"); }); - modelBuilder.Entity("UserService.DataModels.UserMember", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.UserMember", b => { b.Property("MemberId") .HasColumnType("INTEGER"); @@ -62,14 +62,14 @@ namespace UserService.Migrations b.HasData( new { - MemberId = -3, - UserId = -4 + MemberId = -8, + UserId = -7 }); }); - modelBuilder.Entity("UserService.DataModels.Member", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.Member", b => { - b.HasBaseType("UserService.DataModels.Node"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Node"); b.Property("EMail") .HasColumnType("TEXT"); @@ -77,9 +77,9 @@ namespace UserService.Migrations b.HasDiscriminator().HasValue("Member"); }); - modelBuilder.Entity("UserService.DataModels.OrganizationUnit", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.OrganizationUnit", b => { - b.HasBaseType("UserService.DataModels.Node"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Node"); b.Property("ManagerId") .HasColumnType("INTEGER"); @@ -98,27 +98,51 @@ namespace UserService.Migrations { Id = -1, CommonName = "Groups" + }, + new + { + Id = -6, + CommonName = "Germany", + ParentId = -2 + }, + new + { + Id = -5, + CommonName = "USA", + ParentId = -2 + }, + new + { + Id = -4, + CommonName = "Arizona", + ParentId = -5 + }, + new + { + Id = -3, + CommonName = "France", + ParentId = -2 }); }); - modelBuilder.Entity("UserService.DataModels.SecurityGroup", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.SecurityGroup", b => { - b.HasBaseType("UserService.DataModels.Member"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Member"); b.HasDiscriminator().HasValue("SecurityGroup"); b.HasData( new { - Id = -3, + Id = -8, CommonName = "Global Admin", ParentId = -1 }); }); - modelBuilder.Entity("UserService.DataModels.User", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.User", b => { - b.HasBaseType("UserService.DataModels.Member"); + b.HasBaseType("UserService.DatabaseLayer.DataModels.Member"); b.Property("FirstName") .HasColumnType("TEXT"); @@ -134,38 +158,38 @@ namespace UserService.Migrations b.HasData( new { - Id = -4, + Id = -7, CommonName = "holger", - ParentId = -2, + ParentId = -6, IsActive = true }); }); - modelBuilder.Entity("UserService.DataModels.Node", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.Node", b => { - b.HasOne("UserService.DataModels.Node", "Parent") + b.HasOne("UserService.DatabaseLayer.DataModels.Node", "Parent") .WithMany("Children") .HasForeignKey("ParentId"); }); - modelBuilder.Entity("UserService.DataModels.UserMember", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.UserMember", b => { - b.HasOne("UserService.DataModels.Member", "Member") + b.HasOne("UserService.DatabaseLayer.DataModels.Member", "Member") .WithMany("Members") .HasForeignKey("MemberId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("UserService.DataModels.User", "User") + b.HasOne("UserService.DatabaseLayer.DataModels.User", "User") .WithMany("MemberOf") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("UserService.DataModels.OrganizationUnit", b => + modelBuilder.Entity("UserService.DatabaseLayer.DataModels.OrganizationUnit", b => { - b.HasOne("UserService.DataModels.Member", "Manager") + b.HasOne("UserService.DatabaseLayer.DataModels.Member", "Manager") .WithMany() .HasForeignKey("ManagerId"); }); diff --git a/UserService.DatabaseLayer/Repository/BaseRepository.cs b/UserService.DatabaseLayer/Repository/BaseRepository.cs new file mode 100644 index 0000000..5072183 --- /dev/null +++ b/UserService.DatabaseLayer/Repository/BaseRepository.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using UserService.DatabaseLayer.DataModels; + +namespace UserService.DatabaseLayer.Repository +{ + public class BaseRepository where T : class + { + private readonly Func> _context; + + protected BaseRepository(Func> context) + { + _context = context; + } + + public async Task> GetAllAsync(CancellationToken token = default) + { + await using var db = new UserServiceDbContext(); + return await _context(db).ToListAsync(token); + } + + public async Task GetAsync(Expression> predicate, CancellationToken token = default) + { + await using var db = new UserServiceDbContext(); + return await _context(db).FirstOrDefaultAsync(predicate, token); + } + + public async Task AddAsync(T entity, CancellationToken token = default) + { + await using var db = new UserServiceDbContext(); + await _context(db).AddAsync(@entity, token); + await db.SaveChangesAsync(token); + } + + public async Task UpdateAsync(T entity, CancellationToken token = default) + { + await using var db = new UserServiceDbContext(); + _context(db).Update(entity); + await db.SaveChangesAsync(token); + } + + public async Task DeleteAsync(T entity, CancellationToken token = default) + { + await using var db = new UserServiceDbContext(); + _context(db).Remove(entity); + await db.SaveChangesAsync(token); + } + } +} \ No newline at end of file diff --git a/UserService/Repository/IRepository.cs b/UserService.DatabaseLayer/Repository/IRepository.cs similarity index 62% rename from UserService/Repository/IRepository.cs rename to UserService.DatabaseLayer/Repository/IRepository.cs index ee9c8e0..19f8598 100644 --- a/UserService/Repository/IRepository.cs +++ b/UserService.DatabaseLayer/Repository/IRepository.cs @@ -1,21 +1,22 @@ using System; using System.Collections.Generic; +using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; -using UserService.DataModels; +using UserService.DatabaseLayer.DataModels; -namespace UserService.Repository +namespace UserService.DatabaseLayer.Repository { public interface IRepository where T : Node { Task> GetAllAsync(CancellationToken token = default); - Task GetAsync(Func predicate, CancellationToken token = default); + Task GetAsync(Expression> predicate, CancellationToken token = default); Task AddAsync(T entity, CancellationToken token = default); Task UpdateAsync(T entity, CancellationToken token = default); Task DeleteAsync(T entity, CancellationToken token = default); } - public interface IOrganizationUnitRepository : IRepository + public interface IOrganizationUnitsRepository : IRepository { } @@ -29,4 +30,9 @@ namespace UserService.Repository { } + + public interface INodesRepository : IRepository + { + + } } \ No newline at end of file diff --git a/UserService.DatabaseLayer/Repository/Repository.cs b/UserService.DatabaseLayer/Repository/Repository.cs new file mode 100644 index 0000000..346cea0 --- /dev/null +++ b/UserService.DatabaseLayer/Repository/Repository.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using UserService.DatabaseLayer.DataModels; + +namespace UserService.DatabaseLayer.Repository +{ + public class OrganizationUnitsRepository : BaseRepository, IOrganizationUnitsRepository + { + public OrganizationUnitsRepository() : base(x => x.OrganizationUnits) + { + } + } + + public class SecurityGroupsRepository : BaseRepository, ISecurityGroupsRepository + { + public SecurityGroupsRepository() : base(x=> x.SecurityGroups) + { + } + } + + + public class UsersRepository : BaseRepository, IUsersRepository + { + public UsersRepository() : base(x => x.Users) + { + } + } + + public class NodesRepository : INodesRepository + { + private readonly IUsersRepository _users; + private readonly ISecurityGroupsRepository _securityGroups; + private readonly IOrganizationUnitsRepository _organizationUnits; + + public NodesRepository(IUsersRepository users, ISecurityGroupsRepository securityGroups, IOrganizationUnitsRepository organizationUnits) + { + _users = users; + _securityGroups = securityGroups; + _organizationUnits = organizationUnits; + } + + public static async IAsyncEnumerable GetNodesAsync([EnumeratorCancellation] CancellationToken token = default) + { + await using var db = new UserServiceDbContext(); + await foreach (var note in db.OrganizationUnits.AsAsyncEnumerable().WithCancellation(token)) + { + yield return note; + } + await foreach (var node in db.SecurityGroups.AsAsyncEnumerable().WithCancellation(token)) + { + yield return node; + } + + await foreach (var node in db.Users.AsAsyncEnumerable().WithCancellation(token)) + { + yield return node; + } + } + + public async Task> GetAllAsync(CancellationToken token = default) + { + + var list = new List(); + await foreach (var node in GetNodesAsync(token)) + { + list.Add(node); + } + + return list; + } + + public Task GetAsync(Expression> predicate, CancellationToken token = default) + { + throw new NotImplementedException(); + } + + public Task AddAsync(Node entity, CancellationToken token = default) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(Node entity, CancellationToken token = default) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(Node entity, CancellationToken token = default) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/UserService.DatabaseLayer/UserService.DatabaseLayer.csproj b/UserService.DatabaseLayer/UserService.DatabaseLayer.csproj new file mode 100644 index 0000000..c6062ce --- /dev/null +++ b/UserService.DatabaseLayer/UserService.DatabaseLayer.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + 8 + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/UserService.Test/UnitTest1.cs b/UserService.Test/UnitTest1.cs index c1b8016..10d9223 100644 --- a/UserService.Test/UnitTest1.cs +++ b/UserService.Test/UnitTest1.cs @@ -1,8 +1,12 @@ +using System; +using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using NUnit.Framework; -using UserService.DataModels; +using UserService.DatabaseLayer.DataModels; namespace UserService.Test { @@ -22,6 +26,8 @@ namespace UserService.Test var ous = await db.OrganizationUnits.ToListAsync(); var mo = await db.UserMembers.ToListAsync(); + var securityGroupsOfUser = user.GetSecurityGroups(); + var usersOfSecurityGroup = secGroup.GetUsers(); //var testGroup = new SecurityGroup {CommonName = "Test", Parent = ous.Last()}; //await db.SecurityGroups.AddAsync(testGroup); //var testgroup = await db.SecurityGroups.FindAsync(1); @@ -29,5 +35,26 @@ namespace UserService.Test //await db.SaveChangesAsync(); Assert.Pass(); } + + [Test] + public async Task Test2() + { + await using var db = new UserServiceDbContext(); + var ous = await db.OrganizationUnits.ToListAsync(); + var sb = new StringBuilder(); + NewMethod(ous, null, 0, ref sb); + var result = sb.ToString(); + Assert.Pass(); + + } + + private static void NewMethod(IEnumerable ous, Node parent, int level, ref StringBuilder sb) + { + foreach (var unit in ous.Where(x => x.Parent == parent)) + { + sb.AppendLine(string.Concat(Enumerable.Repeat(' ', 4 * level)) + unit.CommonName); + NewMethod(unit.Children.OfType(), unit, level + 1, ref sb); + } + } } } \ No newline at end of file diff --git a/UserService.Test/UserService.Test.csproj b/UserService.Test/UserService.Test.csproj index 2dbff29..022e46a 100644 --- a/UserService.Test/UserService.Test.csproj +++ b/UserService.Test/UserService.Test.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/UserService.db b/UserService.db index c07d1917a2377e4bac4e28b54cd0b686d9124aa3..e7d1466056b24ebcc496b6475b82b5a899bdc834 100644 GIT binary patch delta 484 zcmZoTz|?SnX~P?Nc2h%3Q!`VG&7bA}@vB2Q@tJvtP(;&1<=^u)Z(s>G7a{Jc=0 zX<{(NKe-ts85l)@N;w%a@^jKti++U`rxvNg{iQhr8WYG`q4QGs4@W=>|RLwasv zW{zHRer^CrO)yBz{XZ1^;bxH3W)wA+l;>o~$j?bnEppCCO;ae#&ns4NPt2=I{Rc7$ zB9mlcj4;b1UmT>`>F~CqwAPG# diff --git a/UserService.sln b/UserService.sln index 85bbd48..f9488a0 100644 --- a/UserService.sln +++ b/UserService.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UserService", "UserService\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UserService.Test", "UserService.Test\UserService.Test.csproj", "{F4C0B161-F9DD-4335-A609-D7FC5EA0E14A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UserService.DatabaseLayer", "UserService.DatabaseLayer\UserService.DatabaseLayer.csproj", "{4505C991-7E39-416F-94E5-D906DD0D90F9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {F4C0B161-F9DD-4335-A609-D7FC5EA0E14A}.Debug|Any CPU.Build.0 = Debug|Any CPU {F4C0B161-F9DD-4335-A609-D7FC5EA0E14A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F4C0B161-F9DD-4335-A609-D7FC5EA0E14A}.Release|Any CPU.Build.0 = Release|Any CPU + {4505C991-7E39-416F-94E5-D906DD0D90F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4505C991-7E39-416F-94E5-D906DD0D90F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4505C991-7E39-416F-94E5-D906DD0D90F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4505C991-7E39-416F-94E5-D906DD0D90F9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UserService/Data/WeatherForecast.cs b/UserService/Data/WeatherForecast.cs deleted file mode 100644 index b5a2390..0000000 --- a/UserService/Data/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace UserService.Data -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} diff --git a/UserService/Data/WeatherForecastService.cs b/UserService/Data/WeatherForecastService.cs deleted file mode 100644 index d280982..0000000 --- a/UserService/Data/WeatherForecastService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace UserService.Data -{ - public class WeatherForecastService - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - public Task GetForecastAsync(DateTime startDate) - { - var rng = new Random(); - return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }).ToArray()); - } - } -} diff --git a/UserService/DataModels/ModelBuilderExtensions.cs b/UserService/DataModels/ModelBuilderExtensions.cs deleted file mode 100644 index 168b92d..0000000 --- a/UserService/DataModels/ModelBuilderExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace UserService.DataModels -{ - public static class ModelBuilderExtensions - { - public static void Seed(this ModelBuilder modelBuilder) - { - var users = new OrganizationUnit {CommonName = "Users", Id = -2}; - var groups = new OrganizationUnit {CommonName = "Groups", Id = -1}; - modelBuilder.Entity().HasData(users, groups); - var user = new User {CommonName = "holger", IsActive = true, Id = -4, ParentId = users.Id}; - modelBuilder.Entity().HasData(user); - var secGroup = new SecurityGroup {CommonName = "Global Admin", Id = -3, ParentId = groups.Id}; - modelBuilder.Entity().HasData(secGroup); - - modelBuilder.Entity() - .HasData(new UserMember {MemberId = secGroup.Id, UserId = user.Id}); - } - - public static void CreateRelations(this ModelBuilder modelBuilder) - { - modelBuilder.Entity() - .HasKey(bc => new {bc.MemberId, bc.UserId}); - modelBuilder.Entity() - .HasOne(bc => bc.User) - .WithMany(b => b!.MemberOf) - .HasForeignKey(bc => bc.UserId); - modelBuilder.Entity() - .HasOne(bc => bc.Member) - .WithMany(c => c!.Members) - .HasForeignKey(bc => bc.MemberId); - - modelBuilder.Entity() - .HasMany(c => c.Children) - .WithOne(e => e.Parent!) - .HasForeignKey(bc => bc.ParentId); - } - } -} \ No newline at end of file diff --git a/UserService/Pages/Counter.razor b/UserService/Pages/Counter.razor index 8641f78..974a852 100644 --- a/UserService/Pages/Counter.razor +++ b/UserService/Pages/Counter.razor @@ -1,16 +1,30 @@ @page "/counter" +@using UserService.DatabaseLayer.DataModels +@using UserService.DatabaseLayer.Repository +@inject IOrganizationUnitsRepository OuRepository -

Counter

+

Tree

-

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } +@if (_organizationUnits == null) +{ +

+ Loading... +

} +else +{ + + @foreach (var unit in _organizationUnits) + { + + } + +} +@code { + private IReadOnlyList _organizationUnits; + + protected override async Task OnInitializedAsync() + { + _organizationUnits = await OuRepository.GetAllAsync().ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/UserService/Pages/FetchData.razor b/UserService/Pages/FetchData.razor deleted file mode 100644 index e8a9c06..0000000 --- a/UserService/Pages/FetchData.razor +++ /dev/null @@ -1,46 +0,0 @@ -@page "/fetchdata" - -@using UserService.Data -@inject WeatherForecastService ForecastService - -

Weather forecast

- -

This component demonstrates fetching data from a service.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[] forecasts; - - protected override async Task OnInitializedAsync() - { - forecasts = await ForecastService.GetForecastAsync(DateTime.Now); - } -} diff --git a/UserService/Pages/Index.razor b/UserService/Pages/Index.razor index c1287cf..5a13a53 100644 --- a/UserService/Pages/Index.razor +++ b/UserService/Pages/Index.razor @@ -1,6 +1,6 @@ @page "/" -@using UserService.Repository -@using UserService.DataModels +@using UserService.DatabaseLayer.DataModels +@using UserService.DatabaseLayer.Repository @inject IUsersRepository UsersRepository @@ -63,7 +63,7 @@ else } else { - No user selected + No securityGroup selected } No Thanks @@ -74,7 +74,7 @@ else @code { bool _dialogIsOpen; - User _userToEdit; + User? _userToEdit; private IReadOnlyList _users; diff --git a/UserService/Repository/IGroupsRepository.cs b/UserService/Repository/IGroupsRepository.cs deleted file mode 100644 index 26d0e61..0000000 --- a/UserService/Repository/IGroupsRepository.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using UserService.DataModels; - -namespace UserService.Repository -{ - public class OrganizationUnitRepository : IOrganizationUnitRepository - { - public async Task> GetAllAsync(CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - return await db.OrganizationUnits.ToListAsync(token); - } - - /// - public async Task GetAsync(Func predicate, - CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - return db.OrganizationUnits.Where(predicate).FirstOrDefault(); - } - - public async Task AddAsync(OrganizationUnit entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - await db.OrganizationUnits.AddAsync(@entity, token); - await db.SaveChangesAsync(token); - } - - public async Task UpdateAsync(OrganizationUnit entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - db.OrganizationUnits.Update(entity); - await db.SaveChangesAsync(token); - } - - public async Task DeleteAsync(OrganizationUnit entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - db.OrganizationUnits.Remove(entity); - await db.SaveChangesAsync(token); - } - } - - - public class SecurityGroupsRepository : ISecurityGroupsRepository - { - public async Task> GetAllAsync(CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - return await db.SecurityGroups.ToListAsync(token); - } - - /// - public async Task GetAsync(Func predicate, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - return db.SecurityGroups.Where(predicate).FirstOrDefault(); - } - - public async Task AddAsync(SecurityGroup entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - await db.SecurityGroups.AddAsync(@entity, token); - await db.SaveChangesAsync(token); - } - - public async Task UpdateAsync(SecurityGroup entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - db.SecurityGroups.Update(entity); - await db.SaveChangesAsync(token); - } - - public async Task DeleteAsync(SecurityGroup entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - db.SecurityGroups.Remove(entity); - await db.SaveChangesAsync(token); - } - } - - - public class UsersRepository : IUsersRepository - { - public async Task> GetAllAsync(CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - return await db.Users.ToListAsync(token); - } - - /// - public async Task GetAsync(Func predicate, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - return db.Users.Where(predicate).FirstOrDefault(); - } - - public async Task AddAsync(User entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - await db.Users.AddAsync(@entity, token); - await db.SaveChangesAsync(token); - } - - public async Task UpdateAsync(User entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - db.Users.Update(entity); - await db.SaveChangesAsync(token); - } - - public async Task DeleteAsync(User entity, CancellationToken token = default) - { - await using var db = new UserServiceDbContext(); - db.Users.Remove(entity); - await db.SaveChangesAsync(token); - } - } -} \ No newline at end of file diff --git a/UserService/Shared/NavMenu.razor b/UserService/Shared/NavMenu.razor index 6b2933c..05f5ee3 100644 --- a/UserService/Shared/NavMenu.razor +++ b/UserService/Shared/NavMenu.razor @@ -17,11 +17,6 @@ Counter - diff --git a/UserService/Shared/OrgUnitItem.razor b/UserService/Shared/OrgUnitItem.razor new file mode 100644 index 0000000..5ce2d9c --- /dev/null +++ b/UserService/Shared/OrgUnitItem.razor @@ -0,0 +1,15 @@ +@using UserService.DatabaseLayer.DataModels + + +   @OrganizationUnit.CommonName + + + Item 6.A + Item 6.B + Item 6.C + + +@code { + [Parameter] + public OrganizationUnit OrganizationUnit { get; set; } +} diff --git a/UserService/Startup.cs b/UserService/Startup.cs index 9c38374..ece0d57 100644 --- a/UserService/Startup.cs +++ b/UserService/Startup.cs @@ -1,16 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using UserService.Data; -using UserService.Repository; +using UserService.DatabaseLayer.Repository; namespace UserService { @@ -29,10 +22,10 @@ namespace UserService { services.AddRazorPages(); services.AddServerSideBlazor(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + //services.AddSingleton(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/UserService/UserService.csproj b/UserService/UserService.csproj index efaf653..5633dec 100644 --- a/UserService/UserService.csproj +++ b/UserService/UserService.csproj @@ -7,11 +7,10 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + +