{"id":89,"date":"2020-09-28T14:23:19","date_gmt":"2020-09-28T12:23:19","guid":{"rendered":"https:\/\/mtymejczyk.wordpress.com\/?p=89"},"modified":"2020-09-28T14:23:19","modified_gmt":"2020-09-28T12:23:19","slug":"coffeshop-cz-2-ef-core","status":"publish","type":"post","link":"https:\/\/tymejczyk.net\/index.php\/2020\/09\/28\/coffeshop-cz-2-ef-core\/","title":{"rendered":"CoffeShop cz. 2 &#8211; Migracje i Seedowanie"},"content":{"rendered":"\n<p><strong>K<\/strong>ontynuuje prace nad projektem, \u017ceby tyle nie musie\u0107 mockowa\u0107 podepn\u0119 pod wszystko baze przy u\u017cyciu EF Core \ud83d\ude42<\/p>\n\n\n\n<p>Stworzy\u0142em 3 encje kt\u00f3re ca\u0142kowicie b\u0119d\u0105 wystarczy\u0142y do naszego projektu.<br>S\u0105 to: <em>Order, OrderDetail oraz Product.<\/em><\/p>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">namespace Domain.Entities\n{\n    public class Order\n    {\n        public Order()\n        {\n            OrderDetails = new HashSet&lt;OrderDetail&gt;();\n        }\n\n        public int Id { get; set; }\n        public ICollection&lt;OrderDetail&gt; OrderDetails { get; set; }\n        public DateTime OrderPlaced { get; set; }\n        public DateTime? OrderCompleted { get; set; }\n        public OrderStatus Status { get; set; }\n    }\n\n    public class OrderDetail\n    {\n        public int Id { get; set; }\n        public int Quantity { get; set; }\n        public decimal UnitPrice { get; set; }\n        public int UnitTimeToPrepare { get; set; }\n        public string AdditionalInfo { get; set; }\n\n        public int ProductId { get; set; }\n        public int OrderId { get; set; }\n        public Product Product { get; set; }\n        public Order Order { get; set; }\n    }\n\n    public class Product\n    {\n        public Product()\n        {\n            OrderDetails = new HashSet&lt;OrderDetail&gt;();\n        }\n\n        public int Id { get; set; }\n        public string Name { get; set; }\n        public decimal Price { get; set; }\n        public int TimeToPrepare { get; set; }\n        public ICollection&lt;OrderDetail&gt; OrderDetails { get; set; }\n    }\n}\n\n<\/pre>\n\n\n\n<p>Encja Order posiada pole OrderStatus kt\u00f3rej jest enumem posiadaj\u0105cy 3 stany: <br><em>NotStarted, InProgress, Completed<\/em>.<br><br>Dodane zosta\u0142y tak\u017ce pola wskazuj\u0105ce na czas oczekiwania na zam\u00f3wienie oraz napiwek przy zam\u00f3wieniu powy\u017cej 5 kaw wynosz\u0105cy +10% do ceny zam\u00f3wienia. <br><br>Dzi\u0119ki zastosowaniu <em>ICollection&lt;OrderDetail&gt;<\/em> w klasach <em>Order <\/em>i <em>Product <\/em>oraz dodaniu p\u00f3l <em>Product product<\/em> i <em>Order order<\/em> wraz z ich kluczami otrzymali\u015bmy relacje One-To-Many. \ud83d\ude42<\/p>\n\n\n\n<p>Teraz tworzymy Interfejs IContext i implementujemy go. Pos\u0142u\u017cy on do <em>Dependency Injection<\/em> poprzez konstruktor.<\/p>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">namespace Domain.Interfaces\n{\n    public interface IContext\n    {\n        DbSet&lt;Product&gt; Products { get; set; }\n        DbSet&lt;Order&gt; Orders { get; set; }\n        DbSet&lt;OrderDetail&gt; OrderDetails { get; set; }\n\n        Task&lt;int&gt; SaveChangesAsync();\n    }\n}<\/pre>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">namespace Infrastructure\n{\n    public class Context : DbContext, IContext\n    {\n        public DbSet&lt;Product&gt; Products { get; set; }\n        public DbSet&lt;Order&gt; Orders { get; set; }\n        public DbSet&lt;OrderDetail&gt; OrderDetails { get; set; }\n\n        public Context(DbContextOptions&lt;Context&gt; options)\n            : base(options)\n        { }\n\n        protected override void OnModelCreating(ModelBuilder builder)\n        {\n            \/\/ builder.ApplyConfigurationsFromAssembly(typeof(Context).Assembly);\n            builder.Entity&lt;OrderDetail&gt;()\n             .HasOne(o =&gt; o.Product)\n             .WithMany(p =&gt; p.OrderDetails)\n             .OnDelete(DeleteBehavior.Cascade);\n        }\n\n        public async Task&lt;int&gt; SaveChangesAsync()\n        {\n            return this.SaveChanges();\n        }\n    }\n}<\/pre>\n\n\n\n<p>Nast\u0119pnie dodajemy <em>DI<\/em> dla <em>IContext <\/em>oraz <em>ConnectionString <\/em>kt\u00f3ry zczytywany jest z <em>appsettings.json<\/em> w warstwie Prezentacji.<\/p>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">namespace Infrastructure\n{\n    public static class DependencyInjection\n    {\n        public static void AddInfrastructure(this IServiceCollection services, IConfiguration configuration)\n        {\n            services.AddDbContext&lt;Context&gt;(opt =&gt;\n                opt.UseSqlServer(configuration.GetConnectionString(\"Context\")));\n\n            services.AddScoped&lt;IContext, Context&gt;();\n        }\n    }\n}<\/pre>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">  \"ConnectionStrings\": {\n    \"Context\": \"Server=(localdb)\\\\mssqllocaldb;Database=CoffeShop;Trusted_Connection=True;MultipleActiveResultSets=true\",\n  },<\/pre>\n\n\n\n<p>Tworzymy migracje wpisuj\u0105c w okno PMC:  <em>Add-Migration Initial<\/em><br><br>Je\u015bli wyskakuje b\u0142\u0105d trzeba zrestartowa\u0107 Visual Studio, \u017ceby widzia\u0142o komend\u0119. Dodatkowo w warstwie aplikacji trzeba doinstalowa\u0107 <em><strong>Microsoft.EntityFrameworkCore.Design<\/strong><\/em> o co &#8222;prosi&#8221; sam PMC.<\/p>\n\n\n\n<p>Je\u015bli stworzenie Migracji si\u0119 powiod\u0142o wpisujemy w PMC: <em>Update-Database<\/em><\/p>\n\n\n\n<p>Dodam teraz opcje seedowania bazy. <br>Mo\u017cna by to zrobi\u0107 za pomoc\u0105 ModelBuildera ale zrobie poprostu endpoint kt\u00f3ry b\u0119dziemy mogli seedowa\u0107 baz\u0119 danych je\u015bli nie posiada ona jeszcze element\u00f3w. <br><\/p>\n\n\n\n<p>Najpierw dodajemy klase do seedowania:<\/p>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">namespace Application.System.Command\n{\n    public class SampleDataSeeder : IRequest\n    {\n        public class SampleDataSeederHandler : IRequestHandler&lt;SampleDataSeeder&gt;\n        {\n            private readonly IContext _context;\n\n            public SampleDataSeederHandler(IContext context)\n            {\n                _context = context;\n            }\n\n            private readonly Dictionary&lt;int, Product&gt; Products = new Dictionary&lt;int, Product&gt;();\n\n            public async Task&lt;Unit&gt; Handle(SampleDataSeeder request, CancellationToken cancellationToken)\n            {\n                if (_context.Orders.Any())\n                {\n                    return Unit.Value;\n                }\n\n                await SeedProducts();\n                await SeedOrders();\n\n                return Unit.Value;\n            }\n\n            private async Task SeedProducts()\n            {\n                if (_context.Products.Any() || Products.Any())\n                {\n                    return;\n                }\n\n                Products.Add(1, new Product { Name = \"Cappuccino S\", Price = 5.99m, TimeToPrepare = 3 });\n                Products.Add(2, new Product { Name = \"Cappuccino M\", Price = 8.99m, TimeToPrepare = 4 });\n                Products.Add(3, new Product { Name = \"Cappuccino L\", Price = 9.99m, TimeToPrepare = 4 });\n                Products.Add(4, new Product { Name = \"Espresso\", Price = 4.99m, TimeToPrepare = 2 });\n                Products.Add(5, new Product { Name = \"Espresso Doppio\", Price = 5.99m, TimeToPrepare = 4 });\n                Products.Add(6, new Product { Name = \"Americano S\", Price = 4.99m, TimeToPrepare = 2 });\n                Products.Add(7, new Product { Name = \"Americano M\", Price = 6.99m, TimeToPrepare = 3 });\n                Products.Add(8, new Product { Name = \"Americano L\", Price = 8.99m, TimeToPrepare = 4 });\n                Products.Add(9, new Product { Name = \"Latte S\", Price = 6.99m, TimeToPrepare = 4 });\n                Products.Add(10, new Product { Name = \"Latte M\", Price = 8.59m, TimeToPrepare = 4 });\n                Products.Add(11, new Product { Name = \"Latte L\", Price = 9.99m, TimeToPrepare = 5 });\n\n                foreach (var product in Products.Values)\n                {\n                    _context.Products.Add(product);\n                }\n\n                await _context.SaveChangesAsync();\n            }\n\n            private async Task SeedOrders()\n            {\n                var orders = new List&lt;Order&gt;()\n                {\n                    new Order\n                    {\n                        OrderPlaced = DateTime.UtcNow,\n                        OrderCompleted = DateTime.UtcNow.AddMinutes(7),\n                        Status = OrderStatus.Completed\n                    }.AddOrderDetails(\n                        new OrderDetail { Product = Products[1], Quantity = 2, },\n                        new OrderDetail { Product = Products[2], Quantity = 1, }),\n\n                    new Order\n                    {\n                        OrderPlaced = DateTime.UtcNow,\n                        OrderCompleted = DateTime.UtcNow.AddMinutes(2),\n                        Status = OrderStatus.Completed\n                    }.AddOrderDetails(\n                        new OrderDetail { Product = Products[3], Quantity = 1, }),\n\n                    new Order\n                    {\n                        OrderPlaced = DateTime.UtcNow,\n                        OrderCompleted = DateTime.UtcNow.AddMinutes(9),\n                        Status = OrderStatus.Completed,\n                    }.AddOrderDetails(\n                        new OrderDetail { Product = Products[4], Quantity = 1, },\n                        new OrderDetail { Product = Products[5], Quantity = 3, }),\n\n                    new Order\n                    {\n                        OrderPlaced = DateTime.UtcNow,\n                        OrderCompleted = DateTime.UtcNow.AddMinutes(6),\n                        Status = OrderStatus.Completed,\n                    }.AddOrderDetails(\n                        new OrderDetail { Product = Products[6], Quantity = 2, }),\n\n                    new Order\n                    {\n                        OrderPlaced = DateTime.UtcNow,\n                        OrderCompleted = DateTime.UtcNow.AddMinutes(5),\n                        Status = OrderStatus.Completed,\n                    }.AddOrderDetails(\n                        new OrderDetail { Product = Products[7], Quantity = 3, }),\n                };\n\n                foreach (var order in orders)\n                {\n                    foreach (var detail in order.OrderDetails)\n                    {\n                        detail.UnitPrice = detail.Product.Price * detail.Quantity;\n                        detail.UnitTimeToPrepare = detail.Product.TimeToPrepare * detail.Quantity;\n                    }\n                }\n\n                _context.Orders.AddRange(orders);\n\n                await _context.SaveChangesAsync();\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<p>Oraz endpoint:<\/p>\n\n\n\n<pre class=\"wp-block-syntaxhighlighter-code\">namespace Presentation.Controllers\n{\n    public class SystemController : BaseController\n    {\n        [HttpGet]\n        public async Task&lt;Unit&gt; SeedDatabase()\n        {\n            return await Mediator.Send(new SampleDataSeeder()); \n        }\n    }\n}<\/pre>\n\n\n\n<p>W\u0142\u0105czamy nasz projekt, przechodzimy do Swaggera i strzelamy w <em>\/api\/system<\/em>.<br><br>Teraz je\u015bli u\u017cywamy Visual Studio 2019 mo\u017cemy w \u0142atwy spos\u00f3b podejrze\u0107 zawarto\u015b\u0107 naszej bazy.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"740\" height=\"356\" src=\"https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-6.png?w=740\" alt=\"\" class=\"wp-image-122\" srcset=\"https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-6.png 740w, https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-6-300x144.png 300w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><\/figure>\n\n\n\n<p>Mo\u017cna wpisa\u0107 lub u\u017cy\u0107 skr\u00f3tu klawiszowego. <br>Otwieramy nasz\u0105 baze i sprawdzamy czy wszystko si\u0119 zasia\u0142o jak mia\u0142o \ud83e\udd14<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-7.png?w=458\" alt=\"\" class=\"wp-image-124\" width=\"344\" height=\"367\" srcset=\"https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-7.png 458w, https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-7-281x300.png 281w\" sizes=\"auto, (max-width: 344px) 100vw, 344px\" \/><\/figure>\n\n\n\n<p>Wszystko si\u0119 uda\u0142o \ud83d\ude00 <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1699\" height=\"362\" src=\"https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-8.png?w=1024\" alt=\"\" class=\"wp-image-126\" srcset=\"https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-8.png 1699w, https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-8-300x64.png 300w, https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-8-1024x218.png 1024w, https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-8-768x164.png 768w, https:\/\/tymejczyk.net\/wp-content\/uploads\/2020\/09\/image-8-1536x327.png 1536w\" sizes=\"auto, (max-width: 1699px) 100vw, 1699px\" \/><\/figure>\n\n\n\n<p>Ca\u0142y kod zamieszczam na github:&nbsp;<a href=\"https:\/\/github.com\/MichaelStett\/CoffeShop\">https:\/\/github.com\/MichaelStett\/CoffeShop<\/a><\/p>\n\n\n\n<p>Dzi\u0119kuje za Twoj\u0105 uwag\u0119 i do nast\u0119pnego&nbsp;\ud83d\ude00<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kontynuuje prace nad projektem, \u017ceby tyle nie musie\u0107 mockowa\u0107 podepn\u0119 pod wszystko baze przy u\u017cyciu EF Core \ud83d\ude42 Stworzy\u0142em 3 encje kt\u00f3re ca\u0142kowicie b\u0119d\u0105 wystarczy\u0142y do naszego projektu.S\u0105 to: Order, OrderDetail oraz Product. Encja Order posiada pole OrderStatus kt\u00f3rej jest enumem posiadaj\u0105cy 3 stany: NotStarted, InProgress, Completed. Dodane zosta\u0142y tak\u017ce pola wskazuj\u0105ce na czas oczekiwania [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":133,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-89","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-project-coffeshop"],"_links":{"self":[{"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/posts\/89","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/comments?post=89"}],"version-history":[{"count":0,"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/posts\/89\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/media\/133"}],"wp:attachment":[{"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/media?parent=89"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/categories?post=89"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tymejczyk.net\/index.php\/wp-json\/wp\/v2\/tags?post=89"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}