This commit is contained in:
Максим Сергунов 2023-05-12 12:35:01 +04:00
parent 80e8ff165c
commit 83f78b7334
20 changed files with 1646 additions and 2 deletions

View File

@ -3,9 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505 VisualStudioVersion = 17.5.33530.505
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookShopView", "BookShopView\BookShopView.csproj", "{1455C2BC-5C22-4B93-8954-ACC01353BD24}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookShopView", "BookShopView\BookShopView.csproj", "{1455C2BC-5C22-4B93-8954-ACC01353BD24}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookShopDataModels", "BookShopDataModels\BookShopDataModels.csproj", "{032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookShopDataModels", "BookShopDataModels\BookShopDataModels.csproj", "{032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookShopContracts", "BookShopContracts\BookShopContracts.csproj", "{0F724A0C-BAE5-4784-A7C5-FCCF7BE07EA5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookShopBusinessLogic", "BookShopBusinessLogic\BookShopBusinessLogic.csproj", "{CA4B49FE-3671-4440-9E06-9B5F8D0CDB58}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookShopDataBaseImplement", "BookShopDataBaseImplement\BookShopDataBaseImplement.csproj", "{EF559D7F-C94F-4F76-833E-44DF7FEDEDBD}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -21,6 +27,18 @@ Global
{032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}.Debug|Any CPU.Build.0 = Debug|Any CPU {032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}.Release|Any CPU.ActiveCfg = Release|Any CPU {032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}.Release|Any CPU.Build.0 = Release|Any CPU {032BDFC0-6AAF-4A75-A9AE-B24C85A4679C}.Release|Any CPU.Build.0 = Release|Any CPU
{0F724A0C-BAE5-4784-A7C5-FCCF7BE07EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F724A0C-BAE5-4784-A7C5-FCCF7BE07EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F724A0C-BAE5-4784-A7C5-FCCF7BE07EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F724A0C-BAE5-4784-A7C5-FCCF7BE07EA5}.Release|Any CPU.Build.0 = Release|Any CPU
{CA4B49FE-3671-4440-9E06-9B5F8D0CDB58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA4B49FE-3671-4440-9E06-9B5F8D0CDB58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA4B49FE-3671-4440-9E06-9B5F8D0CDB58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA4B49FE-3671-4440-9E06-9B5F8D0CDB58}.Release|Any CPU.Build.0 = Release|Any CPU
{EF559D7F-C94F-4F76-833E-44DF7FEDEDBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF559D7F-C94F-4F76-833E-44DF7FEDEDBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF559D7F-C94F-4F76-833E-44DF7FEDEDBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF559D7F-C94F-4F76-833E-44DF7FEDEDBD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -6,4 +6,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BookShopContracts\BookShopContracts.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -6,4 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\BookShopDataModels\BookShopDataModels.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BookShopContracts\BookShopContracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
using BookShopDataBaseImplement.Models;
using Microsoft.EntityFrameworkCore;
namespace BookShopDataBaseImplement
{
public class BookShopDatabase : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (optionsBuilder.IsConfigured == false)
{
optionsBuilder.UseNpgsql(@"Host=192.168.56.101;Port=5432;Database=SUBD_Lab2;Username=postgres;Password=postgres");
}
base.OnConfiguring(optionsBuilder);
}
public virtual DbSet<Author> Authors { set; get; }
public virtual DbSet<Book> Books { set; get; }
public virtual DbSet<BookAuthor> BookAuthors { set; get; }
public virtual DbSet<Order> Orders { set; get; }
public virtual DbSet<Client> Clients { set; get; }
public virtual DbSet<Genre> Genres { set; get; }
}
}

View File

@ -0,0 +1,76 @@
using BookShopContracts.BindingModels;
using BookShopContracts.SearchModels;
using BookShopContracts.StoragesContracts;
using BookShopContracts.ViewModels;
using BookShopDataBaseImplement.Models;
namespace BookShopDataBaseImplement.Implements
{
public class AuthorStorage : IAuthorStorage
{
public AuthorViewModel? Delete(AuthorBindingModel model)
{
using var context = new BookShopDatabase();
var element = context.Authors.FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
{
context.Authors.Remove(element);
context.SaveChanges();
return element.GetViewModel;
}
return null;
}
public AuthorViewModel? GetElement(AuthorSearchModel model)
{
using var context = new BookShopDatabase();
if (model.Id.HasValue)
return context.Authors.FirstOrDefault(x => x.Id == model.Id)?.GetViewModel;
if (!string.IsNullOrEmpty(model.AuthorSurname))
return context.Authors.FirstOrDefault(x => x.AuthorSurname.Equals(model.AuthorSurname))?.GetViewModel;
return null;
}
public List<AuthorViewModel> GetFilteredList(AuthorSearchModel model)
{
if (string.IsNullOrEmpty(model.AuthorSurname))
{
return new();
}
using var context = new BookShopDatabase();
return context.Authors.Where(x => x.AuthorSurname.Contains(model.AuthorSurname)).Select(x => x.GetViewModel).ToList();
}
public List<AuthorViewModel> GetFullList()
{
using var context = new BookShopDatabase();
return context.Authors.Select(x => x.GetViewModel).ToList();
}
public AuthorViewModel? Insert(AuthorBindingModel model)
{
var newAuthor = Author.Create(model);
if (newAuthor == null)
{
return null;
}
using var context = new BookShopDatabase();
context.Authors.Add(newAuthor);
context.SaveChanges();
return newAuthor.GetViewModel;
}
public AuthorViewModel? Update(AuthorBindingModel model)
{
using var context = new BookShopDatabase();
var client = context.Authors.FirstOrDefault(x => x.Id == model.Id);
if (client == null)
{
return null;
}
client.Update(model);
context.SaveChanges();
return client.GetViewModel;
}
}
}

View File

@ -0,0 +1,137 @@
using BookShopContracts.BindingModels;
using BookShopContracts.SearchModels;
using BookShopContracts.StoragesContracts;
using BookShopContracts.ViewModels;
using BookShopDataBaseImplement.Models;
using Microsoft.EntityFrameworkCore;
using System.Diagnostics;
namespace BookShopDataBaseImplement.Implements
{
public class BookStorage : IBookStorage
{
public List<BookViewModel> GetFullList()
{
using var context = new BookShopDatabase();
return context.Books
.Include(x => x.Genre)
.Include(x => x.Authors)
.ThenInclude(x => x.Author)
.ToList()
.Select(x => x.GetViewModel)
.ToList();
}
public List<BookViewModel> GetFilteredList(BookSearchModel model)
{
if (string.IsNullOrEmpty(model.BookName))
{
return new();
}
using var context = new BookShopDatabase();
return context.Books
.Include(x => x.Authors)
.ThenInclude(x => x.Author)
.Where(x => x.BookName.Contains(model.BookName))
.ToList()
.Select(x => x.GetViewModel)
.ToList();
}
public BookViewModel? GetElement(BookSearchModel model)
{
if (string.IsNullOrEmpty(model.BookName) &&
!model.Id.HasValue)
{
return null;
}
using var context = new BookShopDatabase();
return context.Books
.Include(x => x.Authors)
.ThenInclude(x => x.Author)
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.BookName) &&
x.BookName == model.BookName) ||
(model.Id.HasValue && x.Id ==
model.Id))
?.GetViewModel;
}
public BookViewModel? Insert(BookBindingModel model)
{
using var context = new BookShopDatabase();
var newBook = Book.Create(context, model);
if (newBook == null)
{
return null;
}
context.Books.Add(newBook);
context.SaveChanges();
return newBook.GetViewModel;
}
public BookViewModel? Update(BookBindingModel model)
{
using var context = new BookShopDatabase();
using var transaction = context.Database.BeginTransaction();
try
{
var book = context.Books.FirstOrDefault(rec =>
rec.Id == model.Id);
if (book == null)
{
return null;
}
book.Update(model);
context.SaveChanges();
book.UpdateAuthors(context, model);
transaction.Commit();
return book.GetViewModel;
}
catch
{
transaction.Rollback();
throw;
}
}
public BookViewModel? Delete(BookBindingModel model)
{
using var context = new BookShopDatabase();
var element = context.Books
.Include(x => x.GenreId)
.Include(x => x.Authors)
.FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
{
context.Books.Remove(element);
context.SaveChanges();
return element.GetViewModel;
}
return null;
}
public string TestInsertList(int num)
{
throw new NotImplementedException();
}
public string TestReadList(int num)
{
var context = new BookShopDatabase();
Stopwatch stopwatch = new();
long[] res = new long[num];
for (int i = 0; i < num; i++)
{
stopwatch.Start();
List<BookViewModel> list = context.Books.Include(x => x.Genre).Include(x => x.Authors).ThenInclude(x => x.Author).ToList().Select(x => x.GetViewModel).ToList();
stopwatch.Stop();
res[i] = stopwatch.ElapsedMilliseconds;
}
long sum = 0;
for (int i = 0; i < num; i++)
{
sum += res[i];
}
int result = Convert.ToInt32(sum / num);
return result.ToString();
}
}
}

View File

@ -0,0 +1,76 @@
using BookShopContracts.BindingModels;
using BookShopContracts.SearchModels;
using BookShopContracts.StoragesContracts;
using BookShopContracts.ViewModels;
using BookShopDataBaseImplement.Models;
namespace BookShopDataBaseImplement.Implements
{
public class ClientStorage : IClientStorage
{
public ClientViewModel? Delete(ClientBindingModel model)
{
using var context = new BookShopDatabase();
var element = context.Clients.FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
{
context.Clients.Remove(element);
context.SaveChanges();
return element.GetViewModel;
}
return null;
}
public ClientViewModel? GetElement(ClientSearchModel model)
{
using var context = new BookShopDatabase();
if (model.Id.HasValue)
return context.Clients.FirstOrDefault(x => x.Id == model.Id)?.GetViewModel;
if (!string.IsNullOrEmpty(model.Email))
return context.Clients.FirstOrDefault(x => x.Email.Equals(model.Email))?.GetViewModel;
return null;
}
public List<ClientViewModel> GetFilteredList(ClientSearchModel model)
{
if (string.IsNullOrEmpty(model.ClientSurname))
{
return new();
}
using var context = new BookShopDatabase();
return context.Clients.Where(x => x.ClientSurname.Contains(model.ClientSurname)).Select(x => x.GetViewModel).ToList();
}
public List<ClientViewModel> GetFullList()
{
using var context = new BookShopDatabase();
return context.Clients.Select(x => x.GetViewModel).ToList();
}
public ClientViewModel? Insert(ClientBindingModel model)
{
var newClient = Client.Create(model);
if (newClient == null)
{
return null;
}
using var context = new BookShopDatabase();
context.Clients.Add(newClient);
context.SaveChanges();
return newClient.GetViewModel;
}
public ClientViewModel? Update(ClientBindingModel model)
{
using var context = new BookShopDatabase();
var client = context.Clients.FirstOrDefault(x => x.Id == model.Id);
if (client == null)
{
return null;
}
client.Update(model);
context.SaveChanges();
return client.GetViewModel;
}
}
}

View File

@ -0,0 +1,86 @@
using BookShopContracts.BindingModels;
using BookShopContracts.SearchModels;
using BookShopContracts.StoragesContracts;
using BookShopContracts.ViewModels;
using BookShopDataBaseImplement.Models;
using BookShopDataModels.Models;
using System.ComponentModel;
namespace BookShopDataBaseImplement.Implements
{
public class GenreStorage : IGenreStorage
{
public List<GenreViewModel> GetFullList()
{
using var context = new BookShopDatabase();
return context.Genres
.Select(x => x.GetViewModel)
.ToList();
}
public List<GenreViewModel> GetFilteredList(GenreSearchModel
model)
{
if (string.IsNullOrEmpty(model.GenreName))
{
return new();
}
using var context = new BookShopDatabase();
return context.Genres
.Where(x => x.GenreName.Contains(model.GenreName))
.Select(x => x.GetViewModel)
.ToList();
}
public GenreViewModel? GetElement(GenreSearchModel model)
{
if (string.IsNullOrEmpty(model.GenreName) && !model.Id.HasValue)
{
return null;
}
using var context = new BookShopDatabase();
return context.Genres
.FirstOrDefault(x =>
(!string.IsNullOrEmpty(model.GenreName) && x.GenreName ==
model.GenreName) ||
(model.Id.HasValue && x.Id == model.Id))
?.GetViewModel;
}
public GenreViewModel? Insert(GenreBindingModel model)
{
var newGenre = Genre.Create(model);
if (newGenre == null)
{
return null;
}
using var context = new BookShopDatabase();
context.Genres.Add(newGenre);
context.SaveChanges();
return newGenre.GetViewModel;
}
public GenreViewModel? Update(GenreBindingModel model)
{
using var context = new BookShopDatabase();
var genre = context.Genres.FirstOrDefault(x => x.Id ==
model.Id);
if (genre == null)
{
return null;
}
genre.Update(model);
context.SaveChanges();
return genre.GetViewModel;
}
public GenreViewModel? Delete(GenreBindingModel model)
{
using var context = new BookShopDatabase();
var element = context.Genres.FirstOrDefault(rec => rec.Id ==
model.Id);
if (element != null)
{
context.Genres.Remove(element);
context.SaveChanges();
return element.GetViewModel;
}
return null;
}
}
}

View File

@ -0,0 +1,103 @@
using BookShopContracts.BindingModels;
using BookShopContracts.SearchModels;
using BookShopContracts.StoragesContracts;
using BookShopContracts.ViewModels;
using BookShopDataBaseImplement.Models;
using Microsoft.EntityFrameworkCore;
namespace BookShopDataBaseImplement.Implements
{
public class OrderStorage : IOrderStorage
{
public OrderViewModel? Delete(OrderBindingModel model)
{
using var context = new BookShopDatabase();
var element = context.Orders.Include(x => x.Book)
.Include(x => x.Client).FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
{
context.Orders.Remove(element);
context.SaveChanges();
return element.GetViewModel;
}
return null;
}
public OrderViewModel? GetElement(OrderSearchModel model)
{
if (!model.Id.HasValue)
{
return null;
}
using var context = new BookShopDatabase();
return context.Orders
.FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)
?.GetViewModel;
}
public List<OrderViewModel> GetFilteredList(OrderSearchModel model)
{
if (model is null)
{
return new();
}
using var context = new BookShopDatabase();
return context.Orders
.Where(x => x.ClientId == model.ClientId)
.ToList()
.Select(x => x.GetViewModel)
.ToList();
}
public List<OrderViewModel> GetFullList()
{
using var context = new BookShopDatabase();
return context.Orders.Include(x => x.Book)
.Include(x => x.Client).Select(x => x.GetViewModel).ToList();
}
public OrderViewModel? Insert(OrderBindingModel model)
{
var newOrder = Order.Create(model);
if (newOrder == null)
{
return null;
}
using var context = new BookShopDatabase();
context.Orders.Add(newOrder);
context.SaveChanges();
return context.Orders
.Include(x => x.Book)
.Include(x => x.Client)
.FirstOrDefault(x => x.Id == newOrder.Id)
?.GetViewModel;
}
public OrderViewModel? Update(OrderBindingModel model)
{
using var context = new BookShopDatabase();
var order = context.Orders.Include(x => x.Book)
.Include(x => x.Client).FirstOrDefault(x => x.Id == model.Id);
if (order == null)
{
return null;
}
order.Update(model);
context.SaveChanges();
return order.GetViewModel;
}
}
}

View File

@ -0,0 +1,255 @@
// <auto-generated />
using System;
using BookShopDataBaseImplement;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BookShopDataBaseImplement.Migrations
{
[DbContext(typeof(BookShopDatabase))]
[Migration("20230512083316_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BookShopDataBaseImplement.Models.Author", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("AuthorName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("AuthorPatronymic")
.IsRequired()
.HasColumnType("text");
b.Property<string>("AuthorSurname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Authors");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Book", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("BookName")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Cost")
.HasColumnType("double precision");
b.Property<int>("Count")
.HasColumnType("integer");
b.Property<int>("GenreId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GenreId");
b.ToTable("Books");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.BookAuthor", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("AuthorId")
.HasColumnType("integer");
b.Property<int>("BookId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("BookId");
b.ToTable("BookAuthors");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClientName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ClientPatronymic")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ClientSurname")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Clients");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Genre", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("GenreName")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Genres");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("BookId")
.HasColumnType("integer");
b.Property<int>("ClientId")
.HasColumnType("integer");
b.Property<int>("Count")
.HasColumnType("integer");
b.Property<DateTime>("DateCreate")
.HasColumnType("timestamp with time zone");
b.Property<double>("Sum")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("BookId");
b.HasIndex("ClientId");
b.ToTable("Orders");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Book", b =>
{
b.HasOne("BookShopDataBaseImplement.Models.Genre", "Genre")
.WithMany("Books")
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Genre");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.BookAuthor", b =>
{
b.HasOne("BookShopDataBaseImplement.Models.Author", "Author")
.WithMany("BookAuthors")
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BookShopDataBaseImplement.Models.Book", "Book")
.WithMany("Authors")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
b.Navigation("Book");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Order", b =>
{
b.HasOne("BookShopDataBaseImplement.Models.Book", "Book")
.WithMany("Orders")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BookShopDataBaseImplement.Models.Client", "Client")
.WithMany("Orders")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("Client");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Author", b =>
{
b.Navigation("BookAuthors");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Book", b =>
{
b.Navigation("Authors");
b.Navigation("Orders");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Client", b =>
{
b.Navigation("Orders");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Genre", b =>
{
b.Navigation("Books");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,184 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BookShopDataBaseImplement.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Authors",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
AuthorSurname = table.Column<string>(type: "text", nullable: false),
AuthorName = table.Column<string>(type: "text", nullable: false),
AuthorPatronymic = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Authors", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Clients",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ClientSurname = table.Column<string>(type: "text", nullable: false),
ClientName = table.Column<string>(type: "text", nullable: false),
ClientPatronymic = table.Column<string>(type: "text", nullable: false),
Email = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clients", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Genres",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
GenreName = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Genres", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Books",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
BookName = table.Column<string>(type: "text", nullable: false),
Count = table.Column<int>(type: "integer", nullable: false),
Cost = table.Column<double>(type: "double precision", nullable: false),
GenreId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Books", x => x.Id);
table.ForeignKey(
name: "FK_Books_Genres_GenreId",
column: x => x.GenreId,
principalTable: "Genres",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "BookAuthors",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
BookId = table.Column<int>(type: "integer", nullable: false),
AuthorId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_BookAuthors", x => x.Id);
table.ForeignKey(
name: "FK_BookAuthors_Authors_AuthorId",
column: x => x.AuthorId,
principalTable: "Authors",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_BookAuthors_Books_BookId",
column: x => x.BookId,
principalTable: "Books",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
BookId = table.Column<int>(type: "integer", nullable: false),
ClientId = table.Column<int>(type: "integer", nullable: false),
Count = table.Column<int>(type: "integer", nullable: false),
Sum = table.Column<double>(type: "double precision", nullable: false),
DateCreate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
table.ForeignKey(
name: "FK_Orders_Books_BookId",
column: x => x.BookId,
principalTable: "Books",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Orders_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_BookAuthors_AuthorId",
table: "BookAuthors",
column: "AuthorId");
migrationBuilder.CreateIndex(
name: "IX_BookAuthors_BookId",
table: "BookAuthors",
column: "BookId");
migrationBuilder.CreateIndex(
name: "IX_Books_GenreId",
table: "Books",
column: "GenreId");
migrationBuilder.CreateIndex(
name: "IX_Orders_BookId",
table: "Orders",
column: "BookId");
migrationBuilder.CreateIndex(
name: "IX_Orders_ClientId",
table: "Orders",
column: "ClientId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "BookAuthors");
migrationBuilder.DropTable(
name: "Orders");
migrationBuilder.DropTable(
name: "Authors");
migrationBuilder.DropTable(
name: "Books");
migrationBuilder.DropTable(
name: "Clients");
migrationBuilder.DropTable(
name: "Genres");
}
}
}

View File

@ -0,0 +1,252 @@
// <auto-generated />
using System;
using BookShopDataBaseImplement;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BookShopDataBaseImplement.Migrations
{
[DbContext(typeof(BookShopDatabase))]
partial class BookShopDatabaseModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BookShopDataBaseImplement.Models.Author", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("AuthorName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("AuthorPatronymic")
.IsRequired()
.HasColumnType("text");
b.Property<string>("AuthorSurname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Authors");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Book", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("BookName")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Cost")
.HasColumnType("double precision");
b.Property<int>("Count")
.HasColumnType("integer");
b.Property<int>("GenreId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GenreId");
b.ToTable("Books");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.BookAuthor", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("AuthorId")
.HasColumnType("integer");
b.Property<int>("BookId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("BookId");
b.ToTable("BookAuthors");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClientName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ClientPatronymic")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ClientSurname")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Clients");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Genre", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("GenreName")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Genres");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("BookId")
.HasColumnType("integer");
b.Property<int>("ClientId")
.HasColumnType("integer");
b.Property<int>("Count")
.HasColumnType("integer");
b.Property<DateTime>("DateCreate")
.HasColumnType("timestamp with time zone");
b.Property<double>("Sum")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("BookId");
b.HasIndex("ClientId");
b.ToTable("Orders");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Book", b =>
{
b.HasOne("BookShopDataBaseImplement.Models.Genre", "Genre")
.WithMany("Books")
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Genre");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.BookAuthor", b =>
{
b.HasOne("BookShopDataBaseImplement.Models.Author", "Author")
.WithMany("BookAuthors")
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BookShopDataBaseImplement.Models.Book", "Book")
.WithMany("Authors")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
b.Navigation("Book");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Order", b =>
{
b.HasOne("BookShopDataBaseImplement.Models.Book", "Book")
.WithMany("Orders")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BookShopDataBaseImplement.Models.Client", "Client")
.WithMany("Orders")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("Client");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Author", b =>
{
b.Navigation("BookAuthors");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Book", b =>
{
b.Navigation("Authors");
b.Navigation("Orders");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Client", b =>
{
b.Navigation("Orders");
});
modelBuilder.Entity("BookShopDataBaseImplement.Models.Genre", b =>
{
b.Navigation("Books");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,63 @@
using BookShopContracts.BindingModels;
using BookShopContracts.ViewModels;
using BookShopDataModels.Models;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace BookShopDataBaseImplement.Models
{
public class Author : IAuthorModel
{
public int Id { get; private set; }
[Required]
public string AuthorSurname { get; set; } = string.Empty;
[Required]
public string AuthorName { get; set; } = string.Empty;
[Required]
public string AuthorPatronymic { get; set; } = string.Empty;
[ForeignKey("AuthorId")]
public virtual List<BookAuthor> BookAuthors { get; set; } = new();
public static Author? Create(AuthorBindingModel model)
{
if (model == null)
{
return null;
}
return new Author()
{
Id = model.Id,
AuthorSurname = model.AuthorSurname,
AuthorName = model.AuthorName,
AuthorPatronymic = model.AuthorPatronymic
};
}
public static Author Create(AuthorViewModel model)
{
return new Author
{
Id = model.Id,
AuthorSurname = model.AuthorSurname,
AuthorName = model.AuthorName,
AuthorPatronymic = model.AuthorPatronymic
};
}
public void Update(AuthorBindingModel model)
{
if (model == null)
{
return;
}
AuthorSurname = model.AuthorSurname;
AuthorName = model.AuthorName;
AuthorPatronymic = model.AuthorPatronymic;
}
public AuthorViewModel GetViewModel => new()
{
Id = Id,
AuthorSurname = AuthorSurname,
AuthorName = AuthorName,
AuthorPatronymic = AuthorPatronymic
};
}
}

View File

@ -0,0 +1,108 @@
using BookShopContracts.BindingModels;
using BookShopContracts.ViewModels;
using BookShopDataModels.Models;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
namespace BookShopDataBaseImplement.Models
{
public class Book : IBookModel
{
public int Id { get; private set; }
[Required]
public string BookName { get; set; } = string.Empty;
[Required]
public int Count { get; set; }
[Required]
public double Cost { get; set; }
[Required]
public int GenreId { get; set; }
public virtual Genre Genre { get; set; }
private Dictionary<int, IAuthorModel>? _bookAuthors = null;
[NotMapped]
public Dictionary<int, IAuthorModel> BookAuthors
{
get
{
if (_bookAuthors == null)
{
_bookAuthors = Authors.ToDictionary(recPC => recPC.AuthorId, recPC => recPC.Author as IAuthorModel);
}
return _bookAuthors;
}
}
[ForeignKey("BookId")]
public virtual List<BookAuthor> Authors { get; set; } = new();
[ForeignKey("BookId")]
public virtual List<Order> Orders { get; set; } = new();
public static Book Create(BookShopDatabase context, BookBindingModel model)
{
return new Book()
{
Id = model.Id,
BookName = model.BookName,
Cost = model.Cost,
Count = model.Count,
GenreId = model.GenreId,
Authors = model.BookAuthors.Select(x => new BookAuthor
{
Author = context.Authors.First(y => y.Id == x.Key)
}).ToList()
};
}
public void Update(BookBindingModel model)
{
BookName = model.BookName;
Cost = model.Cost;
Cost = model.Cost;
GenreId = model.GenreId;
}
public BookViewModel GetViewModel
{
get
{
using var context = new BookShopDatabase();
return new BookViewModel
{
Id = Id,
BookName = BookName,
Cost = Cost,
GenreId= GenreId,
GenreName = context.Genres.FirstOrDefault(x => x.Id == GenreId)?.GenreName ?? string.Empty,
BookAuthors = BookAuthors
};
}
}
public void UpdateAuthors(BookShopDatabase context, BookBindingModel model)
{
var bookAuthors = context.BookAuthors.Where(rec =>
rec.BookId == model.Id).ToList();
if (bookAuthors != null && bookAuthors.Count > 0)
{ // удалили те, которых нет в модели
context.BookAuthors.RemoveRange(bookAuthors.Where(rec
=> !model.BookAuthors.ContainsKey(rec.AuthorId)));
context.SaveChanges();
// обновили количество у существующих записей
foreach (var updateAuthor in bookAuthors)
{
model.BookAuthors.Remove(updateAuthor.AuthorId);
}
context.SaveChanges();
}
var book = context.Books.First(x => x.Id == Id);
foreach (var pc in model.BookAuthors)
{
context.BookAuthors.Add(new BookAuthor
{
Book = book,
Author = context.Authors.First(x => x.Id == pc.Key)
});
context.SaveChanges();
}
_bookAuthors = null;
}
}
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace BookShopDataBaseImplement.Models
{
public class BookAuthor
{
public int Id { get; set; }
[Required]
public int BookId { get; set; }
[Required]
public int AuthorId { get; set; }
public virtual Book Book { get; set; } = new();
public virtual Author Author { get; set; } = new();
}
}

View File

@ -0,0 +1,69 @@
using BookShopContracts.BindingModels;
using BookShopContracts.ViewModels;
using BookShopDataModels.Models;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace BookShopDataBaseImplement.Models
{
public class Client : IClientModel
{
public int Id { get; private set; }
[Required]
public string ClientSurname { get; set; } = string.Empty;
[Required]
public string ClientName { get; set; } = string.Empty;
[Required]
public string ClientPatronymic { get; set; } = string.Empty;
[Required]
public string Email { get; set; } = string.Empty;
[ForeignKey("ClientId")]
public virtual List<Order> Orders { get; set; } = new();
public static Client? Create(ClientBindingModel model)
{
if (model == null)
{
return null;
}
return new Client()
{
Id = model.Id,
ClientSurname = model.ClientSurname,
ClientName = model.ClientName,
ClientPatronymic = model.ClientPatronymic,
Email = model.Email
};
}
public static Client Create(ClientViewModel model)
{
return new Client
{
Id = model.Id,
ClientSurname = model.ClientSurname,
ClientName = model.ClientName,
ClientPatronymic = model.ClientPatronymic,
Email = model.Email
};
}
public void Update(ClientBindingModel model)
{
if (model == null)
{
return;
}
ClientSurname = model.ClientSurname;
ClientName = model.ClientName;
ClientPatronymic = model.ClientPatronymic;
Email = model.Email;
}
public ClientViewModel GetViewModel => new()
{
Id = Id,
ClientSurname = ClientSurname,
ClientName = ClientName,
ClientPatronymic = ClientPatronymic,
Email = Email
};
}
}

View File

@ -0,0 +1,51 @@
using BookShopContracts.BindingModels;
using BookShopContracts.ViewModels;
using BookShopDataModels.Models;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace BookShopDataBaseImplement.Models
{
public class Genre : IGenreModel
{
public int Id { get; private set; }
[Required]
public string GenreName { get; set; } = string.Empty;
[ForeignKey("GenreId")]
public virtual List<Book> Books { get; set; } = new();
public static Genre? Create(GenreBindingModel model)
{
if (model == null)
{
return null;
}
return new Genre()
{
Id = model.Id,
GenreName = model.GenreName
};
}
public static Genre Create(GenreViewModel model)
{
return new Genre
{
Id = model.Id,
GenreName = model.GenreName
};
}
public void Update(GenreBindingModel model)
{
if (model == null)
{
return;
}
GenreName = model.GenreName;
}
public GenreViewModel GetViewModel => new()
{
Id = Id,
GenreName = GenreName
};
}
}

View File

@ -0,0 +1,79 @@
using BookShopContracts.BindingModels;
using BookShopContracts.ViewModels;
using BookShopDataModels.Models;
using System.ComponentModel.DataAnnotations;
namespace BookShopDataBaseImplement.Models
{
public class Order : IOrderModel
{
public int Id { get; private set; }
public int BookId { get; private set; }
[Required]
public int ClientId { get; set; }
[Required]
public int Count { get; private set; }
[Required]
public double Sum { get; private set; }
[Required]
public DateTime DateCreate { get; private set; } = DateTime.Now;
public virtual Book Book { get; set; }
public Client Client { get; set; }
public static Order? Create(OrderBindingModel? model)
{
if (model == null)
{
return null;
}
return new Order()
{
Id = model.Id,
ClientId = model.ClientId,
BookId = model.BookId,
Count = model.Count,
Sum = model.Sum,
DateCreate = model.DateCreate
};
}
public void Update(OrderBindingModel? model)
{
if (model == null)
{
return;
}
Id = model.Id;
ClientId = model.ClientId;
BookId = model.BookId;
Count = model.Count;
Sum = model.Sum;
DateCreate = model.DateCreate;
}
public OrderViewModel GetViewModel
{
get
{
using var context = new BookShopDatabase();
return new OrderViewModel
{
Id = Id,
ClientId = ClientId,
ClientSurname = context.Clients.FirstOrDefault(x => x.Id == ClientId)?.ClientSurname ?? string.Empty,
BookName = context.Books.FirstOrDefault(x => x.Id == BookId)?.BookName ?? string.Empty,
BookId = BookId,
Count = Count,
Sum = Sum,
DateCreate = DateCreate
};
}
}
}
}

View File

@ -8,4 +8,18 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BookShopBusinessLogic\BookShopBusinessLogic.csproj" />
<ProjectReference Include="..\BookShopContracts\BookShopContracts.csproj" />
<ProjectReference Include="..\BookShopDataBaseImplement\BookShopDataBaseImplement.csproj" />
<ProjectReference Include="..\BookShopDataModels\BookShopDataModels.csproj" />
</ItemGroup>
</Project> </Project>