#61 impovement on data structures

This commit is contained in:
Hamid Reza Mohammadi 2021-01-12 22:04:49 +03:30
parent f75c62a40e
commit 7123d5d017
8 changed files with 261 additions and 146 deletions

View File

@ -204,15 +204,21 @@ namespace RMuseum.DbContext
/// </summary>
public DbSet<GanjoorMetre> GanjoorMetres { get; set; }
/// <summary>
/// singers
/// </summary>
public DbSet<GanjoorSinger> Singers { get; set; }
public DbSet<GanjoorSinger> GanjoorSingers { get; set; }
/// <summary>
/// music tracks
/// </summary>
public DbSet<GanjoorTrack> MusicTracks { get; set; }
public DbSet<GanjoorTrack> GanjoorMusicCatalogueTracks { get; set; }
/// <summary>
/// golha tracks
/// </summary>
public DbSet<GolhaTrack> GolhaTracks { get; set; }
/// <summary>
/// GolhaCollection
@ -222,7 +228,8 @@ namespace RMuseum.DbContext
/// <summary>
/// PoemMusicTracks
/// </summary>
public DbSet<PoemMusicTrack> PoemMusicTracks { get; set; }
public DbSet<PoemMusicTrack> GanjoorPoemMusicTracks { get; set; }
}
}

View File

@ -10,7 +10,7 @@ using RMuseum.DbContext;
namespace RMuseum.Migrations
{
[DbContext(typeof(RMuseumDbContext))]
[Migration("20210111084102_GanjoorMusicCatalogue")]
[Migration("20210112141927_GanjoorMusicCatalogue")]
partial class GanjoorMusicCatalogue
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -618,7 +618,7 @@ namespace RMuseum.Migrations
b.HasIndex("SuggestedById");
b.ToTable("PoemMusicTracks");
b.ToTable("GanjoorPoemMusicTracks");
});
modelBuilder.Entity("RMuseum.Models.GanjoorAudio.Recitation", b =>
@ -996,7 +996,7 @@ namespace RMuseum.Migrations
b.HasIndex("Name");
b.ToTable("Singers");
b.ToTable("GanjoorSingers");
});
modelBuilder.Entity("RMuseum.Models.MusicCatalogue.GanjoorTrack", b =>
@ -1027,7 +1027,7 @@ namespace RMuseum.Migrations
b.HasIndex("Name");
b.ToTable("MusicTracks");
b.ToTable("GanjoorMusicCatalogueTracks");
});
modelBuilder.Entity("RMuseum.Models.MusicCatalogue.GolhaCollection", b =>
@ -1073,9 +1073,7 @@ namespace RMuseum.Migrations
modelBuilder.Entity("RMuseum.Models.MusicCatalogue.GolhaTrack", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
.HasColumnType("int");
b.Property<string>("BlockReason")
.HasColumnType("nvarchar(max)");
@ -1104,7 +1102,7 @@ namespace RMuseum.Migrations
b.HasIndex("SingerId");
b.ToTable("GolhaTrack");
b.ToTable("GolhaTracks");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>

View File

@ -7,6 +7,20 @@ namespace RMuseum.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "GanjoorSingers",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(nullable: true),
Url = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorSingers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GolhaCollections",
columns: table => new
@ -20,17 +34,24 @@ namespace RMuseum.Migrations
});
migrationBuilder.CreateTable(
name: "Singers",
name: "GanjoorAlbum",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
SingerId = table.Column<int>(nullable: false),
Name = table.Column<string>(nullable: true),
Url = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Singers", x => x.Id);
table.PrimaryKey("PK_GanjoorAlbum", x => x.Id);
table.ForeignKey(
name: "FK_GanjoorAlbum_GanjoorSingers_SingerId",
column: x => x.SingerId,
principalTable: "GanjoorSingers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
@ -56,59 +77,7 @@ namespace RMuseum.Migrations
});
migrationBuilder.CreateTable(
name: "GanjoorAlbum",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
SingerId = table.Column<int>(nullable: false),
Name = table.Column<string>(nullable: true),
Url = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GanjoorAlbum", x => x.Id);
table.ForeignKey(
name: "FK_GanjoorAlbum_Singers_SingerId",
column: x => x.SingerId,
principalTable: "Singers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "GolhaTrack",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GolhaProgramId = table.Column<int>(nullable: false),
TrackNo = table.Column<int>(nullable: false),
Timing = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
SingerId = table.Column<int>(nullable: true),
Blocked = table.Column<bool>(nullable: false),
BlockReason = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GolhaTrack", x => x.Id);
table.ForeignKey(
name: "FK_GolhaTrack_GolhaProgram_GolhaProgramId",
column: x => x.GolhaProgramId,
principalTable: "GolhaProgram",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_GolhaTrack_Singers_SingerId",
column: x => x.SingerId,
principalTable: "Singers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "MusicTracks",
name: "GanjoorMusicCatalogueTracks",
columns: table => new
{
Id = table.Column<int>(nullable: false)
@ -121,9 +90,9 @@ namespace RMuseum.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_MusicTracks", x => x.Id);
table.PrimaryKey("PK_GanjoorMusicCatalogueTracks", x => x.Id);
table.ForeignKey(
name: "FK_MusicTracks_GanjoorAlbum_AlbumId",
name: "FK_GanjoorMusicCatalogueTracks_GanjoorAlbum_AlbumId",
column: x => x.AlbumId,
principalTable: "GanjoorAlbum",
principalColumn: "Id",
@ -131,7 +100,37 @@ namespace RMuseum.Migrations
});
migrationBuilder.CreateTable(
name: "PoemMusicTracks",
name: "GolhaTracks",
columns: table => new
{
Id = table.Column<int>(nullable: false),
GolhaProgramId = table.Column<int>(nullable: false),
TrackNo = table.Column<int>(nullable: false),
Timing = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
SingerId = table.Column<int>(nullable: true),
Blocked = table.Column<bool>(nullable: false),
BlockReason = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GolhaTracks", x => x.Id);
table.ForeignKey(
name: "FK_GolhaTracks_GolhaProgram_GolhaProgramId",
column: x => x.GolhaProgramId,
principalTable: "GolhaProgram",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_GolhaTracks_GanjoorSingers_SingerId",
column: x => x.SingerId,
principalTable: "GanjoorSingers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "GanjoorPoemMusicTracks",
columns: table => new
{
Id = table.Column<int>(nullable: false)
@ -155,33 +154,33 @@ namespace RMuseum.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_PoemMusicTracks", x => x.Id);
table.PrimaryKey("PK_GanjoorPoemMusicTracks", x => x.Id);
table.ForeignKey(
name: "FK_PoemMusicTracks_MusicTracks_GanjoorTrackId",
name: "FK_GanjoorPoemMusicTracks_GanjoorMusicCatalogueTracks_GanjoorTrackId",
column: x => x.GanjoorTrackId,
principalTable: "MusicTracks",
principalTable: "GanjoorMusicCatalogueTracks",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PoemMusicTracks_GolhaTrack_GolhaTrackId",
name: "FK_GanjoorPoemMusicTracks_GolhaTracks_GolhaTrackId",
column: x => x.GolhaTrackId,
principalTable: "GolhaTrack",
principalTable: "GolhaTracks",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PoemMusicTracks_GanjoorPoems_PoemId",
name: "FK_GanjoorPoemMusicTracks_GanjoorPoems_PoemId",
column: x => x.PoemId,
principalTable: "GanjoorPoems",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PoemMusicTracks_Singers_SingerId",
name: "FK_GanjoorPoemMusicTracks_GanjoorSingers_SingerId",
column: x => x.SingerId,
principalTable: "Singers",
principalTable: "GanjoorSingers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PoemMusicTracks_AspNetUsers_SuggestedById",
name: "FK_GanjoorPoemMusicTracks_AspNetUsers_SuggestedById",
column: x => x.SuggestedById,
principalTable: "AspNetUsers",
principalColumn: "Id",
@ -193,72 +192,72 @@ namespace RMuseum.Migrations
table: "GanjoorAlbum",
column: "SingerId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorMusicCatalogueTracks_AlbumId",
table: "GanjoorMusicCatalogueTracks",
column: "AlbumId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorMusicCatalogueTracks_Name",
table: "GanjoorMusicCatalogueTracks",
column: "Name");
migrationBuilder.CreateIndex(
name: "IX_GanjoorPoemMusicTracks_GanjoorTrackId",
table: "GanjoorPoemMusicTracks",
column: "GanjoorTrackId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorPoemMusicTracks_GolhaTrackId",
table: "GanjoorPoemMusicTracks",
column: "GolhaTrackId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorPoemMusicTracks_PoemId",
table: "GanjoorPoemMusicTracks",
column: "PoemId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorPoemMusicTracks_SingerId",
table: "GanjoorPoemMusicTracks",
column: "SingerId");
migrationBuilder.CreateIndex(
name: "IX_GanjoorPoemMusicTracks_SuggestedById",
table: "GanjoorPoemMusicTracks",
column: "SuggestedById");
migrationBuilder.CreateIndex(
name: "IX_GanjoorSingers_Name",
table: "GanjoorSingers",
column: "Name");
migrationBuilder.CreateIndex(
name: "IX_GolhaProgram_GolhaCollectionId",
table: "GolhaProgram",
column: "GolhaCollectionId");
migrationBuilder.CreateIndex(
name: "IX_GolhaTrack_GolhaProgramId",
table: "GolhaTrack",
name: "IX_GolhaTracks_GolhaProgramId",
table: "GolhaTracks",
column: "GolhaProgramId");
migrationBuilder.CreateIndex(
name: "IX_GolhaTrack_SingerId",
table: "GolhaTrack",
name: "IX_GolhaTracks_SingerId",
table: "GolhaTracks",
column: "SingerId");
migrationBuilder.CreateIndex(
name: "IX_MusicTracks_AlbumId",
table: "MusicTracks",
column: "AlbumId");
migrationBuilder.CreateIndex(
name: "IX_MusicTracks_Name",
table: "MusicTracks",
column: "Name");
migrationBuilder.CreateIndex(
name: "IX_PoemMusicTracks_GanjoorTrackId",
table: "PoemMusicTracks",
column: "GanjoorTrackId");
migrationBuilder.CreateIndex(
name: "IX_PoemMusicTracks_GolhaTrackId",
table: "PoemMusicTracks",
column: "GolhaTrackId");
migrationBuilder.CreateIndex(
name: "IX_PoemMusicTracks_PoemId",
table: "PoemMusicTracks",
column: "PoemId");
migrationBuilder.CreateIndex(
name: "IX_PoemMusicTracks_SingerId",
table: "PoemMusicTracks",
column: "SingerId");
migrationBuilder.CreateIndex(
name: "IX_PoemMusicTracks_SuggestedById",
table: "PoemMusicTracks",
column: "SuggestedById");
migrationBuilder.CreateIndex(
name: "IX_Singers_Name",
table: "Singers",
column: "Name");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PoemMusicTracks");
name: "GanjoorPoemMusicTracks");
migrationBuilder.DropTable(
name: "MusicTracks");
name: "GanjoorMusicCatalogueTracks");
migrationBuilder.DropTable(
name: "GolhaTrack");
name: "GolhaTracks");
migrationBuilder.DropTable(
name: "GanjoorAlbum");
@ -267,7 +266,7 @@ namespace RMuseum.Migrations
name: "GolhaProgram");
migrationBuilder.DropTable(
name: "Singers");
name: "GanjoorSingers");
migrationBuilder.DropTable(
name: "GolhaCollections");

View File

@ -616,7 +616,7 @@ namespace RMuseum.Migrations
b.HasIndex("SuggestedById");
b.ToTable("PoemMusicTracks");
b.ToTable("GanjoorPoemMusicTracks");
});
modelBuilder.Entity("RMuseum.Models.GanjoorAudio.Recitation", b =>
@ -994,7 +994,7 @@ namespace RMuseum.Migrations
b.HasIndex("Name");
b.ToTable("Singers");
b.ToTable("GanjoorSingers");
});
modelBuilder.Entity("RMuseum.Models.MusicCatalogue.GanjoorTrack", b =>
@ -1025,7 +1025,7 @@ namespace RMuseum.Migrations
b.HasIndex("Name");
b.ToTable("MusicTracks");
b.ToTable("GanjoorMusicCatalogueTracks");
});
modelBuilder.Entity("RMuseum.Models.MusicCatalogue.GolhaCollection", b =>
@ -1071,9 +1071,7 @@ namespace RMuseum.Migrations
modelBuilder.Entity("RMuseum.Models.MusicCatalogue.GolhaTrack", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
.HasColumnType("int");
b.Property<string>("BlockReason")
.HasColumnType("nvarchar(max)");
@ -1102,7 +1100,7 @@ namespace RMuseum.Migrations
b.HasIndex("SingerId");
b.ToTable("GolhaTrack");
b.ToTable("GolhaTracks");
});
modelBuilder.Entity("RMuseum.Models.Note.RUserNote", b =>

View File

@ -1,7 +1,14 @@
namespace RMuseum.Models.MusicCatalogue
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RMuseum.Models.MusicCatalogue
{
public class GolhaTrack
{
/// <summary>
/// id
/// </summary>
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
/// <summary>
/// id
/// </summary>

View File

@ -1226,22 +1226,27 @@
Ganjoor Metres
</summary>
</member>
<member name="P:RMuseum.DbContext.RMuseumDbContext.Singers">
<member name="P:RMuseum.DbContext.RMuseumDbContext.GanjoorSingers">
<summary>
singers
</summary>
</member>
<member name="P:RMuseum.DbContext.RMuseumDbContext.MusicTracks">
<member name="P:RMuseum.DbContext.RMuseumDbContext.GanjoorMusicCatalogueTracks">
<summary>
music tracks
</summary>
</member>
<member name="P:RMuseum.DbContext.RMuseumDbContext.GolhaTracks">
<summary>
golha tracks
</summary>
</member>
<member name="P:RMuseum.DbContext.RMuseumDbContext.GolhaCollections">
<summary>
GolhaCollection
</summary>
</member>
<member name="P:RMuseum.DbContext.RMuseumDbContext.PoemMusicTracks">
<member name="P:RMuseum.DbContext.RMuseumDbContext.GanjoorPoemMusicTracks">
<summary>
PoemMusicTracks
</summary>

View File

@ -9,7 +9,9 @@ using RMuseum.Models.Ganjoor.ViewModels;
using RMuseum.Models.GanjoorAudio;
using RMuseum.Models.GanjoorAudio.ViewModels;
using RMuseum.Models.GanjoorIntegration.ViewModels;
using RMuseum.Models.MusicCatalogue;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Models.Generic.Db;
using RSecurityBackend.Services;
using RSecurityBackend.Services.Implementation;
using System;
@ -960,8 +962,18 @@ namespace RMuseum.Services.Implementation
using (RMuseumDbContext contextReport = new RMuseumDbContext(Configuration)) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(contextReport);
var job = (await jobProgressServiceEF.NewJob("GanjoorService:ImportFromMySql", "pre open connection")).Result;
MusicCatalogueService catalogueService = new MusicCatalogueService(Configuration);
RServiceResult<bool> musicCatalogueRes = await catalogueService.ImportFromMySql(jobProgressServiceEF, job);
if (!musicCatalogueRes.Result)
return;
try
{
@ -1062,13 +1074,14 @@ namespace RMuseum.Services.Implementation
}
context.GanjoorPages.Add(page);
await context.SaveChangesAsync();
}
}
}
}
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 1 - finalizing")).Result;
await context.SaveChangesAsync();
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 2 - pre fetch data")).Result;
@ -1135,11 +1148,11 @@ namespace RMuseum.Services.Implementation
page.FullTitle = fullTitle;
context.Update(page);
await context.SaveChangesAsync();
}
job = (await jobProgressServiceEF.UpdateJob(job.Id, job.Progress, "phase 2 - finalizing")).Result;
await context.SaveChangesAsync();
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 3 - pre mysql data fetch")).Result;
@ -1214,8 +1227,10 @@ namespace RMuseum.Services.Implementation
}
MusicCatalogueService catalogueService = new MusicCatalogueService(Configuration);
await catalogueService.ImportFromMySql(jobProgressServiceEF, job);
await _ImportPoemSongsDataFromMySql(context, jobProgressServiceEF, job);
await jobProgressServiceEF.UpdateJob(job.Id, 100, "Finished", true);
}
@ -1235,6 +1250,91 @@ namespace RMuseum.Services.Implementation
}
}
private async Task<RServiceResult<bool>> _ImportPoemSongsDataFromMySql(RMuseumDbContext context, LongRunningJobProgressServiceEF jobProgressServiceEF, RLongRunningJobStatus job)
{
try
{
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 5 - pre mysql data fetch")).Result;
using (MySqlConnection connection = new MySqlConnection
(
$"server={Configuration.GetSection("AudioMySqlServer")["Server"]};uid={Configuration.GetSection("AudioMySqlServer")["Username"]};pwd={Configuration.GetSection("AudioMySqlServer")["Password"]};database={Configuration.GetSection("AudioMySqlServer")["Database"]};charset=utf8;convert zero datetime=True"
))
{
connection.Open();
using (MySqlDataAdapter src = new MySqlDataAdapter(
"SELECT poem_id, artist_name, artist_beeptunesurl, album_name, album_beeptunesurl, track_name, track_beeptunesurl, ptrack_typeid FROM ganja_ptracks ORDER BY id",
connection))
{
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 5 - mysql")).Result;
using (DataTable data = new DataTable())
{
await src.FillAsync(data);
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 5 - processing approved poem songs")).Result;
foreach (DataRow row in data.Rows)
{
PoemMusicTrack track = new PoemMusicTrack()
{
TrackType = (PoemMusicTrackType)int.Parse(row["ptrack_typeid"].ToString()),
PoemId = int.Parse(row["poem_id"].ToString()),
ArtistName = row["artist_name"].ToString(),
ArtistUrl = row["artist_beeptunesurl"].ToString(),
AlbumName = row["album_name"].ToString(),
AlbumUrl = row["album_beeptunesurl"].ToString(),
TrackName = row["track_name"].ToString(),
TrackUrl = row["track_beeptunesurl"].ToString()
};
switch(track.TrackType)
{
case PoemMusicTrackType.BeepTunesOrKhosousi:
case PoemMusicTrackType.iTunes:
{
GanjoorTrack catalogueTrack = await context.GanjoorMusicCatalogueTracks.Where(m => m.Url == track.TrackUrl).SingleOrDefaultAsync();
if(catalogueTrack != null)
{
track.GanjoorTrackId = catalogueTrack.Id;
}
}
break;
case PoemMusicTrackType.Golha:
{
track.AlbumName = $"{track.ArtistName} » {track.AlbumName}";
track.ArtistName = "";
int golhaTrackId = int.Parse(track.AlbumUrl);
GolhaTrack golhaTrack = await context.GolhaTracks.Where(g => g.Id == golhaTrackId).SingleOrDefaultAsync();
if(golhaTrack != null)
{
track.GolhaTrackId = golhaTrack.Id;
track.AlbumUrl = "";
}
}
break;
}
context.GanjoorPoemMusicTracks.Add(track);
}
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, "phase 5 - finalizing approved poem songs data")).Result;
await context.SaveChangesAsync();
}
}
}
return new RServiceResult<bool>(true);
}
catch(Exception exp)
{
return new RServiceResult<bool>(false, exp.ToString());
}
}
private List<GanjoorVerse> _extractVersesFromPoemHtmlText(string poemtext)
{
List<GanjoorVerse> verses = new List<GanjoorVerse>();

View File

@ -82,7 +82,7 @@ namespace RMuseum.Services.Implementation
};
using (MySqlDataAdapter srcTracks = new MySqlDataAdapter(
$"SELECT track_no, timing, title FROM golha_tracks WHERE program_id = {program.Id} ORDER BY track_no",
$"SELECT track_id, track_no, timing, title FROM golha_tracks WHERE program_id = {program.Id} ORDER BY track_no",
connection))
{
using (DataTable trackData = new DataTable())
@ -95,6 +95,7 @@ namespace RMuseum.Services.Implementation
(
new GolhaTrack()
{
Id = int.Parse(golhaTrack["track_id"].ToString()),
TrackNo = int.Parse(golhaTrack["track_no"].ToString()),
Timing = golhaTrack["timing"].ToString(),
Title = golhaTrack["title"].ToString(),
@ -194,7 +195,7 @@ namespace RMuseum.Services.Implementation
context.Singers.Add(singer);
context.GanjoorSingers.Add(singer);
job = (await jobProgressServiceEF.UpdateJob(job.Id, 0, $"phase 4 - import singers data - {singer.Name}")).Result;