From dbf76eb7e1f70367264bf1de453355d1351eb6a2 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Sun, 14 Jul 2019 11:08:10 -0700 Subject: [PATCH] Add more MusicStore and Identity tests Covering all the queries MusicStore does, and also updating the tests to properly use store-generated values. Also, added the ModelSnapshots generated by the project templates for 2.1, 2.2, and 3.0, for SQL Server and SQLite. They all diff correctly against the current code showing no differences in the generated schema. Part of #15662 and #11838 Note that currently the ASP.NET 3.0 templates still contain migrations generated by 2.2. See https://github.com/aspnet/AspNetCore/issues/12168 --- .../MigrationsTestBase.cs | 20 + .../MusicStoreTestBase.cs | 528 ++++++++++- .../AspNetIdentity/IdentityDbContext.cs | 3 +- .../AspNetIdentity/IdentityDbContext`.cs | 8 +- .../AspNetIdentity/IdentityDbContext```.cs | 23 + .../AspNetIdentity/IdentityUserContext.cs | 13 +- .../TestModels/MusicStore/Album.cs | 1 - .../TestModels/MusicStore/Artist.cs | 2 - .../TestModels/MusicStore/Genre.cs | 2 - .../TestModels/MusicStore/Order.cs | 1 - .../MigrationsSqlServerTest.cs | 858 +++++++++++++++++- .../MigrationsSqliteTest.cs | 744 ++++++++++++++- 12 files changed, 2112 insertions(+), 91 deletions(-) create mode 100644 test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext```.cs diff --git a/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs b/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs index 7e79736b8bb..36468f1e89b 100644 --- a/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs @@ -357,6 +357,26 @@ protected virtual Task ExecuteAsync(IServiceProvider services, Action(); + var operations = modelDiffer.GetDifferences(sourceModel, targetModel); + + Assert.Equal(0, operations.Count); + } + protected virtual void BuildFirstMigration(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( diff --git a/test/EFCore.Specification.Tests/MusicStoreTestBase.cs b/test/EFCore.Specification.Tests/MusicStoreTestBase.cs index 74e7c0de608..965325ad046 100644 --- a/test/EFCore.Specification.Tests/MusicStoreTestBase.cs +++ b/test/EFCore.Specification.Tests/MusicStoreTestBase.cs @@ -2,9 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestModels.MusicStore; +using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.EntityFrameworkCore @@ -18,6 +20,320 @@ protected MusicStoreTestBase(TFixture fixture) fixture.ListLoggerFactory.Clear(); } + [ConditionalFact] + public async Task Browse_ReturnsViewWithGenre() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + const string genreName = "Genre 1"; + CreateTestGenres(numberOfGenres: 3, numberOfAlbums: 3, context: context); + + var controller = new StoreController(context); + + var result = await controller.Browse(genreName); + + Assert.Equal(genreName, result.Name); + Assert.NotNull(result.Albums); + Assert.Equal(3, result.Albums.Count); + } + } + } + + [ConditionalFact] + public async Task Index_CreatesViewWithGenres() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + CreateTestGenres(numberOfGenres: 10, numberOfAlbums: 1, context: context); + + var controller = new StoreController(context); + + var result = await controller.Index(); + + Assert.Equal(10, result.Count); + } + } + } + + [ConditionalFact] + public async Task Details_ReturnsAlbumDetail() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var genres = CreateTestGenres(numberOfGenres: 3, numberOfAlbums: 3, context: context); + var albumId = genres.First().Albums[2].AlbumId; + + var controller = new StoreController(context); + + var result = await controller.Details(albumId); + + Assert.NotNull(result.Genre); + var genre = genres.SingleOrDefault(g => g.GenreId == result.GenreId); + Assert.NotNull(genre); + Assert.NotNull(genre.Albums.SingleOrDefault(a => a.AlbumId == albumId)); + Assert.NotNull(result.Artist); + Assert.NotEqual(0, result.ArtistId); + } + } + } + + private static Genre[] CreateTestGenres(int numberOfGenres, int numberOfAlbums, DbContext context) + { + var artist = new Artist + { + Name = "Artist1" + }; + + var genres = Enumerable.Range(1, numberOfGenres).Select( + g => + new Genre + { + Name = "Genre " + g, + Albums = Enumerable.Range(1, numberOfAlbums).Select( + n => + new Album + { + Artist = artist, Title = "Greatest Hits" + }).ToList() + }).ToList(); + + context.AddRange(genres); + context.SaveChanges(); + + return genres.ToArray(); + } + + [ConditionalFact] + public async Task Index_GetsSixTopAlbums() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var controller = new HomeController(); + + var albums = TestAlbumDataProvider.GetAlbums(); + + foreach (var album in albums) + { + context.Add(album); + } + + context.SaveChanges(); + + var result = await controller.Index(context); + + Assert.Equal(6, result.Count); + } + } + } + + private static class TestAlbumDataProvider + { + public static Album[] GetAlbums() + { + var genres = Enumerable.Range(1, 10).Select( + n => + new Genre + { + Name = "Genre Name " + n + }).ToArray(); + + var artists = Enumerable.Range(1, 10).Select( + n => + new Artist + { + Name = "Artist Name " + n + }).ToArray(); + + var albums = Enumerable.Range(1, 10).Select( + n => + new Album + { + Artist = artists[n - 1], Genre = genres[n - 1], Title = "Greatest Hits" + }).ToArray(); + + return albums; + } + } + + [ConditionalFact] + public async Task GenreMenuComponent_Returns_NineGenres() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var genreMenuComponent = new GenreMenuComponent(context); + + var genres = Enumerable.Range(1, 10).Select( + n => new Genre + { + Name = $"G{n}" + }); + + context.AddRange(genres); + context.SaveChanges(); + + var result = await genreMenuComponent.InvokeAsync(); + + Assert.Equal(9, result.Count); + } + } + } + + [ConditionalFact] + public async Task AddressAndPayment_RedirectToCompleteWhenSuccessful() + { + const string cartId = "CartId_A"; + + var order = CreateOrder(); + + var formCollection = new Dictionary + { + { + "PromoCode", new[] + { + "FREE" + } + } + }; + + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var cartItems = CreateTestCartItems(cartId, itemPrice: 10, numberOfItems: 1); + context.AddRange(cartItems.Select(n => n.Album).Distinct()); + context.AddRange(cartItems); + context.SaveChanges(); + + var controller = new CheckoutController(formCollection); + + var result = await controller.AddressAndPayment(context, cartId, order); + + Assert.Equal(order.OrderId, result); + } + } + } + + [ConditionalFact] + public async Task AddressAndPayment_ReturnsOrderIfInvalidPromoCode() + { + const string cartId = "CartId_A"; + + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var controller = new CheckoutController(); + var order = CreateOrder(); + + var result = await controller.AddressAndPayment(context, cartId, order); + + Assert.Null(result); + } + } + } + + protected Order CreateOrder(string userName = "RainbowDash") + => new Order + { + Username = userName, + FirstName = "Macavity", + LastName = "Clark", + Address = "11 Meadow Drive", + City = "Healing", + State = "IA", + PostalCode = "DN37 7RU", + Country = "USK", + Phone = "555 887876", + Email = "mc@sample.com" + }; + + [ConditionalFact] + public async Task Complete_ReturnsOrderIdIfValid() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var controller = new CheckoutController(); + + var order = context.Add(CreateOrder()).Entity; + context.SaveChanges(); + + var result = await controller.Complete(context, order.OrderId); + + Assert.Equal(order.OrderId, result); + } + } + } + + [ConditionalFact] + public async Task Complete_ReturnsErrorIfInvalidOrder() + { + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var controller = new CheckoutController(); + + var result = await controller.Complete(context, -3333); + + Assert.Equal("Error", result); + } + } + } + + [ConditionalFact] + public async Task CartSummaryComponent_returns_items() + { + const string cartId = "CartId_A"; + const string albumTitle = "Good goat, M.A.A.D Village"; + const int itemCount = 10; + + using (var context = CreateContext()) + { + using (Fixture.BeginTransaction(context)) + { + var album = new Album + { + Title = albumTitle, + Artist = new Artist + { + Name = "Kung Fu Kenny" + }, + Genre = new Genre + { + Name = "Rap" + } + }; + + var cartItems = Enumerable.Range(1, itemCount).Select( + n => + new CartItem + { + Album = album, Count = 1, CartId = cartId + }).ToArray(); + + context.AddRange(cartItems); + context.SaveChanges(); + + var result = await new CartSummaryComponent(context, cartId).InvokeAsync(); + + Assert.Equal(itemCount, result.CartCount); + Assert.Equal(albumTitle, result.CartSummary); + } + } + } + [ConditionalFact] public void Music_store_project_to_mapped_entity() { @@ -29,10 +345,10 @@ public void Music_store_project_to_mapped_entity() 10, new Artist { - ArtistId = 1, Name = "Kung Fu Kenny" + Name = "Kung Fu Kenny" }, new Genre { - GenreId = 1, Name = "Rap" + Name = "Rap" }); context.Albums.AddRange(albums); @@ -70,25 +386,25 @@ join artist in context.Artists on album.ArtistId equals artist.ArtistId public async Task RemoveFromCart_removes_items_from_cart() { const string cartId = "CartId_A"; - const int cartItemId = 3; - const int numberOfItem = 5; + const int numberOfItems = 5; const int unitPrice = 10; using (var context = CreateContext()) { using (Fixture.BeginTransaction(context)) { - var cartItems = CreateTestCartItems(cartId, unitPrice, numberOfItem); + var cartItems = CreateTestCartItems(cartId, unitPrice, numberOfItems); context.AddRange(cartItems.Select(n => n.Album).Distinct()); context.AddRange(cartItems); context.SaveChanges(); var controller = new ShoppingCartController(context, cartId); + var cartItemId = cartItems[2].CartItemId; var viewModel = await controller.RemoveFromCart(cartItemId); - Assert.Equal(numberOfItem - 1, viewModel.CartCount); - Assert.Equal((numberOfItem - 1) * 10, viewModel.CartTotal); + Assert.Equal(numberOfItems - 1, viewModel.CartCount); + Assert.Equal((numberOfItems - 1) * 10, viewModel.CartTotal); Assert.Equal("Greatest Hits has been removed from your shopping cart.", viewModel.Message); var cart = ShoppingCart.GetCart(context, cartId); @@ -115,7 +431,7 @@ public async Task Cart_is_empty_when_no_items_have_been_added(string cartId) } } - [Fact] + [ConditionalFact] public async Task Cart_has_items_once_they_have_been_added() { const string cartId = "CartId_A"; @@ -127,7 +443,7 @@ public async Task Cart_has_items_once_they_have_been_added() var cartItems = CreateTestCartItems( cartId, itemPrice: 10, - numberOfItem: 5); + numberOfItems: 5); context.AddRange(cartItems.Select(n => n.Album).Distinct()); context.AddRange(cartItems); @@ -142,12 +458,10 @@ public async Task Cart_has_items_once_they_have_been_added() } } - [Fact] + [ConditionalFact] public async Task Can_add_items_to_cart() { const string cartId = "CartId_A"; - const int albumId = 3; - using (var context = CreateContext()) { @@ -157,16 +471,17 @@ public async Task Can_add_items_to_cart() 10, new Artist { - ArtistId = 1, Name = "Kung Fu Kenny" + Name = "Kung Fu Kenny" }, new Genre { - GenreId = 1, Name = "Rap" + Name = "Rap" }); context.AddRange(albums); context.SaveChanges(); var controller = new ShoppingCartController(context, cartId); + var albumId = albums[2].AlbumId; await controller.AddToCart(albumId); var cart = ShoppingCart.GetCart(context, cartId); @@ -176,23 +491,23 @@ public async Task Can_add_items_to_cart() } } - private static CartItem[] CreateTestCartItems(string cartId, decimal itemPrice, int numberOfItem) + private static CartItem[] CreateTestCartItems(string cartId, decimal itemPrice, int numberOfItems) { var albums = CreateTestAlbums( - itemPrice, new Artist + itemPrice, + new Artist { - ArtistId = 1, Name = "Kung Fu Kenny" + Name = "Kung Fu Kenny" }, new Genre { - GenreId = 1, Name = "Rap" + Name = "Rap" }); - var cartItems = Enumerable.Range(1, numberOfItem).Select( - n => - new CartItem() - { - Count = 1, CartId = cartId, AlbumId = n % albums.Length, Album = albums[n % albums.Length], - }).ToArray(); + var cartItems = Enumerable.Range(1, numberOfItems).Select( + n => new CartItem + { + Count = 1, CartId = cartId, Album = albums[n % albums.Length] + }).ToArray(); return cartItems; } @@ -203,14 +518,41 @@ private static Album[] CreateTestAlbums(decimal itemPrice, Artist artist, Genre n => new Album { - Title = "Greatest Hits", - AlbumId = n, - Price = itemPrice, - Artist = artist, - Genre = genre + Title = "Greatest Hits", Price = itemPrice, Artist = artist, Genre = genre }).ToArray(); } + protected class CartSummaryComponent + { + private readonly MusicStoreContext _context; + private readonly string _cartId; + + public CartSummaryComponent(MusicStoreContext context, string cartId) + { + _context = context; + _cartId = cartId; + } + + public async Task InvokeAsync() + { + var cartItems = await ShoppingCart.GetCart(_context, _cartId).GetCartItems(); + + var viewBag = new CartSummaryViewBag + { + CartCount = cartItems.Sum(c => c.Count), + CartSummary = string.Join("\n", cartItems.Select(c => c.Album.Title).Distinct()) + }; + + return viewBag; + } + } + + protected class CartSummaryViewBag + { + public int CartCount { get; set; } + public string CartSummary { get; set; } + } + protected class ShoppingCartController { private readonly MusicStoreContext _context; @@ -284,6 +626,134 @@ public async Task AddToCart(int id) } } + public class CheckoutController + { + private readonly Dictionary _formCollection; + private const string PromoCode = "FREE"; + + public CheckoutController(Dictionary formCollection = null) + { + _formCollection = formCollection ?? new Dictionary(); + } + + public async Task AddressAndPayment(MusicStoreContext context, string cartId, Order order) + { + try + { + if (!string.Equals( + _formCollection["PromoCode"].FirstOrDefault(), + PromoCode, + StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + order.Username = "RainbowDash"; + order.OrderDate = DateTime.Now; + context.Orders.Add(order); + + var cart = ShoppingCart.GetCart(context, cartId); + await cart.CreateOrder(order); + await context.SaveChangesAsync(); + + return order.OrderId; + } + catch (Exception) + { + return null; + } + } + + public async Task Complete(MusicStoreContext context, int id) + { + var userName = "RainbowDash"; + + var isValid = await context.Orders.AnyAsync( + o => o.OrderId == id && + o.Username == userName); + + if (isValid) + { + return id; + } + + return "Error"; + } + } + + public class GenreMenuComponent + { + private readonly MusicStoreContext _context; + + public GenreMenuComponent(MusicStoreContext context) + { + _context = context; + } + + public async Task> InvokeAsync() + { + var genres = await _context.Genres.Select(g => g.Name).Take(9).ToListAsync(); + + return genres; + } + } + + public class HomeController + { + public async Task> Index(MusicStoreContext context) + { + var albums = await GetTopSellingAlbumsAsync(context, 6); + + return albums; + } + + private Task> GetTopSellingAlbumsAsync(MusicStoreContext dbContext, int count) + { + return dbContext.Albums + .OrderByDescending(a => a.OrderDetails.Count) + .Take(count) + .ToListAsync(); + } + } + + public class StoreController + { + private readonly MusicStoreContext _context; + + public StoreController(MusicStoreContext context) + { + _context = context; + } + + public async Task> Index() + { + var genres = await _context.Genres.ToListAsync(); + + return genres; + } + + public async Task Browse(string genre) + { + var genreModel = await _context.Genres + .Include(g => g.Albums) + .Where(g => g.Name == genre) + .FirstOrDefaultAsync(); + + return genreModel; + } + + public async Task Details(int id) + { + var album = await _context.Albums + .Where(a => a.AlbumId == id) + .Include(a => a.Artist) + .Include(a => a.Genre) + .FirstOrDefaultAsync(); + + return album; + } + } + protected TFixture Fixture { get; } protected MusicStoreContext CreateContext() => Fixture.CreateContext(); diff --git a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext.cs b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext.cs index 56018c3f808..d093b75d570 100644 --- a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext.cs +++ b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext.cs @@ -3,8 +3,7 @@ namespace Microsoft.EntityFrameworkCore.TestModels.AspNetIdentity { - public class IdentityDbContext : IdentityDbContext - where TUser : IdentityUser + public class IdentityDbContext : IdentityDbContext { public IdentityDbContext(DbContextOptions options) : base(options) diff --git a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext`.cs b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext`.cs index 29c37cee9e0..56018c3f808 100644 --- a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext`.cs +++ b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext`.cs @@ -1,14 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; - namespace Microsoft.EntityFrameworkCore.TestModels.AspNetIdentity { - public class IdentityDbContext : IdentityDbContext, IdentityUserRole, IdentityUserLogin, IdentityRoleClaim, IdentityUserToken> - where TUser : IdentityUser - where TRole : IdentityRole - where TKey : IEquatable + public class IdentityDbContext : IdentityDbContext + where TUser : IdentityUser { public IdentityDbContext(DbContextOptions options) : base(options) diff --git a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext```.cs b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext```.cs new file mode 100644 index 00000000000..32fcaed859e --- /dev/null +++ b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityDbContext```.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.EntityFrameworkCore.TestModels.AspNetIdentity +{ + public class IdentityDbContext : IdentityDbContext, + IdentityUserRole, IdentityUserLogin, IdentityRoleClaim, IdentityUserToken> + where TUser : IdentityUser + where TRole : IdentityRole + where TKey : IEquatable + { + public IdentityDbContext(DbContextOptions options) + : base(options) + { + } + + protected IdentityDbContext() + { + } + } +} diff --git a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityUserContext.cs b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityUserContext.cs index dd44b7709af..2b851ecc00a 100644 --- a/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityUserContext.cs +++ b/test/EFCore.Specification.Tests/TestModels/AspNetIdentity/IdentityUserContext.cs @@ -39,11 +39,17 @@ public PersonalDataConverter(IPersonalDataProtector protector) } } + private class PersonalDataProtector : IPersonalDataProtector + { + public string Protect(string data) => data; + public string Unprotect(string data) => data; + } + protected override void OnModelCreating(ModelBuilder builder) { - var maxKeyLength = 0; - var encryptPersonalData = false; - PersonalDataConverter converter = null; + const int maxKeyLength = 128; + const bool encryptPersonalData = true; + var converter = new PersonalDataConverter(new PersonalDataProtector()); builder.Entity( b => @@ -60,7 +66,6 @@ protected override void OnModelCreating(ModelBuilder builder) if (encryptPersonalData) { - converter = new PersonalDataConverter(this.GetService()); var personalDataProps = typeof(TUser).GetProperties().Where( prop => Attribute.IsDefined(prop, typeof(ProtectedPersonalDataAttribute))); foreach (var p in personalDataProps) diff --git a/test/EFCore.Specification.Tests/TestModels/MusicStore/Album.cs b/test/EFCore.Specification.Tests/TestModels/MusicStore/Album.cs index cc151d9b6fc..1d3a4dc2cc2 100644 --- a/test/EFCore.Specification.Tests/TestModels/MusicStore/Album.cs +++ b/test/EFCore.Specification.Tests/TestModels/MusicStore/Album.cs @@ -11,7 +11,6 @@ namespace Microsoft.EntityFrameworkCore.TestModels.MusicStore public class Album { [ScaffoldColumn(false)] - [DatabaseGenerated(DatabaseGeneratedOption.None)] public int AlbumId { get; set; } public int GenreId { get; set; } diff --git a/test/EFCore.Specification.Tests/TestModels/MusicStore/Artist.cs b/test/EFCore.Specification.Tests/TestModels/MusicStore/Artist.cs index 4ace89b5acd..c29a59c088d 100644 --- a/test/EFCore.Specification.Tests/TestModels/MusicStore/Artist.cs +++ b/test/EFCore.Specification.Tests/TestModels/MusicStore/Artist.cs @@ -2,13 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; namespace Microsoft.EntityFrameworkCore.TestModels.MusicStore { public class Artist { - [DatabaseGenerated(DatabaseGeneratedOption.None)] public int ArtistId { get; set; } [Required] diff --git a/test/EFCore.Specification.Tests/TestModels/MusicStore/Genre.cs b/test/EFCore.Specification.Tests/TestModels/MusicStore/Genre.cs index 7653cc96321..9aa71ea3f8d 100644 --- a/test/EFCore.Specification.Tests/TestModels/MusicStore/Genre.cs +++ b/test/EFCore.Specification.Tests/TestModels/MusicStore/Genre.cs @@ -3,13 +3,11 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; namespace Microsoft.EntityFrameworkCore.TestModels.MusicStore { public class Genre { - [DatabaseGenerated(DatabaseGeneratedOption.None)] public int GenreId { get; set; } [Required] diff --git a/test/EFCore.Specification.Tests/TestModels/MusicStore/Order.cs b/test/EFCore.Specification.Tests/TestModels/MusicStore/Order.cs index 44f93e5da3b..2e4052cb48d 100644 --- a/test/EFCore.Specification.Tests/TestModels/MusicStore/Order.cs +++ b/test/EFCore.Specification.Tests/TestModels/MusicStore/Order.cs @@ -11,7 +11,6 @@ namespace Microsoft.EntityFrameworkCore.TestModels.MusicStore public class Order { [ScaffoldColumn(false)] - [DatabaseGenerated(DatabaseGeneratedOption.None)] public int OrderId { get; set; } [ScaffoldColumn(false)] diff --git a/test/EFCore.SqlServer.FunctionalTests/MigrationsSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/MigrationsSqlServerTest.cs index 3b7e2496baa..fd32d99b28b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/MigrationsSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/MigrationsSqlServerTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Data.Common; using System.Threading.Tasks; +using Identity30.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; @@ -12,6 +13,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.TestModels.AspNetIdentity; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; @@ -500,14 +502,7 @@ public override void Can_diff_against_2_2_model() { using (var context = new ModelSnapshot22.BloggingContext()) { - var snapshot = new BloggingContextModelSnapshot22(); - var sourceModel = snapshot.Model; - var targetModel = context.Model; - - var modelDiffer = context.GetService(); - var operations = modelDiffer.GetDifferences(sourceModel, targetModel); - - Assert.Equal(0, operations.Count); + DiffSnapshot(new BloggingContextModelSnapshot22(), context); } } @@ -521,46 +516,778 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("ModelSnapshot22.Blog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + modelBuilder.Entity( + "ModelSnapshot22.Blog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("Name"); + b.Property("Name"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("Blogs"); - }); + b.ToTable("Blogs"); + }); - modelBuilder.Entity("ModelSnapshot22.Post", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + modelBuilder.Entity( + "ModelSnapshot22.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("BlogId"); + b.Property("BlogId"); - b.Property("Content"); + b.Property("Content"); - b.Property("EditDate"); + b.Property("EditDate"); - b.Property("Title"); + b.Property("Title"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("BlogId"); + b.HasIndex("BlogId"); - b.ToTable("Post"); - }); + b.ToTable("Post"); + }); - modelBuilder.Entity("ModelSnapshot22.Post", b => - { - b.HasOne("ModelSnapshot22.Blog", "Blog") - .WithMany("Posts") - .HasForeignKey("BlogId"); - }); + modelBuilder.Entity( + "ModelSnapshot22.Post", b => + { + b.HasOne("ModelSnapshot22.Blog", "Blog") + .WithMany("Posts") + .HasForeignKey("BlogId"); + }); +#pragma warning restore 612, 618 + } + } + + public override void Can_diff_against_2_1_ASP_NET_Identity_model() + { + using (var context = new ApplicationDbContext()) + { + DiffSnapshot(new AspNetIdentity21ModelSnapshot(), context); + } + } + + public class AspNetIdentity21ModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } + + public override void Can_diff_against_2_2_ASP_NET_Identity_model() + { + using (var context = new ApplicationDbContext()) + { + DiffSnapshot(new AspNetIdentity22ModelSnapshot(), context); + } + } + + public class AspNetIdentity22ModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } + + public override void Can_diff_against_3_0_ASP_NET_Identity_model() + { + using (var context = new ApplicationDbContext()) + { + DiffSnapshot(new AspNetIdentity30ModelSnapshot(), context); + } + } + + public class AspNetIdentity30ModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity( + "Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); #pragma warning restore 612, 618 } } @@ -595,3 +1322,62 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) public DbSet Blogs { get; set; } } } + +namespace Identity30.Data +{ + public class ApplicationDbContext : IdentityDbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"); + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity( + b => + { + b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique(); + b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex"); + b.ToTable("AspNetUsers"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserClaims"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserLogins"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserTokens"); + }); + + builder.Entity( + b => + { + b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique(); + b.ToTable("AspNetRoles"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetRoleClaims"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserRoles"); + }); + } + } +} diff --git a/test/EFCore.Sqlite.FunctionalTests/MigrationsSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/MigrationsSqliteTest.cs index 517cb6b6560..24c9f6a3a86 100644 --- a/test/EFCore.Sqlite.FunctionalTests/MigrationsSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/MigrationsSqliteTest.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; using System.Data.Common; +using Identity30.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; +using Microsoft.EntityFrameworkCore.TestModels.AspNetIdentity; using Xunit; namespace Microsoft.EntityFrameworkCore @@ -305,14 +307,7 @@ public override void Can_diff_against_2_2_model() { using (var context = new ModelSnapshot22.BloggingContext()) { - var snapshot = new BloggingContextModelSnapshot22(); - var sourceModel = snapshot.Model; - var targetModel = context.Model; - - var modelDiffer = context.GetService(); - var operations = modelDiffer.GetDifferences(sourceModel, targetModel); - - Assert.Equal(0, operations.Count); + DiffSnapshot(new BloggingContextModelSnapshot22(), context); } } @@ -368,6 +363,681 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } + + public class AspNetIdentity21ModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } + + public override void Can_diff_against_2_1_ASP_NET_Identity_model() + { + using (var context = new ApplicationDbContext()) + { + DiffSnapshot(new AspNetIdentity21ModelSnapshot(), context); + } + } + + public class AspNetIdentity22ModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } + + public override void Can_diff_against_2_2_ASP_NET_Identity_model() + { + using (var context = new ApplicationDbContext()) + { + DiffSnapshot(new AspNetIdentity22ModelSnapshot(), context); + } + } + + public class AspNetIdentity30ModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1"); // This came from 3.0 preview 6 + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } + + public override void Can_diff_against_3_0_ASP_NET_Identity_model() + { + using (var context = new ApplicationDbContext()) + { + DiffSnapshot(new AspNetIdentity30ModelSnapshot(), context); + } + } } } @@ -400,3 +1070,61 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) } } +namespace Identity30.Data +{ + public class ApplicationDbContext : IdentityDbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlite("DataSource=Test.db"); + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity( + b => + { + b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique(); + b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex"); + b.ToTable("AspNetUsers"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserClaims"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserLogins"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserTokens"); + }); + + builder.Entity( + b => + { + b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique(); + b.ToTable("AspNetRoles"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetRoleClaims"); + }); + + builder.Entity>( + b => + { + b.ToTable("AspNetUserRoles"); + }); + } + } +}