From beb15ee1f9fdee6f4610a7873555c8dd51a34363 Mon Sep 17 00:00:00 2001
From: Lucca Santangelo <luccasant95@gmail.com>
Date: Sat, 7 Nov 2020 14:25:29 -0300
Subject: [PATCH] add sections

---
 .../Tsi1.Api/Controllers/SectionController.cs |  56 ++
 .../Controllers/SectionItemController.cs      |   2 +-
 Tsi1.Api/Tsi1.Api/Startup.cs                  |   5 +-
 .../{SectionItemOrderDto.cs => OrderDto.cs}   |   4 +-
 .../Dtos/SectionCreateDto.cs                  |  13 +
 .../Helpers/ErrorMessages.cs                  |   1 +
 .../Helpers/MappingProfile.cs                 |   2 +
 .../Interfaces/ISectionItemService.cs         |   2 +-
 .../Interfaces/ISectionService.cs             |  17 +
 .../Services/SectionItemService.cs            |  10 +-
 .../Services/SectionService.cs                | 118 ++++
 Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs    |   4 +-
 Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs   |   3 +
 .../EntityConfiguration/ForumConfiguration.cs |   4 -
 .../SectionConfiguration.cs                   |   4 +
 .../20201107172316_sections.Designer.cs       | 614 ++++++++++++++++++
 .../Migrations/20201107172316_sections.cs     |  75 +++
 .../Migrations/Tsi1ContextModelSnapshot.cs    |  26 +-
 18 files changed, 929 insertions(+), 31 deletions(-)
 rename Tsi1.Api/Tsi1.BusinessLayer/Dtos/{SectionItemOrderDto.cs => OrderDto.cs} (65%)
 create mode 100644 Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs
 create mode 100644 Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs
 create mode 100644 Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs
 create mode 100644 Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs
 create mode 100644 Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs

diff --git a/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs b/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs
index a4ef022..fec06ce 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs
@@ -4,6 +4,8 @@ using System.Linq;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Interfaces;
 
 namespace Tsi1.Api.Controllers
 {
@@ -11,5 +13,59 @@ namespace Tsi1.Api.Controllers
     [ApiController]
     public class SectionController : ControllerBase
     {
+        private readonly ISectionService _sectionService;
+
+        public SectionController(ISectionService sectionService)
+        {
+            _sectionService = sectionService;
+        }
+
+        [HttpPost("Create")]
+        public async Task<IActionResult> Create(SectionCreateDto section)
+        {
+            var result = await _sectionService.Create(section);
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok();
+        }
+
+        [HttpPut("Modify/{sectionId}")]
+        public async Task<IActionResult> Modify(int sectionId, string newName)
+        {
+            var result = await _sectionService.Modify(sectionId, newName);
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok();
+        }
+
+        [HttpDelete("Delete/{sectionId}")]
+        public async Task<IActionResult> Delete(int sectionId)
+        {
+            var result = await _sectionService.Delete(sectionId);
+            if (result.HasError)
+            {
+                return NotFound(result.Message);
+            }
+
+            return Ok();
+        }
+
+        [HttpPost("ChangeOrder")]
+        public async Task<IActionResult> ChangeOrder(List<OrderDto> orderDtos)
+        {
+            var result = await _sectionService.OrderSections(orderDtos);
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok();
+        }
     }
 }
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs b/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs
index 1eae7d6..5722e80 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs
@@ -45,7 +45,7 @@ namespace Tsi1.Api.Controllers
         }
 
         [HttpPost("ChangeOrder")]
-        public async Task<IActionResult> ChangeOrder(List<SectionItemOrderDto> orderDtos)
+        public async Task<IActionResult> ChangeOrder(List<OrderDto> orderDtos)
         {
             var result = await _sectionItemService.OrderSectionItems(orderDtos);
             if (result.HasError)
diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs
index b4f76b7..e8e1ee7 100644
--- a/Tsi1.Api/Tsi1.Api/Startup.cs
+++ b/Tsi1.Api/Tsi1.Api/Startup.cs
@@ -1,7 +1,5 @@
 using System;
-using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using AutoMapper;
@@ -9,8 +7,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.HttpsPolicy;
-using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.StaticFiles;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
@@ -80,6 +76,7 @@ namespace Tsi1.Api
             services.AddScoped<IPostMessageService, PostMessageService>();
             services.AddScoped<ITenantService, TenantService>();
             services.AddScoped<IFileService, FileService>();
+            services.AddScoped<ISectionService, SectionService>();
             services.AddScoped<ISectionItemService, SectionItemService>();
 
             services.Configure<MailSettings>(Configuration.GetSection("MailSettings"));
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemOrderDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/OrderDto.cs
similarity index 65%
rename from Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemOrderDto.cs
rename to Tsi1.Api/Tsi1.BusinessLayer/Dtos/OrderDto.cs
index 15e252c..b541634 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemOrderDto.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/OrderDto.cs
@@ -4,9 +4,9 @@ using System.Text;
 
 namespace Tsi1.BusinessLayer.Dtos
 {
-    public class SectionItemOrderDto
+    public class OrderDto
     {
-        public int SectionItemId { get; set; }
+        public int Id { get; set; }
         public int Order { get; set; }
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs
new file mode 100644
index 0000000..3eee0e0
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+    public class SectionCreateDto
+    {
+        public int CourseId { get; set; }
+        public string Name { get; set; }
+        public int Order { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
index 3f57fdf..df38861 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
@@ -45,6 +45,7 @@ namespace Tsi1.BusinessLayer.Helpers
         public const string FileDoesNotExist = "El archivo '{0}' no existe fisicamente en el file server";
 
         public const string SectionDoesNotExist = "La seccion con id '{0}' no existe";
+        public const string DuplicateSectionOrder = "Hay secciones con el mismo orden";
         public const string SectionItemDoesNotExist = "El item de seccion con id '{0}' no existe";
         public const string DuplicateSectionItemOrder = "Hay items de seccion con el mismo orden";
 
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
index d9dff00..46914ab 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
@@ -33,6 +33,7 @@ namespace Tsi1.BusinessLayer.Helpers
             CreateMap<UserType, UserTypeDto>();
             CreateMap<File, FileDto>();
             CreateMap<SectionItem, SectionItemCreateDto>();
+            CreateMap<Section, SectionCreateDto>();
 
             CreateMap<ForumCreateDto, Forum>();
             CreateMap<ForumPreviewDto, Forum>();
@@ -55,6 +56,7 @@ namespace Tsi1.BusinessLayer.Helpers
             CreateMap<UserTypeDto, UserType>();
             CreateMap<FileDto, File>();
             CreateMap<SectionItemCreateDto, SectionItem>();
+            CreateMap<SectionCreateDto, Section>();
         }
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs
index d169c20..4834f03 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs
@@ -11,6 +11,6 @@ namespace Tsi1.BusinessLayer.Interfaces
     {
         Task<ServiceResult<bool>> Create(SectionItemCreateDto newSectionItem);
         Task<ServiceResult<bool>> Delete(int sectionItemId);
-        Task<ServiceResult<bool>> OrderSectionItems(List<SectionItemOrderDto> orderDtos);
+        Task<ServiceResult<bool>> OrderSectionItems(List<OrderDto> orderDtos);
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs
new file mode 100644
index 0000000..4ba20e7
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+
+namespace Tsi1.BusinessLayer.Interfaces
+{
+    public interface ISectionService
+    {
+        Task<ServiceResult<bool>> Create(SectionCreateDto newSection);
+        Task<ServiceResult<bool>> Delete(int sectionId);
+        Task<ServiceResult<bool>> OrderSections(List<OrderDto> orderDtos);
+        Task<ServiceResult<bool>> Modify(int sectionId, string name);
+    }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs
index fe4f531..ae0eedc 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs
@@ -62,11 +62,11 @@ namespace Tsi1.BusinessLayer.Services
             return result;
         }
 
-        public async Task<ServiceResult<bool>> OrderSectionItems(List<SectionItemOrderDto> orderDtos)
+        public async Task<ServiceResult<bool>> OrderSectionItems(List<OrderDto> orderDtos)
         {
             var result = new ServiceResult<bool>();
 
-            var sectionItemsIds = orderDtos.Select(x => x.SectionItemId);
+            var sectionItemIds = orderDtos.Select(x => x.Id);
             var orders = orderDtos.Select(x => x.Order).Distinct();
 
             if (orders.Count() != orderDtos.Count())
@@ -77,16 +77,16 @@ namespace Tsi1.BusinessLayer.Services
             }
 
             var sectionItems = await _context.SectionItems
-                .Where(x => sectionItemsIds.Contains(x.Id))
+                .Where(x => sectionItemIds.Contains(x.Id))
                 .ToListAsync();
 
             foreach (var orderDto in orderDtos)
             {
-                var sectionItem = sectionItems.FirstOrDefault(x => x.Id == orderDto.SectionItemId);
+                var sectionItem = sectionItems.FirstOrDefault(x => x.Id == orderDto.Id);
                 if (sectionItem == null)
                 {
                     result.HasError = true;
-                    result.AddMessage(string.Format(ErrorMessages.SectionItemDoesNotExist, orderDto.SectionItemId));
+                    result.AddMessage(string.Format(ErrorMessages.SectionItemDoesNotExist, orderDto.Id));
                 }
 
                 sectionItem.Order = orderDto.Order;
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs
new file mode 100644
index 0000000..9da29bb
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs
@@ -0,0 +1,118 @@
+using AutoMapper;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+using Tsi1.DataLayer;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.BusinessLayer.Services
+{
+    public class SectionService : ISectionService
+    {
+        private readonly Tsi1Context _context;
+        private readonly IMapper _mapper;
+
+        public SectionService(Tsi1Context context, IMapper mapper)
+        {
+            _context = context;
+            _mapper = mapper;
+        }
+
+        public async Task<ServiceResult<bool>> Create(SectionCreateDto newSection)
+        {
+            var result = new ServiceResult<bool>();
+
+            var course = await _context.Courses.FirstOrDefaultAsync(x => x.Id == newSection.CourseId);
+            if (course == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.CourseDoesNotExist, newSection.CourseId));
+                return result;
+            }
+
+            var section = _mapper.Map<Section>(newSection);
+            _context.Sections.Add(section);
+            await _context.SaveChangesAsync();
+
+            return result;
+        }
+
+        public async Task<ServiceResult<bool>> Delete(int sectionId)
+        {
+            var result = new ServiceResult<bool>();
+
+            var section = await _context.Sections.FirstOrDefaultAsync(x => x.Id == sectionId);
+            if (section == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.SectionDoesNotExist, sectionId));
+                return result;
+            }
+
+            _context.Sections.Remove(section);
+            await _context.SaveChangesAsync();
+            return result;
+        }
+
+        public async Task<ServiceResult<bool>> Modify(int sectionId, string name)
+        {
+            var result = new ServiceResult<bool>();
+
+            var section = await _context.Sections.FirstOrDefaultAsync(x => x.Id == sectionId);
+            if (section == null)
+            {
+                result.HasError = true;
+                result.AddMessage(string.Format(ErrorMessages.SectionDoesNotExist, sectionId));
+                return result;
+            }
+
+            section.Name = name;
+            await _context.SaveChangesAsync();
+            return result;
+        }
+
+        public async Task<ServiceResult<bool>> OrderSections(List<OrderDto> orderDtos)
+        {
+            var result = new ServiceResult<bool>();
+
+            var sectionIds = orderDtos.Select(x => x.Id);
+            var orders = orderDtos.Select(x => x.Order).Distinct();
+
+            if (orders.Count() != orderDtos.Count())
+            {
+                result.HasError = true;
+                result.AddMessage(ErrorMessages.DuplicateSectionOrder);
+                return result;
+            }
+
+            var sections = await _context.Sections
+                .Where(x => sectionIds.Contains(x.Id))
+                .ToListAsync();
+
+            foreach (var orderDto in orderDtos)
+            {
+                var section = sections.FirstOrDefault(x => x.Id == orderDto.Id);
+                if (section == null)
+                {
+                    result.HasError = true;
+                    result.AddMessage(string.Format(ErrorMessages.SectionDoesNotExist, orderDto.Id));
+                }
+
+                section.Order = orderDto.Order;
+            }
+
+            if (!result.HasError)
+            {
+                await _context.SaveChangesAsync();
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs
index d77eefa..942858f 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs
@@ -10,7 +10,7 @@ namespace Tsi1.DataLayer.Entities
         {
             StudentCourses = new HashSet<StudentCourse>();
             ProfessorCourses = new HashSet<ProfessorCourse>();
-            Forums = new HashSet<Forum>();
+            Sections = new HashSet<Section>();
         }
 
         public int Id { get; set; }
@@ -20,6 +20,6 @@ namespace Tsi1.DataLayer.Entities
         public Tenant Tenant { get; set; }
         public ICollection<StudentCourse> StudentCourses { get; set; }
         public ICollection<ProfessorCourse> ProfessorCourses { get; set; }
-        public ICollection<Forum> Forums { get; set; }
+        public ICollection<Section> Sections { get; set; }
     }
 }
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs
index 033231a..db0f9d9 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs
@@ -12,8 +12,11 @@ namespace Tsi1.DataLayer.Entities
         }
 
         public int Id { get; set; }
+        public int CourseId { get; set; }
         public string Name { get; set; }
         public int Order { get; set; }
+
+        public Course Course { get; set; }
         public ICollection<SectionItem> SectionItems { get; set; }
     }
 }
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs
index be40300..2c84b92 100644
--- a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs
@@ -1,9 +1,5 @@
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using System;
-using System.Collections.Generic;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
 using Tsi1.DataLayer.Entities;
 
 namespace Tsi1.DataLayer.EntityConfiguration
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs
index 8b2f806..b87104c 100644
--- a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs
@@ -19,6 +19,10 @@ namespace Tsi1.DataLayer.EntityConfiguration
 
             builder.Property(x => x.Order)
                 .IsRequired();
+
+            builder.HasOne(x => x.Course)
+                .WithMany(x => x.Sections)
+                .HasForeignKey(x => x.CourseId);
         }
     }
 }
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs
new file mode 100644
index 0000000..8b4eba5
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs
@@ -0,0 +1,614 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Tsi1.DataLayer;
+
+namespace Tsi1.DataLayer.Migrations
+{
+    [DbContext(typeof(Tsi1Context))]
+    [Migration("20201107172316_sections")]
+    partial class sections
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
+                .HasAnnotation("ProductVersion", "3.1.4")
+                .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.Property<string>("ConnectionId")
+                        .HasColumnType("text");
+
+                    b.Property<string>("GroupName")
+                        .HasColumnType("text");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ConnectionId");
+
+                    b.HasIndex("GroupName");
+
+                    b.ToTable("Connections");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("Name", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Courses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<string>("Path")
+                        .IsRequired()
+                        .HasColumnType("character varying(1000)");
+
+                    b.Property<int>("SectionItemId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Path")
+                        .IsUnique();
+
+                    b.ToTable("Files");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("SectionItemId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Forums");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+                {
+                    b.Property<int>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ForumId", "UserId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("ForumUsers");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+                {
+                    b.Property<string>("Name")
+                        .HasColumnType("text");
+
+                    b.HasKey("Name");
+
+                    b.ToTable("Groups");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .HasColumnType("character varying(100)");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.HasIndex("ForumId", "Title")
+                        .IsUnique();
+
+                    b.ToTable("Posts");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Content")
+                        .IsRequired()
+                        .HasColumnType("character varying(10485760)");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("PostId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("PostId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("PostMessages");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("IdentityCard")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("IdentityCard", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Professors");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+                {
+                    b.Property<int>("ProfessorId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ProfessorId", "CourseId");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("ProfessorCourses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int>("Order")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("Sections");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int?>("FileId")
+                        .HasColumnType("integer");
+
+                    b.Property<int?>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("Order")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("SectionId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("SectionItemTypeId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("FileId")
+                        .IsUnique();
+
+                    b.HasIndex("ForumId")
+                        .IsUnique();
+
+                    b.HasIndex("SectionId");
+
+                    b.HasIndex("SectionItemTypeId");
+
+                    b.ToTable("SectionItems");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItemType", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(100)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SectionItemTypes");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("Age")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("IdentityCard")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("IdentityCard", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Students");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+                {
+                    b.Property<int>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("StudentId", "CourseId");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("StudentCourses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("Tenants");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("FirstName")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("LastName")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("Password")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int?>("ProfessorId")
+                        .HasColumnType("integer");
+
+                    b.Property<int?>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserTypeId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Username")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ProfessorId")
+                        .IsUnique();
+
+                    b.HasIndex("StudentId")
+                        .IsUnique();
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("UserTypeId");
+
+                    b.HasIndex("Username", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Users");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("UserTypes");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+                        .WithMany("Connections")
+                        .HasForeignKey("GroupName");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Courses")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithMany("ForumUsers")
+                        .HasForeignKey("ForumId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("ForumUsers")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithMany("Posts")
+                        .HasForeignKey("ForumId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("Posts")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Post", "Post")
+                        .WithMany("PostMessages")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("PostMessages")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Professors")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("ProfessorCourses")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+                        .WithMany("ProfessorCourses")
+                        .HasForeignKey("ProfessorId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("Sections")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.File", "File")
+                        .WithOne("SectionItem")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "FileId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithOne("SectionItem")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "ForumId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Section", "Section")
+                        .WithMany("SectionItems")
+                        .HasForeignKey("SectionId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.SectionItemType", "SectionItemType")
+                        .WithMany("SectionItems")
+                        .HasForeignKey("SectionItemTypeId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Students")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("StudentCourses")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithMany("StudentCourses")
+                        .HasForeignKey("StudentId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+                        .WithOne("User")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithOne("User")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Users")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType")
+                        .WithMany()
+                        .HasForeignKey("UserTypeId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs
new file mode 100644
index 0000000..5166873
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs
@@ -0,0 +1,75 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Tsi1.DataLayer.Migrations
+{
+    public partial class sections : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropForeignKey(
+                name: "FK_Forums_Courses_CourseId",
+                table: "Forums");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Forums_CourseId",
+                table: "Forums");
+
+            migrationBuilder.DropColumn(
+                name: "CourseId",
+                table: "Forums");
+
+            migrationBuilder.AddColumn<int>(
+                name: "CourseId",
+                table: "Sections",
+                nullable: false,
+                defaultValue: 0);
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Sections_CourseId",
+                table: "Sections",
+                column: "CourseId");
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_Sections_Courses_CourseId",
+                table: "Sections",
+                column: "CourseId",
+                principalTable: "Courses",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Cascade);
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropForeignKey(
+                name: "FK_Sections_Courses_CourseId",
+                table: "Sections");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Sections_CourseId",
+                table: "Sections");
+
+            migrationBuilder.DropColumn(
+                name: "CourseId",
+                table: "Sections");
+
+            migrationBuilder.AddColumn<int>(
+                name: "CourseId",
+                table: "Forums",
+                type: "integer",
+                nullable: true);
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Forums_CourseId",
+                table: "Forums",
+                column: "CourseId");
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_Forums_Courses_CourseId",
+                table: "Forums",
+                column: "CourseId",
+                principalTable: "Courses",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Restrict);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
index 419247e..b520b49 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
@@ -94,9 +94,6 @@ namespace Tsi1.DataLayer.Migrations
                         .HasColumnType("integer")
                         .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
 
-                    b.Property<int?>("CourseId")
-                        .HasColumnType("integer");
-
                     b.Property<string>("Name")
                         .IsRequired()
                         .HasColumnType("character varying(50)");
@@ -106,8 +103,6 @@ namespace Tsi1.DataLayer.Migrations
 
                     b.HasKey("Id");
 
-                    b.HasIndex("CourseId");
-
                     b.ToTable("Forums");
                 });
 
@@ -241,6 +236,9 @@ namespace Tsi1.DataLayer.Migrations
                         .HasColumnType("integer")
                         .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
 
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
                     b.Property<string>("Name")
                         .IsRequired()
                         .HasColumnType("character varying(255)");
@@ -250,6 +248,8 @@ namespace Tsi1.DataLayer.Migrations
 
                     b.HasKey("Id");
 
+                    b.HasIndex("CourseId");
+
                     b.ToTable("Sections");
                 });
 
@@ -459,13 +459,6 @@ namespace Tsi1.DataLayer.Migrations
                         .IsRequired();
                 });
 
-            modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
-                {
-                    b.HasOne("Tsi1.DataLayer.Entities.Course", null)
-                        .WithMany("Forums")
-                        .HasForeignKey("CourseId");
-                });
-
             modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
                 {
                     b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
@@ -535,6 +528,15 @@ namespace Tsi1.DataLayer.Migrations
                         .IsRequired();
                 });
 
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("Sections")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
             modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
                 {
                     b.HasOne("Tsi1.DataLayer.Entities.File", "File")
-- 
GitLab