diff --git a/AuthorDTO.cs b/AuthorDTO.cs index dc6416c..7f5a82d 100644 --- a/AuthorDTO.cs +++ b/AuthorDTO.cs @@ -21,5 +21,5 @@ public class AuthorDTO public int AuthorAge { get; set; } public string AuthorCountry { get; set; } public string AuthorNickName { get; set; } - } + } } diff --git a/AuthorDTO_Simple.cs b/AuthorDTO_Simple.cs new file mode 100644 index 0000000..8f09dbf --- /dev/null +++ b/AuthorDTO_Simple.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace OptimizeMePlease +{ + public class AuthorDTO_Simple + { + //public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public int Age { get; set; } + public string Country { get; set; } + //public string NickName { get; set; } + public string Email { get; set; } + public string UserName { get; set; } + //public int UserId { get; set; } + //public int RoleId { get; set; } + public int BooksCount { get; set; } + public IEnumerable AllBooks { get; set; } + + } +} diff --git a/BenchmarkService.cs b/BenchmarkService.cs index d0a958d..368ec20 100644 --- a/BenchmarkService.cs +++ b/BenchmarkService.cs @@ -1,6 +1,7 @@ using BenchmarkDotNet.Attributes; using Microsoft.EntityFrameworkCore; using OptimizeMePlease.Context; +using System; using System.Collections.Generic; using System.Linq; @@ -19,7 +20,7 @@ public BenchmarkService() /// and all his/her books (Book Name/Title and Publishment Year) published before 1900 /// /// - [Benchmark] + //[Benchmark] public List GetAuthors() { using var dbContext = new AppDbContext(); @@ -85,12 +86,78 @@ public List GetAuthors() return finalAuthors; } - [Benchmark] + //[Benchmark] public List GetAuthors_Optimized() { - List authors = new List(); + using var dbContext = new AppDbContext(); + + var authors = dbContext.Authors + .AsNoTracking() + .Where(x => x.Country == "Serbia" && x.Age == 27) + .OrderByDescending(b => b.BooksCount) + .Take(2) + .Include(x => x.User) + .ThenInclude(x => x.UserRoles) + .Include(x => x.Books) + .ThenInclude(x => x.Publisher) + .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.SingleOrDefault(y => y.UserId == x.UserId).RoleId, + BooksCount = x.BooksCount, + AllBooks = x.Books.Where(b => b.Published.Year < 1900).Select(y => new BookDto + { + Id = y.Id, + Name = y.Name, + Published = y.Published, + ISBN = y.ISBN, + PublisherName = y.Publisher.Name, + PublishedYear = y.Published.Year + }).ToList(), + AuthorAge = x.Age, + AuthorCountry = x.Country, + AuthorNickName = x.NickName, + Id = x.Id + }); + + return authors.ToList(); + } - return authors; + + [Benchmark] + public List GetAuthors_Optimized_Meaningful() + { + using var dbContext = new AppDbContext(); + var date = new DateTime(1900, 1, 1); + return dbContext.Authors + .Include(x => x.Books.Where(b => b.Published < date)) + .AsNoTracking() + .Where(x => x.Country == "Serbia" && x.Age == 27) + .OrderByDescending(x => x.BooksCount) + .Take(2) + .Select(x => new AuthorDTO_Simple + { + FirstName = x.User.FirstName, + LastName = x.User.LastName, + Email = x.User.Email, + UserName = x.User.UserName, + BooksCount = x.BooksCount, + AllBooks = x.Books.Select(y => new BookDto_Meaningful + { + Name = y.Name, + PublishedYear = y.Published + }), + Age = x.Age, + Country = x.Country, + }) + .ToList(); } } } diff --git a/BookDto.cs b/BookDto.cs index 7438367..55f1336 100644 --- a/BookDto.cs +++ b/BookDto.cs @@ -10,5 +10,5 @@ public class BookDto public int PublishedYear { get; set; } public string PublisherName { get; set; } public string ISBN { get; set; } - } + } } diff --git a/BookDto_Meaningful.cs b/BookDto_Meaningful.cs new file mode 100644 index 0000000..eb97c2b --- /dev/null +++ b/BookDto_Meaningful.cs @@ -0,0 +1,13 @@ +using System; + +namespace OptimizeMePlease +{ + public class BookDto_Meaningful + { + //public int Id { get; set; } + public string Name { get; set; } + public DateTime PublishedYear { get; set; } + //public string PublisherName { get; set; } + //public string ISBN { get; set; } + } +} diff --git a/Context/AppDbContext.cs b/Context/AppDbContext.cs index a4c4b8b..e8f3892 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("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=master;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;Database=OptimizeMePlease"); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/OptimizeMePlease.csproj b/OptimizeMePlease.csproj index 7e4fcc9..c898ab0 100644 --- a/OptimizeMePlease.csproj +++ b/OptimizeMePlease.csproj @@ -2,16 +2,16 @@ Exe - netcoreapp3.1 + net6.0 - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/OptimizeMePlease.sln b/OptimizeMePlease.sln new file mode 100644 index 0000000..583c5ea --- /dev/null +++ b/OptimizeMePlease.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OptimizeMePlease", "OptimizeMePlease.csproj", "{BD7CFAC4-DEF7-4A49-BA9A-39D891961236}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BD7CFAC4-DEF7-4A49-BA9A-39D891961236}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD7CFAC4-DEF7-4A49-BA9A-39D891961236}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD7CFAC4-DEF7-4A49-BA9A-39D891961236}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD7CFAC4-DEF7-4A49-BA9A-39D891961236}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Program.cs b/Program.cs index b05ee2d..91a7fd2 100644 --- a/Program.cs +++ b/Program.cs @@ -24,18 +24,23 @@ public class Program static void Main(string[] args) { //Debugging - BenchmarkService benchmarkService = new BenchmarkService(); - benchmarkService.GetAuthors(); + //BenchmarkService benchmarkService = new BenchmarkService(); + // Original query + //var nonOpt = benchmarkService.GetAuthors(); + // Optimized query without uchanging the dto + //var Opt = benchmarkService.GetAuthors_Optimized(); + // Optimized query with changing DTOs + //var Opt_Meaningful = benchmarkService.GetAuthors_Optimized_Meaningful(); //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 sqlConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=master;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;Database=OptimizeMePlease"; string workingDirectory = Environment.CurrentDirectory; string path = Path.Combine(Directory.GetParent(workingDirectory).Parent.Parent.FullName, @"script.sql"); diff --git a/README.md b/README.md index 2bef293..62cbe27 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,3 @@ -# OptimizeMePlease +### Note: In GetAuthors_Optimized_Meaningful I edited the DTOs to a more meaningful one including only the properties that are necessary (In my opinion). -## You are probably here because you saw my post on Linkedin. -## Welcome! - -# Steps - -- Before you run an application, you will need to create a database (I'm using MSSQL) named "OptimizeMePlease" -- Go to Program.cs class -- "IWillPopulateDate()" is a method which will get a script from the project directory and run in on created DB -- Run application in Debug/Release mode -- Comment or delete IWillPopulateData() call from Main method -- Go to BenchmarkService.cs class -- Start coding within GetAuthors_Optimized method - -# How do I submit my solution? - -- Send me a Linkedin message with your github username, I will add you and create a branch for you. - -### OR - -- Clone the project, create a branch and work on that branch. - -### OR - -- If you don't want to bother with github, after you're done with the changes, send me the results on Linkedin. - -# Rules - -- Only Entity Framework (Core) is allowed for using -- The data obtained in the non-optimized version of the code must also be obtained in the optimized version -- If you see potential optimization of something else, you can do it -- Entities and DbContext cannot be changed (you got legacy code no matter what it might be bad :) ) -- The models returned from the method can be changed - -# What should the method return? - -- Given that there is a predefined database of data, the method should in any case return the list of data currently returned by the non-optimized method. - -# How will performance success be measured? - -- Given that each of us works on a computer with different performance and power, the execution time quotient of the non-optimized and optimized method will be checked for each separately. - -Example: - -Non-optimized method Execution time: 1.1s = 1100ms -Optimized method Execution time: 200ms - -### Result: 1100ms/200ms = 5.5x faster. - -In addition, I will check the performance on my computer for individual results. - -# GOOD LUCK! +![image](https://user-images.githubusercontent.com/64654197/193173418-097870d8-b93f-41fe-b89a-dee5d9ebbb13.png)