From 011e1685149104591f70c658514ce07576ebbb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0t=C3=A1fl?= <24357229+qjustfeelitp@users.noreply.github.com> Date: Sat, 1 Oct 2022 15:43:02 +0200 Subject: [PATCH 1/3] benchmarks --- AuthorDTO.cs | 16 +- BenchmarkService.cs | 618 +++++++++++++++++++-- BookDto.cs | 8 + CompiledModels/AppDbContextModel.cs | 29 + CompiledModels/AppDbContextModelBuilder.cs | 40 ++ CompiledModels/AuthorEntityType.cs | 110 ++++ CompiledModels/BookEntityType.cs | 139 +++++ CompiledModels/PublisherEntityType.cs | 66 +++ CompiledModels/RoleEntityType.cs | 59 ++ CompiledModels/UserEntityType.cs | 104 ++++ CompiledModels/UserRoleEntityType.cs | 123 ++++ Context/AppDbContext.cs | 2 +- OptimizeMePlease.csproj | 28 +- Program.cs | 19 +- 14 files changed, 1284 insertions(+), 77 deletions(-) create mode 100644 CompiledModels/AppDbContextModel.cs create mode 100644 CompiledModels/AppDbContextModelBuilder.cs create mode 100644 CompiledModels/AuthorEntityType.cs create mode 100644 CompiledModels/BookEntityType.cs create mode 100644 CompiledModels/PublisherEntityType.cs create mode 100644 CompiledModels/RoleEntityType.cs create mode 100644 CompiledModels/UserEntityType.cs create mode 100644 CompiledModels/UserRoleEntityType.cs diff --git a/AuthorDTO.cs b/AuthorDTO.cs index dc6416c..6fa0d8c 100644 --- a/AuthorDTO.cs +++ b/AuthorDTO.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static OptimizeMePlease.BenchmarkService; namespace OptimizeMePlease { @@ -13,7 +14,6 @@ public class AuthorDTO public string UserEmail { get; set; } public string UserName { get; set; } public int UserId { get; set; } - public int AuthorId { get; set; } public int Id { get; set; } public int RoleId { get; set; } public int BooksCount { get; set; } @@ -22,4 +22,18 @@ public class AuthorDTO public string AuthorCountry { get; set; } public string AuthorNickName { get; set; } } + + public sealed class AuthorDtoOptimized + { + public string UserFirstName { get; set; } + public string UserLastName { get; set; } + public string UserEmail { get; set; } + public string UserName { get; set; } + public int BooksCount { get; set; } + public ICollection Books { get; set; } + public int Age { get; set; } + public string Country { get; set; } + } + + public sealed record AuthorRecordDto(int Age, string Country, int BooksCount, string UserName, string FirstName, string LastName, string Email, ICollection Books); } diff --git a/BenchmarkService.cs b/BenchmarkService.cs index d0a958d..46b7b70 100644 --- a/BenchmarkService.cs +++ b/BenchmarkService.cs @@ -1,71 +1,80 @@ -using BenchmarkDotNet.Attributes; -using Microsoft.EntityFrameworkCore; -using OptimizeMePlease.Context; +using System; +using System.Buffers; using System.Collections.Generic; +using System.Data; +using System.Data.Common; using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using CompiledModels; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using OptimizeMePlease.Context; +using OptimizeMePlease.Entities; namespace OptimizeMePlease { - [MemoryDiagnoser] + [MemoryDiagnoser(false)] + //[InProcess] public class BenchmarkService { - public BenchmarkService() - { - } - /// /// Get top 2 Authors (FirstName, LastName, UserName, Email, Age, Country) /// from country Serbia aged 27, with the highest BooksCount /// and all his/her books (Book Name/Title and Publishment Year) published before 1900 /// /// - [Benchmark] + [Benchmark(Baseline = true)] public List GetAuthors() { using var dbContext = new AppDbContext(); var authors = dbContext.Authors - .Include(x => x.User) - .ThenInclude(x => x.UserRoles) - .ThenInclude(x => x.Role) - .Include(x => x.Books) - .ThenInclude(x => x.Publisher) - .ToList() - .Select(x => new AuthorDTO - { - UserCreated = x.User.Created, - UserEmailConfirmed = x.User.EmailConfirmed, - UserFirstName = x.User.FirstName, - UserLastActivity = x.User.LastActivity, - UserLastName = x.User.LastName, - UserEmail = x.User.Email, - UserName = x.User.UserName, - UserId = x.User.Id, - RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId, - BooksCount = x.BooksCount, - AllBooks = x.Books.Select(y => new BookDto - { - Id = y.Id, - Name = y.Name, - Published = y.Published, - ISBN = y.ISBN, - PublisherName = y.Publisher.Name - }).ToList(), - AuthorAge = x.Age, - AuthorCountry = x.Country, - AuthorNickName = x.NickName, - Id = x.Id - }) - .ToList() - .Where(x => x.AuthorCountry == "Serbia" && x.AuthorAge == 27) - .ToList(); + .Include(x => x.User) + .ThenInclude(x => x.UserRoles) + .ThenInclude(x => x.Role) + .Include(x => x.Books) + .ThenInclude(x => x.Publisher) + .ToList() + .Select(x => new AuthorDTO + { + UserCreated = x.User.Created, + UserEmailConfirmed = x.User.EmailConfirmed, + UserFirstName = x.User.FirstName, + UserLastActivity = x.User.LastActivity, + UserLastName = x.User.LastName, + UserEmail = x.User.Email, + UserName = x.User.UserName, + UserId = x.User.Id, + RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId, + BooksCount = x.BooksCount, + AllBooks = x.Books.Select(y => new BookDto + { + Id = y.Id, + Name = y.Name, + Published = y.Published, + ISBN = y.ISBN, + PublisherName = y.Publisher.Name + }) + .ToList(), + AuthorAge = x.Age, + AuthorCountry = x.Country, + AuthorNickName = x.NickName, + Id = x.Id + }) + .ToList() + .Where(x => (x.AuthorCountry == "Serbia") && (x.AuthorAge == 27)) + .ToList(); var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList(); - List finalAuthors = new List(); + var finalAuthors = new List(); + foreach (var author in orderedAuthors) { - List books = new List(); + var books = new List(); var allBooks = author.AllBooks; @@ -86,11 +95,526 @@ public List GetAuthors() } [Benchmark] - public List GetAuthors_Optimized() + public IList GetAuthors_Optimized() + { + using var db = ContextFactory.CreateDbContext(); + + return Get(db).ToList(); + } + + [Benchmark] + public IList GetAuthors_OptimizedInline() + { + using var db = ContextFactory.CreateDbContext(); + + return db.Set() + .Where(author => (author.Country == Serbia) && (author.Age == Age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => new AuthorDtoOptimized + { + UserFirstName = author.User.FirstName, + UserLastName = author.User.LastName, + UserEmail = author.User.Email, + UserName = author.User.UserName, + BooksCount = author.BooksCount, + Books = author.Books.AsQueryable() + .Where(book => book.Published < EF.Functions.DateFromParts(Year, 1, 1)) + .Select(y => new BookDtoOptimized + { + Name = y.Name, + PublishedYear = y.Published.Year + }) + .ToList(), + Age = author.Age, + Country = author.Country + }) + .ToList(); + } + + [Benchmark] + public IList GetAuthors_OptimizedInlineWithParameters() { - List authors = new List(); + using var db = ContextFactory.CreateDbContext(); + + string country = Serbia; + int age = Age; + int year = Year; + + return db.Set() + .Where(author => (author.Country == country) && (author.Age == age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => new AuthorDtoOptimized + { + UserFirstName = author.User.FirstName, + UserLastName = author.User.LastName, + UserEmail = author.User.Email, + UserName = author.User.UserName, + BooksCount = author.BooksCount, + Books = author.Books.AsQueryable() + .Where(book => book.Published < EF.Functions.DateFromParts(year, 1, 1)) + .Select(y => new BookDtoOptimized + { + Name = y.Name, + PublishedYear = y.Published.Year + }) + .ToList(), + Age = author.Age, + Country = author.Country + }) + .ToList(); + } + + [Benchmark] + public IList GetAuthors_OptimizedWithoutExpressions() + { + using var db = ContextFactory.CreateDbContext(); + + return GetWithoutExpressions(db).ToList(); + } + + [Benchmark] + public async Task GetAuthors_OptimizedAsync() + { + await using var db = await ContextFactory.CreateDbContextAsync(); + + var authors = new AuthorDtoOptimized[2]; + int counter = 0; + + await foreach (var author in GetAsync(db)) + { + authors[counter] = author; + counter++; + } return authors; } + + [Benchmark] + public async IAsyncEnumerable GetAuthors_OptimizedAsyncPassThrough() + { + await using var db = await ContextFactory.CreateDbContextAsync(); + + await foreach (var author in GetAsync(db)) + { + yield return author; + } + } + + [Benchmark] + public IList GetAuthors_OptimizedParameterized() + { + using var db = ContextFactory.CreateDbContext(); + + return GetParameterized(db, Serbia, Age).ToList(); + } + + [Benchmark] + public Span GetAuthors_OptimizedRecordSpan() + { + using var db = ContextFactory.CreateDbContext(); + + var array = ArrayPool.Rent(2); + int counter = 0; + + foreach (var dto in GetRecord(db)) + { + array[counter] = dto; + counter++; + } + + var result = array.AsSpan(); + ArrayPool.Return(array); + + return result; + } + + [Benchmark] + public IList GetAuthors_OptimizedRecordList() + { + using var db = ContextFactory.CreateDbContext(); + + return GetRecord(db).ToList(); + } + + [Benchmark] + public IList GetAuthors_OptimizedRecordListInline() + { + using var db = ContextFactory.CreateDbContext(); + + return db.Set() + .Where(author => (author.Country == Serbia) && (author.Age == Age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => + new AuthorRecordDto(author.Age, + author.Country, + author.BooksCount, + author.User.UserName, + author.User.FirstName, + author.User.LastName, + author.User.Email, + author.Books.AsQueryable() + .Where(book => book.Published < EF.Functions.DateFromParts(Year, 1, 1)) + .Select(y => new BookRecordDto(y.Name, y.Published.Year)) + .ToList())) + .ToList(); + } + + [Benchmark] + public IList GetAuthors_OptimizedRecordListInlineWithParameters() + { + using var db = ContextFactory.CreateDbContext(); + + string country = Serbia; + int age = Age; + int year = Year; + + return db.Set() + .Where(author => (author.Country == country) && (author.Age == age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => + new AuthorRecordDto(author.Age, + author.Country, + author.BooksCount, + author.User.UserName, + author.User.FirstName, + author.User.LastName, + author.User.Email, + author.Books.AsQueryable() + .Where(book => book.Published < EF.Functions.DateFromParts(year, 1, 1)) + .Select(y => new BookRecordDto(y.Name, y.Published.Year)) + .ToList())) + .ToList(); + } + + [Benchmark] + public IList GetAuthors_OptimizedRecordListWithoutExpressions() + { + using var db = ContextFactory.CreateDbContext(); + + return GetRecordWithoutExpressions(db).ToList(); + } + + [Benchmark] + public async Task GetAuthors_OptimizedRecordAsync() + { + await using var db = await ContextFactory.CreateDbContextAsync(); + + var authors = new AuthorRecordDto[2]; + int counter = 0; + + await foreach (var author in GetRecordAsync(db)) + { + authors[counter] = author; + counter++; + } + + return authors; + } + + [Benchmark] + public async IAsyncEnumerable GetAuthors_OptimizedRecordAsyncPassThrough() + { + await using var db = await ContextFactory.CreateDbContextAsync(); + + await foreach (var author in GetRecordAsync(db)) + { + yield return author; + } + } + + [Benchmark] + public IList GetAuthors_OptimizedRecordListParameterized() + { + using var db = ContextFactory.CreateDbContext(); + + return GetRecordParameterized(db, Serbia, Age).ToList(); + } + + [Benchmark] + public AuthorRecordDto[] GetAuthors_RawSqlArray() + { + using var db = ContextFactory.CreateDbContext(); + using var command = db.Database.GetDbConnection().CreateCommand(); + + command.CommandText = CommandText; + + var countryParameter = new SqlParameter("@country", Serbia) + { + SqlDbType = SqlDbType.NVarChar + }; + + command.Parameters.Add(countryParameter); + command.Parameters.Add(new SqlParameter("@take", 2)); + command.Parameters.Add(new SqlParameter("@age", Age)); + command.Parameters.Add(new SqlParameter("@year", 1900)); + + return ProcessRawSqlResult(db, command); + } + + [Benchmark] + public AuthorRecordDto[] GetAuthors_RawSqlWithoutParametersArray() + { + using var db = ContextFactory.CreateDbContext(); + using var command = db.Database.GetDbConnection().CreateCommand(); + + command.CommandText = CommandTextWithoutParameters; + + return ProcessRawSqlResult(db, command); + } + + private static AuthorRecordDto[] ProcessRawSqlResult(DbContext db, DbCommand command) + { + db.Database.OpenConnection(); + using var reader = command.ExecuteReader(); + var authors = new List(); + + while (reader.Read()) + { + //SELECT [u].[FirstName], [u].[LastName], [u].[Email], [u].[UserName], [t].[BooksCount], [t].[Id], [u].[Id], [t0].[Name], [t0].[PublishedYear], [t0].[Id], [t].[Age], [t].[Country] + //(int Age, string Country, int BooksCount, string UserName, string FirstName, string LastName, string Email, ICollection Books); + authors.Add(new AuthorRecordDto(reader.GetInt32(10), reader.GetString(11), reader.GetInt32(4), reader.GetString(3), reader.GetString(0), reader.GetString(1), reader.GetString(2), + new[] { new BookRecordDto(reader.GetString(7), reader.GetInt32(8)) })); + } + + return authors.GroupBy(x => x.UserName) + .Select(x => x.First() with + { + UserName = x.Key, + Books = x.SelectMany(y => y.Books).ToList() + }) + .ToArray(); + } + + [Benchmark] + public AuthorRecordDto[] GetAuthors_OptimizedMultipleQueries() + { + using var db = ContextFactory.CreateDbContext(); + + var authors = db.Set() + .Where(AuthorWhereFilterExpression) + .Select(x => new + { + x.Id, + x.BooksCount, + x.UserId + }) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .ToList(); + + var books = db.Set() + .Where(BookWhereFilterExpression) + .Where(x => authors.Select(y => y.Id).Contains(x.AuthorId)) + .Select(x => new + { + x.AuthorId, + x.Name, + x.Published.Year + }) + .ToList(); + + var users = db.Set() + .Where(x => authors.Select(y => y.UserId).Contains(x.Id)) + .Select(x => new + { + x.Id, + x.UserName, + x.Email, + x.FirstName, + x.LastName + }) + .ToList(); + + return authors.Select(x => + { + var user = users.First(y => y.Id == x.UserId); + + return new AuthorRecordDto(Age, Serbia, x.BooksCount, user.UserName, user.FirstName, user.LastName, user.Email, + books.Where(y => y.AuthorId == x.Id).Select(y => new BookRecordDto(y.Name, y.Year)).ToArray()); + }) + .ToArray(); + } + + private const string CommandText = + @"SELECT [u].[FirstName], [u].[LastName], [u].[Email], [u].[UserName], [t].[BooksCount], [t].[Id], [u].[Id], [t0].[Name], [t0].[PublishedYear], [t0].[Id], [t].[Age], [t].[Country] +FROM ( + SELECT TOP(@take) [a].[Id], [a].[Age], [a].[BooksCount], [a].[Country], [a].[UserId] + FROM [Authors] AS [a] + WHERE ([a].[Country] = @country) AND ([a].[Age] = @age) + ORDER BY [a].[BooksCount] DESC +) AS [t] +INNER JOIN [Users] AS [u] ON [t].[UserId] = [u].[Id] +LEFT JOIN ( + SELECT [b].[Name], DATEPART(year, [b].[Published]) AS [PublishedYear], [b].[Id], [b].[AuthorId] + FROM [Books] AS [b] + WHERE [b].[Published] < DATEFROMPARTS(@year, 1, 1) +) AS [t0] ON [t].[Id] = [t0].[AuthorId] +ORDER BY [t].[BooksCount] DESC, [t].[Id], [u].[Id]"; + + private const string CommandTextWithoutParameters = + @"SELECT [u].[FirstName], [u].[LastName], [u].[Email], [u].[UserName], [t].[BooksCount], [t].[Id], [u].[Id], [t0].[Name], [t0].[PublishedYear], [t0].[Id], [t].[Age], [t].[Country] +FROM ( + SELECT TOP(2) [a].[Id], [a].[Age], [a].[BooksCount], [a].[Country], [a].[UserId] + FROM [Authors] AS [a] + WHERE ([a].[Country] = N'Serbia') AND ([a].[Age] = 27) + ORDER BY [a].[BooksCount] DESC +) AS [t] +INNER JOIN [Users] AS [u] ON [t].[UserId] = [u].[Id] +LEFT JOIN ( + SELECT [b].[Name], DATEPART(year, [b].[Published]) AS [PublishedYear], [b].[Id], [b].[AuthorId] + FROM [Books] AS [b] + WHERE [b].[Published] < DATEFROMPARTS(1900, 1, 1) +) AS [t0] ON [t].[Id] = [t0].[AuthorId] +ORDER BY [t].[BooksCount] DESC, [t].[Id], [u].[Id]"; + + private static readonly ArrayPool ArrayPool = ArrayPool.Shared; + + private const string Serbia = nameof(Serbia); + private const int Age = 27; + private const int Year = 1900; + + public const string ConnectionString = + "Server=c0711p\\sqlstafl;Database=OptimizeMePlease;Trusted_Connection=True;Integrated Security=true;MultipleActiveResultSets=true;TrustServerCertificate=true;"; + + private static readonly PooledDbContextFactory ContextFactory = new(new DbContextOptionsBuilder().UseModel(AppDbContextModel.Instance) + .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) + .EnableThreadSafetyChecks(false) + .UseSqlServer(ConnectionString) + .Options); + + private static readonly Expression> AuthorDtoSelectorExpression = author => new AuthorDtoOptimized + { + UserFirstName = author.User.FirstName, + UserLastName = author.User.LastName, + UserEmail = author.User.Email, + UserName = author.User.UserName, + BooksCount = author.BooksCount, + Books = author.Books.AsQueryable() + .Where(BookWhereFilterExpression) + .Select(BookSelectorExpression) + .ToList(), + Age = author.Age, + Country = author.Country + }; + + private static readonly Expression> BookSelectorExpression = book => new BookDtoOptimized + { + Name = book.Name, + PublishedYear = book.Published.Year + }; + + private static readonly Expression> AuthorRecordDtoSelectorExpression = author => + new AuthorRecordDto(author.Age, + author.Country, + author.BooksCount, + author.User.UserName, + author.User.FirstName, + author.User.LastName, + author.User.Email, + author.Books.AsQueryable() + .Where(BookWhereFilterExpression) + .Select(BookRecordSelectorExpression) + .ToList()); + + private static readonly Expression> BookRecordSelectorExpression = book => new BookRecordDto(book.Name, book.Published.Year); + + private static readonly Expression> AuthorWhereFilterExpression = author => (author.Country == Serbia) && (author.Age == Age); + private static readonly Expression> BookWhereFilterExpression = book => book.Published < EF.Functions.DateFromParts(Year, 1, 1); + + private static readonly Func> Get = + EF.CompileQuery((DbContext db) => + db.Set() + .Where(AuthorWhereFilterExpression) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorDtoSelectorExpression)); + + private static readonly Func> GetWithoutExpressions = + EF.CompileQuery((DbContext db) => + db.Set() + .Where(author => (author.Country == Serbia) && (author.Age == Age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => new AuthorDtoOptimized + { + UserFirstName = author.User.FirstName, + UserLastName = author.User.LastName, + UserEmail = author.User.Email, + UserName = author.User.UserName, + BooksCount = author.BooksCount, + Books = author.Books.AsQueryable() + .Where(book => book.Published < EF.Functions.DateFromParts(Year, 1, 1)) + .Select(y => new BookDtoOptimized + { + Name = y.Name, + PublishedYear = y.Published.Year + }) + .ToList(), + Age = author.Age, + Country = author.Country + })); + + private static readonly Func> GetParameterized = + EF.CompileQuery((DbContext db, string country, int age) => + db.Set() + .Where(x => (x.Country == country) && (x.Age == age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorDtoSelectorExpression)); + + private static readonly Func> GetAsync = + EF.CompileAsyncQuery((DbContext db) => + db.Set() + .Where(AuthorWhereFilterExpression) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorDtoSelectorExpression)); + + private static readonly Func> GetRecord = + EF.CompileQuery((DbContext db) => + db.Set() + .Where(AuthorWhereFilterExpression) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorRecordDtoSelectorExpression)); + + private static readonly Func> GetRecordWithoutExpressions = + EF.CompileQuery((DbContext db) => + db.Set() + .Where(author => (author.Country == Serbia) && (author.Age == Age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => + new AuthorRecordDto(author.Age, + author.Country, + author.BooksCount, + author.User.UserName, + author.User.FirstName, + author.User.LastName, + author.User.Email, + author.Books.AsQueryable() + .Where(book => book.Published < EF.Functions.DateFromParts(Year, 1, 1)) + .Select(y => new BookRecordDto(y.Name, y.Published.Year)) + .ToList()))); + + private static readonly Func> GetRecordAsync = + EF.CompileAsyncQuery((DbContext db) => + db.Set() + .Where(AuthorWhereFilterExpression) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorRecordDtoSelectorExpression)); + + private static readonly Func> GetRecordParameterized = + EF.CompileQuery((DbContext db, string country, int age) => + db.Set() + .Where(x => (x.Country == country) && (x.Age == age)) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorRecordDtoSelectorExpression)); } } diff --git a/BookDto.cs b/BookDto.cs index 7438367..d833f6a 100644 --- a/BookDto.cs +++ b/BookDto.cs @@ -11,4 +11,12 @@ public class BookDto public string PublisherName { get; set; } public string ISBN { get; set; } } + + public sealed class BookDtoOptimized + { + public string Name { get; set; } + public int PublishedYear { get; set; } + } + + public sealed record BookRecordDto(string Name, int PublishedYear); } diff --git a/CompiledModels/AppDbContextModel.cs b/CompiledModels/AppDbContextModel.cs new file mode 100644 index 0000000..8bc7057 --- /dev/null +++ b/CompiledModels/AppDbContextModel.cs @@ -0,0 +1,29 @@ +// +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Context; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + [DbContext(typeof(AppDbContext))] + public partial class AppDbContextModel : RuntimeModel + { + static AppDbContextModel() + { + var model = new AppDbContextModel(); + model.Initialize(); + model.Customize(); + _instance = model; + } + + private static AppDbContextModel _instance; + public static IModel Instance => _instance; + + partial void Initialize(); + + partial void Customize(); + } +} diff --git a/CompiledModels/AppDbContextModelBuilder.cs b/CompiledModels/AppDbContextModelBuilder.cs new file mode 100644 index 0000000..f11497f --- /dev/null +++ b/CompiledModels/AppDbContextModelBuilder.cs @@ -0,0 +1,40 @@ +// +using System; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + public partial class AppDbContextModel + { + partial void Initialize() + { + var author = AuthorEntityType.Create(this); + var book = BookEntityType.Create(this); + var publisher = PublisherEntityType.Create(this); + var role = RoleEntityType.Create(this); + var user = UserEntityType.Create(this); + var userRole = UserRoleEntityType.Create(this); + + AuthorEntityType.CreateForeignKey1(author, user); + BookEntityType.CreateForeignKey1(book, author); + BookEntityType.CreateForeignKey2(book, publisher); + UserRoleEntityType.CreateForeignKey1(userRole, role); + UserRoleEntityType.CreateForeignKey2(userRole, user); + + AuthorEntityType.CreateAnnotations(author); + BookEntityType.CreateAnnotations(book); + PublisherEntityType.CreateAnnotations(publisher); + RoleEntityType.CreateAnnotations(role); + UserEntityType.CreateAnnotations(user); + UserRoleEntityType.CreateAnnotations(userRole); + + AddAnnotation("ProductVersion", "6.0.9"); + AddAnnotation("Relational:MaxIdentifierLength", 128); + AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + } + } +} diff --git a/CompiledModels/AuthorEntityType.cs b/CompiledModels/AuthorEntityType.cs new file mode 100644 index 0000000..65f813f --- /dev/null +++ b/CompiledModels/AuthorEntityType.cs @@ -0,0 +1,110 @@ +// +using System; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Entities; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + internal partial class AuthorEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "OptimizeMePlease.Entities.Author", + typeof(Author), + baseEntityType); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(int), + propertyInfo: typeof(Author).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + var age = runtimeEntityType.AddProperty( + "Age", + typeof(int), + propertyInfo: typeof(Author).GetProperty("Age", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + age.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var booksCount = runtimeEntityType.AddProperty( + "BooksCount", + typeof(int), + propertyInfo: typeof(Author).GetProperty("BooksCount", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + booksCount.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var country = runtimeEntityType.AddProperty( + "Country", + typeof(string), + propertyInfo: typeof(Author).GetProperty("Country", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + country.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var nickName = runtimeEntityType.AddProperty( + "NickName", + typeof(string), + propertyInfo: typeof(Author).GetProperty("NickName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + nickName.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var userId = runtimeEntityType.AddProperty( + "UserId", + typeof(int), + propertyInfo: typeof(Author).GetProperty("UserId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + userId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + var index = runtimeEntityType.AddIndex( + new[] { userId }); + + return runtimeEntityType; + } + + public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("UserId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true); + + var user = declaringEntityType.AddNavigation("User", + runtimeForeignKey, + onDependent: true, + typeof(User), + propertyInfo: typeof(Author).GetProperty("User", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "Authors"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/CompiledModels/BookEntityType.cs b/CompiledModels/BookEntityType.cs new file mode 100644 index 0000000..4760a50 --- /dev/null +++ b/CompiledModels/BookEntityType.cs @@ -0,0 +1,139 @@ +// +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Entities; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + internal partial class BookEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "OptimizeMePlease.Entities.Book", + typeof(Book), + baseEntityType); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(int), + propertyInfo: typeof(Book).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + var authorId = runtimeEntityType.AddProperty( + "AuthorId", + typeof(int), + propertyInfo: typeof(Book).GetProperty("AuthorId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + authorId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var iSBN = runtimeEntityType.AddProperty( + "ISBN", + typeof(string), + propertyInfo: typeof(Book).GetProperty("ISBN", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + iSBN.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var name = runtimeEntityType.AddProperty( + "Name", + typeof(string), + propertyInfo: typeof(Book).GetProperty("Name", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + name.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var published = runtimeEntityType.AddProperty( + "Published", + typeof(DateTime), + propertyInfo: typeof(Book).GetProperty("Published", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + published.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var publisherId = runtimeEntityType.AddProperty( + "PublisherId", + typeof(int), + propertyInfo: typeof(Book).GetProperty("PublisherId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + publisherId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + var index = runtimeEntityType.AddIndex( + new[] { authorId }); + + var index0 = runtimeEntityType.AddIndex( + new[] { publisherId }); + + return runtimeEntityType; + } + + public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("AuthorId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true); + + var author = declaringEntityType.AddNavigation("Author", + runtimeForeignKey, + onDependent: true, + typeof(Author), + propertyInfo: typeof(Book).GetProperty("Author", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var books = principalEntityType.AddNavigation("Books", + runtimeForeignKey, + onDependent: false, + typeof(List), + propertyInfo: typeof(Author).GetProperty("Books", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Author).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PublisherId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true); + + var publisher = declaringEntityType.AddNavigation("Publisher", + runtimeForeignKey, + onDependent: true, + typeof(Publisher), + propertyInfo: typeof(Book).GetProperty("Publisher", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Book).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "Books"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/CompiledModels/PublisherEntityType.cs b/CompiledModels/PublisherEntityType.cs new file mode 100644 index 0000000..035d143 --- /dev/null +++ b/CompiledModels/PublisherEntityType.cs @@ -0,0 +1,66 @@ +// +using System; +using System.Reflection; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Entities; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + internal partial class PublisherEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "OptimizeMePlease.Entities.Publisher", + typeof(Publisher), + baseEntityType); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(int), + propertyInfo: typeof(Publisher).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Publisher).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + var established = runtimeEntityType.AddProperty( + "Established", + typeof(DateTime), + propertyInfo: typeof(Publisher).GetProperty("Established", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Publisher).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + established.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var name = runtimeEntityType.AddProperty( + "Name", + typeof(string), + propertyInfo: typeof(Publisher).GetProperty("Name", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Publisher).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + name.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + return runtimeEntityType; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "Publishers"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/CompiledModels/RoleEntityType.cs b/CompiledModels/RoleEntityType.cs new file mode 100644 index 0000000..c82a230 --- /dev/null +++ b/CompiledModels/RoleEntityType.cs @@ -0,0 +1,59 @@ +// +using System; +using System.Reflection; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Entities; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + internal partial class RoleEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "OptimizeMePlease.Entities.Role", + typeof(Role), + baseEntityType); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(int), + propertyInfo: typeof(Role).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Role).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + var name = runtimeEntityType.AddProperty( + "Name", + typeof(string), + propertyInfo: typeof(Role).GetProperty("Name", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Role).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + name.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + return runtimeEntityType; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "Roles"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/CompiledModels/UserEntityType.cs b/CompiledModels/UserEntityType.cs new file mode 100644 index 0000000..36f36c7 --- /dev/null +++ b/CompiledModels/UserEntityType.cs @@ -0,0 +1,104 @@ +// +using System; +using System.Reflection; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Entities; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + internal partial class UserEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "OptimizeMePlease.Entities.User", + typeof(User), + baseEntityType); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(int), + propertyInfo: typeof(User).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + var created = runtimeEntityType.AddProperty( + "Created", + typeof(DateTime), + propertyInfo: typeof(User).GetProperty("Created", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + created.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var email = runtimeEntityType.AddProperty( + "Email", + typeof(string), + propertyInfo: typeof(User).GetProperty("Email", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + email.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var emailConfirmed = runtimeEntityType.AddProperty( + "EmailConfirmed", + typeof(bool), + propertyInfo: typeof(User).GetProperty("EmailConfirmed", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + emailConfirmed.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var firstName = runtimeEntityType.AddProperty( + "FirstName", + typeof(string), + propertyInfo: typeof(User).GetProperty("FirstName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + firstName.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var lastActivity = runtimeEntityType.AddProperty( + "LastActivity", + typeof(DateTime), + propertyInfo: typeof(User).GetProperty("LastActivity", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + lastActivity.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var lastName = runtimeEntityType.AddProperty( + "LastName", + typeof(string), + propertyInfo: typeof(User).GetProperty("LastName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + lastName.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var userName = runtimeEntityType.AddProperty( + "UserName", + typeof(string), + propertyInfo: typeof(User).GetProperty("UserName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + userName.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + return runtimeEntityType; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "Users"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/CompiledModels/UserRoleEntityType.cs b/CompiledModels/UserRoleEntityType.cs new file mode 100644 index 0000000..cf18219 --- /dev/null +++ b/CompiledModels/UserRoleEntityType.cs @@ -0,0 +1,123 @@ +// +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using OptimizeMePlease.Entities; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace CompiledModels +{ + internal partial class UserRoleEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "OptimizeMePlease.Entities.UserRole", + typeof(UserRole), + baseEntityType); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(int), + propertyInfo: typeof(UserRole).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserRole).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + var roleId = runtimeEntityType.AddProperty( + "RoleId", + typeof(int), + propertyInfo: typeof(UserRole).GetProperty("RoleId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserRole).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + roleId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var userId = runtimeEntityType.AddProperty( + "UserId", + typeof(int), + propertyInfo: typeof(UserRole).GetProperty("UserId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserRole).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + userId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + var index = runtimeEntityType.AddIndex( + new[] { roleId }); + + var index0 = runtimeEntityType.AddIndex( + new[] { userId }); + + return runtimeEntityType; + } + + public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("RoleId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true); + + var role = declaringEntityType.AddNavigation("Role", + runtimeForeignKey, + onDependent: true, + typeof(Role), + propertyInfo: typeof(UserRole).GetProperty("Role", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserRole).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var userRoles = principalEntityType.AddNavigation("UserRoles", + runtimeForeignKey, + onDependent: false, + typeof(List), + propertyInfo: typeof(Role).GetProperty("UserRoles", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(Role).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("UserId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true); + + var user = declaringEntityType.AddNavigation("User", + runtimeForeignKey, + onDependent: true, + typeof(User), + propertyInfo: typeof(UserRole).GetProperty("User", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserRole).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var userRoles = principalEntityType.AddNavigation("UserRoles", + runtimeForeignKey, + onDependent: false, + typeof(List), + propertyInfo: typeof(User).GetProperty("UserRoles", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(User).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "UserRoles"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/Context/AppDbContext.cs b/Context/AppDbContext.cs index a4c4b8b..646841a 100644 --- a/Context/AppDbContext.cs +++ b/Context/AppDbContext.cs @@ -7,7 +7,7 @@ public class AppDbContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder options) { - options.UseSqlServer("Server=localhost;Database=OptimizeMePlease;Trusted_Connection=True;Integrated Security=true;MultipleActiveResultSets=true"); + options.UseSqlServer(BenchmarkService.ConnectionString); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/OptimizeMePlease.csproj b/OptimizeMePlease.csproj index 7e4fcc9..fe28950 100644 --- a/OptimizeMePlease.csproj +++ b/OptimizeMePlease.csproj @@ -1,21 +1,15 @@ - + - - Exe - netcoreapp3.1 - + + Exe + net6.0 + - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + + diff --git a/Program.cs b/Program.cs index b05ee2d..2ff54b3 100644 --- a/Program.cs +++ b/Program.cs @@ -1,9 +1,9 @@ -using BenchmarkDotNet.Running; +using System; +using System.IO; +using BenchmarkDotNet.Running; using Microsoft.Data.SqlClient; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo; -using System; -using System.IO; namespace OptimizeMePlease { @@ -21,27 +21,24 @@ namespace OptimizeMePlease /// public class Program { - static void Main(string[] args) + private static void Main(string[] args) { //Debugging - BenchmarkService benchmarkService = new BenchmarkService(); - benchmarkService.GetAuthors(); - + //var benchmarkService = new BenchmarkService(); + //var a = benchmarkService.GetAuthors_OptimizedRecordSpan(); //Comment me after first execution, please. //IWillPopulateData(); - //BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } public static void IWillPopulateData() { - string sqlConnectionString = @"Server=localhost;Database=OptimizeMePlease;Trusted_Connection=True;Integrated Security=true;MultipleActiveResultSets=true"; - string workingDirectory = Environment.CurrentDirectory; string path = Path.Combine(Directory.GetParent(workingDirectory).Parent.Parent.FullName, @"script.sql"); string script = File.ReadAllText(path); - SqlConnection conn = new SqlConnection(sqlConnectionString); + var conn = new SqlConnection(BenchmarkService.ConnectionString); Server server = new Server(new ServerConnection(conn)); From f0aeaacd57bf01dc843b2f6c539befeb358b3c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0t=C3=A1fl?= <24357229+qjustfeelitp@users.noreply.github.com> Date: Sat, 1 Oct 2022 23:37:58 +0200 Subject: [PATCH 2/3] multiple queries i think that without apropriate indices, the perf way to go is with separate queries --- AuthorDTO.cs | 1 - BenchmarkService.cs | 120 +++++++++++++++++++++++++++++++++++++++++++- Entities/Author.cs | 7 +++ Entities/Book.cs | 7 +++ Entities/User.cs | 9 ++++ 5 files changed, 142 insertions(+), 2 deletions(-) diff --git a/AuthorDTO.cs b/AuthorDTO.cs index 6fa0d8c..f2addeb 100644 --- a/AuthorDTO.cs +++ b/AuthorDTO.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using static OptimizeMePlease.BenchmarkService; namespace OptimizeMePlease { diff --git a/BenchmarkService.cs b/BenchmarkService.cs index 46b7b70..3d908fe 100644 --- a/BenchmarkService.cs +++ b/BenchmarkService.cs @@ -17,7 +17,7 @@ namespace OptimizeMePlease { [MemoryDiagnoser(false)] - //[InProcess] + [InProcess] public class BenchmarkService { /// @@ -439,6 +439,124 @@ public AuthorRecordDto[] GetAuthors_OptimizedMultipleQueries() .ToArray(); } + [Benchmark] + public AuthorRecordDto[] GetAuthors_MultipleQuery() + { + using var db = ContextFactory.CreateDbContext(); + + var authors = GetAuthorsSimplified(db).ToList(); + var books = GetBooksSimplified(db, authors.Select(x => x.Id)).ToList(); + var users = GetUsersSimplified(db, authors.Select(x => x.UserId)).ToList(); + + return ProcessMultipleQueryResult(authors, users, books); + } + + [Benchmark] + public AuthorRecordDto[] GetAuthors_MultipleQueryWithoutSelectorExpressions() + { + using var db = ContextFactory.CreateDbContext(); + + var authors = GetAuthorsSimplifiedWithoutSelectorExpression(db).ToList(); + var books = GetBooksSimplifiedWithoutSelectorExpression(db, authors.Select(x => x.Id)).ToList(); + var users = GetUsersSimplifiedWithoutSelectorExpression(db, authors.Select(x => x.UserId)).ToList(); + + return ProcessMultipleQueryResult(authors, users, books); + } + + private static AuthorRecordDto[] ProcessMultipleQueryResult(IEnumerable authors, IReadOnlyCollection users, IReadOnlyCollection books) + { + return authors.Select(x => + { + var user = users.First(y => y.Id == x.UserId); + + return new AuthorRecordDto(Age, Serbia, x.BookCount, user.UserName, user.FirstName, user.LastName, user.Email, + books.Where(y => y.AuthorId == x.Id).Select(y => new BookRecordDto(y.Name, y.Year)).ToArray()); + }) + .ToArray(); + } + + private static readonly Func> GetAuthorsSimplifiedWithoutSelectorExpression = + EF.CompileQuery((DbContext db) => + db.Set() + .Where(AuthorWhereFilterExpression) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(author => new AuthorSimplified + { + Id = author.Id, + BookCount = author.BooksCount, + UserId = author.UserId + })); + + private static readonly Func, IEnumerable> GetBooksSimplifiedWithoutSelectorExpression = + EF.CompileQuery((DbContext db, IEnumerable authorIds) => + db.Set() + .Where(BookWhereFilterExpression) + .Where(x => authorIds.Contains(x.AuthorId)) + .Select(book => new BookSimplified + { + AuthorId = book.AuthorId, + Name = book.Name, + Year = book.Published.Year + })); + + private static readonly Func, IEnumerable> GetUsersSimplifiedWithoutSelectorExpression = + EF.CompileQuery((DbContext db, IEnumerable userIds) => + db.Set() + .Where(x => userIds.Contains(x.Id)) + .Select(user => new UserSimplified + { + Id = user.Id, + UserName = user.UserName, + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName + })); + + private static readonly Expression> AuthorSimplifiedSelectorExpression = author => new AuthorSimplified + { + Id = author.Id, + BookCount = author.BooksCount, + UserId = author.UserId + }; + + private static readonly Func> GetAuthorsSimplified = + EF.CompileQuery((DbContext db) => + db.Set() + .Where(AuthorWhereFilterExpression) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(AuthorSimplifiedSelectorExpression)); + + private static readonly Expression> BookSimplifiedSelectorExpression = book => new BookSimplified + { + AuthorId = book.AuthorId, + Name = book.Name, + Year = book.Published.Year + }; + + private static readonly Func, IEnumerable> GetBooksSimplified = + EF.CompileQuery((DbContext db, IEnumerable authorIds) => + db.Set() + .Where(BookWhereFilterExpression) + .Where(x => authorIds.Contains(x.AuthorId)) + .Select(BookSimplifiedSelectorExpression)); + + private static readonly Expression> UserSimplifiedSelectorExpression = user => new UserSimplified + { + Id = user.Id, + UserName = user.UserName, + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName + }; + + private static readonly Func, IEnumerable> GetUsersSimplified = + EF.CompileQuery((DbContext db, IEnumerable userIds) => + db.Set() + .Where(x => userIds.Contains(x.Id)) + .Select(UserSimplifiedSelectorExpression)); + private const string CommandText = @"SELECT [u].[FirstName], [u].[LastName], [u].[Email], [u].[UserName], [t].[BooksCount], [t].[Id], [u].[Id], [t0].[Name], [t0].[PublishedYear], [t0].[Id], [t].[Age], [t].[Country] FROM ( diff --git a/Entities/Author.cs b/Entities/Author.cs index 3f6921f..da731d7 100644 --- a/Entities/Author.cs +++ b/Entities/Author.cs @@ -16,4 +16,11 @@ public class Author public int UserId { get; set; } public virtual List Books { get; set; } = new List(); } + + public sealed class AuthorSimplified + { + public int Id { get; set; } + public int BookCount { get; set; } + public int UserId { get; set; } + } } \ No newline at end of file diff --git a/Entities/Book.cs b/Entities/Book.cs index 25ff289..01ef02d 100644 --- a/Entities/Book.cs +++ b/Entities/Book.cs @@ -17,4 +17,11 @@ public class Book public Publisher Publisher { get; set; } public int PublisherId { get; set; } } + + public sealed class BookSimplified + { + public int AuthorId { get; set; } + public string Name { get; set; } + public int Year { get; set; } + } } \ No newline at end of file diff --git a/Entities/User.cs b/Entities/User.cs index 5a8484c..c690c0b 100644 --- a/Entities/User.cs +++ b/Entities/User.cs @@ -15,4 +15,13 @@ public class User public bool EmailConfirmed { get; set; } public DateTime LastActivity { get; set; } } + + public sealed class UserSimplified + { + public int Id { get; set; } + public string UserName { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } } \ No newline at end of file From 59cb58d2cda47b0d60d5fcc8aca5e31368189f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0t=C3=A1fl?= <24357229+qjustfeelitp@users.noreply.github.com> Date: Sun, 2 Oct 2022 00:06:46 +0200 Subject: [PATCH 3/3] remove in process --- BenchmarkService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BenchmarkService.cs b/BenchmarkService.cs index 3d908fe..5d3933f 100644 --- a/BenchmarkService.cs +++ b/BenchmarkService.cs @@ -17,7 +17,7 @@ namespace OptimizeMePlease { [MemoryDiagnoser(false)] - [InProcess] + //[InProcess] public class BenchmarkService { ///