From 8ccde0b1250ee9be57de1150dea4db852c5e0e90 Mon Sep 17 00:00:00 2001 From: kekentin Date: Mon, 17 Mar 2025 17:34:37 +0100 Subject: [PATCH] Build qui marche , verifier que la supretion / insertion / deplacement marche entre : - User/Commentary/Quote - User/Favorite/Quote --- WF_EF_Api/Contextlib/DbCommentaryManager.cs | 2 +- WF_EF_Api/Contextlib/WTFContext.cs | 21 ++++++ WF_EF_Api/Entity/Commentary.cs | 4 +- WF_EF_Api/Entity/Quote.cs | 13 ++-- WF_EF_Api/Entity/Source.cs | 2 +- WF_EF_Api/Entity/Users.cs | 2 - ...20250317163102_migrationTest1.Designer.cs} | 66 +++++++++---------- ...t1.cs => 20250317163102_migrationTest1.cs} | 39 +++++------ .../Migrations/StubWTFContextModelSnapshot.cs | 64 ++++++++---------- WF_EF_Api/StubbedContextLib/StubWTFContext.cs | 4 +- .../StubbedContextLib.csproj | 4 -- 11 files changed, 108 insertions(+), 113 deletions(-) rename WF_EF_Api/StubbedContextLib/Migrations/{20250314112216_migrationTest1.Designer.cs => 20250317163102_migrationTest1.Designer.cs} (94%) rename WF_EF_Api/StubbedContextLib/Migrations/{20250314112216_migrationTest1.cs => 20250317163102_migrationTest1.cs} (92%) diff --git a/WF_EF_Api/Contextlib/DbCommentaryManager.cs b/WF_EF_Api/Contextlib/DbCommentaryManager.cs index 85c7ac8..002082f 100644 --- a/WF_EF_Api/Contextlib/DbCommentaryManager.cs +++ b/WF_EF_Api/Contextlib/DbCommentaryManager.cs @@ -63,7 +63,7 @@ namespace Contextlib public async Task DeleteCommentForUser(int userId) { // Retrieve comments for the specific userId - var comments = await _context.comments.Where(x => x.IdUsers == userId).ToListAsync(); + var comments = await _context.comments.Where(x => x.IdUser == userId).ToListAsync(); if (!comments.Any()) // If no comments exist for this userId { throw new KeyNotFoundException($"No comments found for the user ID: {userId}."); diff --git a/WF_EF_Api/Contextlib/WTFContext.cs b/WF_EF_Api/Contextlib/WTFContext.cs index f98290d..e0f0a31 100644 --- a/WF_EF_Api/Contextlib/WTFContext.cs +++ b/WF_EF_Api/Contextlib/WTFContext.cs @@ -34,17 +34,38 @@ namespace Contextlib .UsingEntity( l => l.HasOne(f => f.Quote) .WithMany() + .OnDelete(DeleteBehavior.ClientCascade) .HasForeignKey(f => f.IdQuote), r => r.HasOne(f => f.Users) .WithMany() + .OnDelete(DeleteBehavior.ClientCascade) .HasForeignKey(f => f.IdUsers) ); modelBuilder.Entity() .HasMany(u => u.Quotes) .WithOne(q => q.User) + .OnDelete(DeleteBehavior.ClientSetNull) .HasForeignKey(q => q.IdUsersPropose); + modelBuilder.Entity() + .HasMany() + .WithMany() + .UsingEntity( + i => i.HasKey(e => new { e.IdUser, e.IdQuote }) + ); + + modelBuilder.Entity() + .HasOne(c => c.User) + .WithMany() + .HasForeignKey(c => c.IdUser); + + modelBuilder.Entity() + .HasMany(q => q.Commentarys) + .WithOne(c => c.Quote) + .OnDelete(DeleteBehavior.ClientCascade) + .HasForeignKey(c => c.IdQuote); + modelBuilder.Entity() .HasMany(q => q.Questions) .WithMany(u => u.Quizs) diff --git a/WF_EF_Api/Entity/Commentary.cs b/WF_EF_Api/Entity/Commentary.cs index 07649d2..92b2908 100644 --- a/WF_EF_Api/Entity/Commentary.cs +++ b/WF_EF_Api/Entity/Commentary.cs @@ -16,7 +16,7 @@ namespace Entity [Required] [ForeignKey(nameof(Users))] - public int IdUsers { get; set; } + public int IdUser { get; set; } [Required] [ForeignKey(nameof(Quote))] @@ -32,6 +32,6 @@ namespace Entity public Quote Quote { get; set; } = null!; - public Users Users { get; set; } = null!; + public Users User { get; set; } = null!; } } diff --git a/WF_EF_Api/Entity/Quote.cs b/WF_EF_Api/Entity/Quote.cs index 976f62e..37d5f00 100644 --- a/WF_EF_Api/Entity/Quote.cs +++ b/WF_EF_Api/Entity/Quote.cs @@ -15,7 +15,7 @@ namespace Entity public int Id { get; set; } [Required] - [StringLength(50)] + [StringLength(100)] public string Content { get; set; } [Required] @@ -35,11 +35,12 @@ namespace Entity [ForeignKey(nameof(Source))] public int IdSource { get; set; } - [Required] [ForeignKey(nameof(Users))] - public int IdUsersPropose { get; set; } - - public Users User { get; set; } = null!; + public int? IdUsersPropose { get; set; } + //Réson de pour quoi j'ai mis le user en nullable et mis .OnDelete(DeleteBehavior.ClientSetNull) dans WTFContext + //https://learn.microsoft.com/fr-fr/ef/core/saving/cascade-delete + //Les suppressions en cascade sont nécessaires quand une entité dépendante/enfant ne peut plus être associée à son entité principale/parente actuelle. Cela peut se produire à la suite de la suppression de l’entité principale/parente, ou quand l’entité principale/parente existe toujours mais que l’entité dépendante/enfant ne lui est plus associée. + public Users? User { get; set; } = null!; public Source Source { get; set; } = null!; @@ -47,7 +48,7 @@ namespace Entity public ICollection DailyQuotes { get; set; } = new List(); - public ICollection commentaries { get; set; } = new List(); + public ICollection Commentarys { get; set; } = new List(); public ICollection Favorite { get; } = new List(); } diff --git a/WF_EF_Api/Entity/Source.cs b/WF_EF_Api/Entity/Source.cs index 4a9f77c..60cda07 100644 --- a/WF_EF_Api/Entity/Source.cs +++ b/WF_EF_Api/Entity/Source.cs @@ -15,7 +15,7 @@ namespace Entity public int Id { get; set; } [Required] - [StringLength(50)] + [StringLength(100)] public string Title { get; set; } [Required] diff --git a/WF_EF_Api/Entity/Users.cs b/WF_EF_Api/Entity/Users.cs index 80e38a7..e7a8101 100644 --- a/WF_EF_Api/Entity/Users.cs +++ b/WF_EF_Api/Entity/Users.cs @@ -37,8 +37,6 @@ namespace Entity public ICollection Quotes { get; set; } = new List(); - public ICollection Commentary { get; set; } = new List(); - public ICollection Favorite { get; set; } = new List(); } } diff --git a/WF_EF_Api/StubbedContextLib/Migrations/20250314112216_migrationTest1.Designer.cs b/WF_EF_Api/StubbedContextLib/Migrations/20250317163102_migrationTest1.Designer.cs similarity index 94% rename from WF_EF_Api/StubbedContextLib/Migrations/20250314112216_migrationTest1.Designer.cs rename to WF_EF_Api/StubbedContextLib/Migrations/20250317163102_migrationTest1.Designer.cs index d466b28..e352e28 100644 --- a/WF_EF_Api/StubbedContextLib/Migrations/20250314112216_migrationTest1.Designer.cs +++ b/WF_EF_Api/StubbedContextLib/Migrations/20250317163102_migrationTest1.Designer.cs @@ -12,7 +12,7 @@ using StubbedContextLib; namespace StubbedContextLib.Migrations { [DbContext(typeof(StubWTFContext))] - [Migration("20250314112216_migrationTest1")] + [Migration("20250317163102_migrationTest1")] partial class migrationTest1 { /// @@ -112,11 +112,11 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Commentary", b => { - b.Property("Id") - .ValueGeneratedOnAdd() + b.Property("IdUser") .HasColumnType("int"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("IdQuote") + .HasColumnType("int"); b.Property("Comment") .IsRequired() @@ -127,36 +127,34 @@ namespace StubbedContextLib.Migrations .HasColumnType("date") .HasColumnName("DateCommentary"); - b.Property("IdQuote") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("IdUsers") - .HasColumnType("int"); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.HasKey("Id"); + b.HasKey("IdUser", "IdQuote"); b.HasIndex("IdQuote"); - b.HasIndex("IdUsers"); - b.ToTable("comments"); b.HasData( new { - Id = 1, + IdUser = 2, + IdQuote = 1, Comment = "Ce film est le meilleur", DateCommentary = new DateTime(2025, 2, 3, 0, 0, 0, 0, DateTimeKind.Unspecified), - IdQuote = 1, - IdUsers = 2 + Id = 1 }, new { - Id = 2, + IdUser = 3, + IdQuote = 1, Comment = "Very good", DateCommentary = new DateTime(2025, 3, 11, 0, 0, 0, 0, DateTimeKind.Unspecified), - IdQuote = 1, - IdUsers = 3 + Id = 2 }); }); @@ -575,8 +573,8 @@ namespace StubbedContextLib.Migrations b.Property("Content") .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("IdCharacter") .HasColumnType("int"); @@ -584,7 +582,7 @@ namespace StubbedContextLib.Migrations b.Property("IdSource") .HasColumnType("int"); - b.Property("IdUsersPropose") + b.Property("IdUsersPropose") .HasColumnType("int"); b.Property("IsValid") @@ -729,8 +727,8 @@ namespace StubbedContextLib.Migrations b.Property("Title") .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("TypeSrc") .HasColumnType("int"); @@ -923,20 +921,20 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Commentary", b => { b.HasOne("Entity.Quote", "Quote") - .WithMany("commentaries") + .WithMany("Commentarys") .HasForeignKey("IdQuote") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.ClientCascade) .IsRequired(); - b.HasOne("Entity.Users", "Users") - .WithMany("Commentary") - .HasForeignKey("IdUsers") + b.HasOne("Entity.Users", "User") + .WithMany() + .HasForeignKey("IdUser") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Quote"); - b.Navigation("Users"); + b.Navigation("User"); }); modelBuilder.Entity("Entity.DailyQuote", b => @@ -955,13 +953,13 @@ namespace StubbedContextLib.Migrations b.HasOne("Entity.Quote", "Quote") .WithMany() .HasForeignKey("IdQuote") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.ClientCascade) .IsRequired(); b.HasOne("Entity.Users", "Users") .WithMany() .HasForeignKey("IdUsers") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.ClientCascade) .IsRequired(); b.Navigation("Quote"); @@ -1011,9 +1009,7 @@ namespace StubbedContextLib.Migrations b.HasOne("Entity.Users", "User") .WithMany("Quotes") - .HasForeignKey("IdUsersPropose") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("IdUsersPropose"); b.Navigation("Character"); @@ -1049,9 +1045,9 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Quote", b => { - b.Navigation("DailyQuotes"); + b.Navigation("Commentarys"); - b.Navigation("commentaries"); + b.Navigation("DailyQuotes"); }); modelBuilder.Entity("Entity.Source", b => @@ -1061,8 +1057,6 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Users", b => { - b.Navigation("Commentary"); - b.Navigation("Quotes"); }); #pragma warning restore 612, 618 diff --git a/WF_EF_Api/StubbedContextLib/Migrations/20250314112216_migrationTest1.cs b/WF_EF_Api/StubbedContextLib/Migrations/20250317163102_migrationTest1.cs similarity index 92% rename from WF_EF_Api/StubbedContextLib/Migrations/20250314112216_migrationTest1.cs rename to WF_EF_Api/StubbedContextLib/Migrations/20250317163102_migrationTest1.cs index a85f30b..437370b 100644 --- a/WF_EF_Api/StubbedContextLib/Migrations/20250314112216_migrationTest1.cs +++ b/WF_EF_Api/StubbedContextLib/Migrations/20250317163102_migrationTest1.cs @@ -50,7 +50,7 @@ namespace StubbedContextLib.Migrations { Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - Title = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Title = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), Year = table.Column(type: "int", nullable: false), TypeSrc = table.Column(type: "int", nullable: false) }, @@ -153,13 +153,13 @@ namespace StubbedContextLib.Migrations { Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - Content = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Content = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), Likes = table.Column(type: "int", nullable: false), Langage = table.Column(type: "int", nullable: false), IsValid = table.Column(type: "bit", nullable: false), IdCharacter = table.Column(type: "int", nullable: false), IdSource = table.Column(type: "int", nullable: false), - IdUsersPropose = table.Column(type: "int", nullable: false) + IdUsersPropose = table.Column(type: "int", nullable: true) }, constraints: table => { @@ -180,33 +180,31 @@ namespace StubbedContextLib.Migrations name: "FK_quotes_users_IdUsersPropose", column: x => x.IdUsersPropose, principalTable: "users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalColumn: "Id"); }); migrationBuilder.CreateTable( name: "comments", columns: table => new { + IdUser = table.Column(type: "int", nullable: false), + IdQuote = table.Column(type: "int", nullable: false), Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - IdUsers = table.Column(type: "int", nullable: false), - IdQuote = table.Column(type: "int", nullable: false), DateCommentary = table.Column(type: "date", nullable: false), Comment = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) }, constraints: table => { - table.PrimaryKey("PK_comments", x => x.Id); + table.PrimaryKey("PK_comments", x => new { x.IdUser, x.IdQuote }); table.ForeignKey( name: "FK_comments_quotes_IdQuote", column: x => x.IdQuote, principalTable: "quotes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalColumn: "Id"); table.ForeignKey( - name: "FK_comments_users_IdUsers", - column: x => x.IdUsers, + name: "FK_comments_users_IdUser", + column: x => x.IdUser, principalTable: "users", principalColumn: "Id", onDelete: ReferentialAction.Cascade); @@ -243,14 +241,12 @@ namespace StubbedContextLib.Migrations name: "FK_favorites_quotes_IdQuote", column: x => x.IdQuote, principalTable: "quotes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalColumn: "Id"); table.ForeignKey( name: "FK_favorites_users_IdUsers", column: x => x.IdUsers, principalTable: "users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalColumn: "Id"); }); migrationBuilder.InsertData( @@ -378,11 +374,11 @@ namespace StubbedContextLib.Migrations migrationBuilder.InsertData( table: "comments", - columns: new[] { "Id", "Comment", "DateCommentary", "IdQuote", "IdUsers" }, + columns: new[] { "IdQuote", "IdUser", "Comment", "DateCommentary", "Id" }, values: new object[,] { - { 1, "Ce film est le meilleur", new DateTime(2025, 2, 3, 0, 0, 0, 0, DateTimeKind.Unspecified), 1, 2 }, - { 2, "Very good", new DateTime(2025, 3, 11, 0, 0, 0, 0, DateTimeKind.Unspecified), 1, 3 } + { 1, 2, "Ce film est le meilleur", new DateTime(2025, 2, 3, 0, 0, 0, 0, DateTimeKind.Unspecified), 1 }, + { 1, 3, "Very good", new DateTime(2025, 3, 11, 0, 0, 0, 0, DateTimeKind.Unspecified), 2 } }); migrationBuilder.InsertData( @@ -420,11 +416,6 @@ namespace StubbedContextLib.Migrations table: "comments", column: "IdQuote"); - migrationBuilder.CreateIndex( - name: "IX_comments_IdUsers", - table: "comments", - column: "IdUsers"); - migrationBuilder.CreateIndex( name: "IX_favorites_IdUsers", table: "favorites", diff --git a/WF_EF_Api/StubbedContextLib/Migrations/StubWTFContextModelSnapshot.cs b/WF_EF_Api/StubbedContextLib/Migrations/StubWTFContextModelSnapshot.cs index 21818ec..275cb8a 100644 --- a/WF_EF_Api/StubbedContextLib/Migrations/StubWTFContextModelSnapshot.cs +++ b/WF_EF_Api/StubbedContextLib/Migrations/StubWTFContextModelSnapshot.cs @@ -109,11 +109,11 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Commentary", b => { - b.Property("Id") - .ValueGeneratedOnAdd() + b.Property("IdUser") .HasColumnType("int"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("IdQuote") + .HasColumnType("int"); b.Property("Comment") .IsRequired() @@ -124,36 +124,34 @@ namespace StubbedContextLib.Migrations .HasColumnType("date") .HasColumnName("DateCommentary"); - b.Property("IdQuote") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("IdUsers") - .HasColumnType("int"); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.HasKey("Id"); + b.HasKey("IdUser", "IdQuote"); b.HasIndex("IdQuote"); - b.HasIndex("IdUsers"); - b.ToTable("comments"); b.HasData( new { - Id = 1, + IdUser = 2, + IdQuote = 1, Comment = "Ce film est le meilleur", DateCommentary = new DateTime(2025, 2, 3, 0, 0, 0, 0, DateTimeKind.Unspecified), - IdQuote = 1, - IdUsers = 2 + Id = 1 }, new { - Id = 2, + IdUser = 3, + IdQuote = 1, Comment = "Very good", DateCommentary = new DateTime(2025, 3, 11, 0, 0, 0, 0, DateTimeKind.Unspecified), - IdQuote = 1, - IdUsers = 3 + Id = 2 }); }); @@ -572,8 +570,8 @@ namespace StubbedContextLib.Migrations b.Property("Content") .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("IdCharacter") .HasColumnType("int"); @@ -581,7 +579,7 @@ namespace StubbedContextLib.Migrations b.Property("IdSource") .HasColumnType("int"); - b.Property("IdUsersPropose") + b.Property("IdUsersPropose") .HasColumnType("int"); b.Property("IsValid") @@ -726,8 +724,8 @@ namespace StubbedContextLib.Migrations b.Property("Title") .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("TypeSrc") .HasColumnType("int"); @@ -920,20 +918,20 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Commentary", b => { b.HasOne("Entity.Quote", "Quote") - .WithMany("commentaries") + .WithMany("Commentarys") .HasForeignKey("IdQuote") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.ClientCascade) .IsRequired(); - b.HasOne("Entity.Users", "Users") - .WithMany("Commentary") - .HasForeignKey("IdUsers") + b.HasOne("Entity.Users", "User") + .WithMany() + .HasForeignKey("IdUser") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Quote"); - b.Navigation("Users"); + b.Navigation("User"); }); modelBuilder.Entity("Entity.DailyQuote", b => @@ -952,13 +950,13 @@ namespace StubbedContextLib.Migrations b.HasOne("Entity.Quote", "Quote") .WithMany() .HasForeignKey("IdQuote") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.ClientCascade) .IsRequired(); b.HasOne("Entity.Users", "Users") .WithMany() .HasForeignKey("IdUsers") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.ClientCascade) .IsRequired(); b.Navigation("Quote"); @@ -1008,9 +1006,7 @@ namespace StubbedContextLib.Migrations b.HasOne("Entity.Users", "User") .WithMany("Quotes") - .HasForeignKey("IdUsersPropose") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("IdUsersPropose"); b.Navigation("Character"); @@ -1046,9 +1042,9 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Quote", b => { - b.Navigation("DailyQuotes"); + b.Navigation("Commentarys"); - b.Navigation("commentaries"); + b.Navigation("DailyQuotes"); }); modelBuilder.Entity("Entity.Source", b => @@ -1058,8 +1054,6 @@ namespace StubbedContextLib.Migrations modelBuilder.Entity("Entity.Users", b => { - b.Navigation("Commentary"); - b.Navigation("Quotes"); }); #pragma warning restore 612, 618 diff --git a/WF_EF_Api/StubbedContextLib/StubWTFContext.cs b/WF_EF_Api/StubbedContextLib/StubWTFContext.cs index 6df9912..ffc1d06 100644 --- a/WF_EF_Api/StubbedContextLib/StubWTFContext.cs +++ b/WF_EF_Api/StubbedContextLib/StubWTFContext.cs @@ -72,8 +72,8 @@ namespace StubbedContextLib ); modelBuilder.Entity().HasData( - new Commentary() { Id = 1, Comment = "Ce film est le meilleur", DateCommentary = new DateTime(2025,2,3), IdQuote = 1, IdUsers = 2 }, - new Commentary() { Id = 2, Comment = "Very good", DateCommentary = new DateTime(2025, 3, 11), IdQuote = 1, IdUsers = 3 } + new Commentary() { Id = 1, Comment = "Ce film est le meilleur", DateCommentary = new DateTime(2025,2,3), IdQuote = 1, IdUser = 2 }, + new Commentary() { Id = 2, Comment = "Very good", DateCommentary = new DateTime(2025, 3, 11), IdQuote = 1, IdUser = 3 } ); modelBuilder.Entity().HasData( diff --git a/WF_EF_Api/StubbedContextLib/StubbedContextLib.csproj b/WF_EF_Api/StubbedContextLib/StubbedContextLib.csproj index 4ae6e1b..9a838f3 100644 --- a/WF_EF_Api/StubbedContextLib/StubbedContextLib.csproj +++ b/WF_EF_Api/StubbedContextLib/StubbedContextLib.csproj @@ -18,8 +18,4 @@ - - - -