diff --git a/samples/core/Miscellaneous/NewInEFCore7/DocumentsContext.cs b/samples/core/Miscellaneous/NewInEFCore7/DocumentsContext.cs new file mode 100644 index 0000000000..df109bb87d --- /dev/null +++ b/samples/core/Miscellaneous/NewInEFCore7/DocumentsContext.cs @@ -0,0 +1,673 @@ +namespace NewInEfCore7; + +public abstract class Document +{ + protected Document(string title, int numberOfPages, DateTime publicationDate, byte[]? coverArt) + { + Title = title; + NumberOfPages = numberOfPages; + PublicationDate = publicationDate; + CoverArt = coverArt; + } + + public int Id { get; private set; } + + [Timestamp] + public byte[]? RowVersion { get; set; } + + public string Title { get; set; } + public int NumberOfPages { get; set; } + public DateTime PublicationDate { get; set; } + public byte[]? CoverArt { get; set; } + + public DateTime FirstRecordedOn { get; private set; } + public DateTime RetrievedOn { get; private set; } +} + +public class Book : Document +{ + public Book(string title, int numberOfPages, DateTime publicationDate, byte[]? coverArt) + : base(title, numberOfPages, publicationDate, coverArt) + { + } + + public string? Isbn { get; set; } + + public List Authors { get; } = new(); +} + +public class Magazine : Document +{ + public Magazine(string title, int numberOfPages, DateTime publicationDate, byte[]? coverArt, int issueNumber) + : base(title, numberOfPages, publicationDate, coverArt) + { + IssueNumber = issueNumber; + } + + public int IssueNumber { get; set; } + public decimal? CoverPrice { get; set; } + public Person Editor { get; set; } = null!; +} + +public class Person +{ + public Person(string name) + { + Name = name; + } + + public int Id { get; private set; } + + [ConcurrencyCheck] + public string Name { get; set; } + + public ContactDetails Contact { get; set; } = null!; + + public List PublishedWorks { get; } = new(); + public List Edited { get; } = new(); +} + +public abstract class DocumentsContext : DbContext +{ + public bool LoggingEnabled { get; set; } + public abstract MappingStrategy MappingStrategy { get; } + public virtual bool UseStoredProcedures => true; + + public DbSet Documents => Set(); + public DbSet Books => Set(); + public DbSet Magazines => Set(); + public DbSet People => Set(); + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}") + .EnableSensitiveDataLogging() + .LogTo( + s => + { + if (LoggingEnabled) + { + Console.WriteLine(s); + } + }, LogLevel.Information); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity( + entityTypeBuilder => + { + entityTypeBuilder.Property(document => document.FirstRecordedOn).HasDefaultValueSql("getutcdate()"); + entityTypeBuilder.Property(document => document.RetrievedOn).HasComputedColumnSql("getutcdate()"); + }); + + modelBuilder.Entity( + entityTypeBuilder => + { + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Person_Insert", storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter(a => a.Name); + storedProcedureBuilder.HasResultColumn(a => a.Id); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Person_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(person => person.Id); + storedProcedureBuilder.HasOriginalValueParameter( + person => person.Name, parameterBuilder => parameterBuilder.HasName("Name_Original")); + storedProcedureBuilder.HasParameter(person => person.Name); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Person_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(person => person.Id); + storedProcedureBuilder.HasOriginalValueParameter(person => person.Name); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + + entityTypeBuilder.OwnsOne( + author => author.Contact, + ownedNavigationBuilder => + { + ownedNavigationBuilder.ToTable("Contacts"); + + if (UseStoredProcedures) + { + ownedNavigationBuilder.InsertUsingStoredProcedure( + "Contacts_Insert", storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter("PersonId"); + storedProcedureBuilder.HasParameter(contactDetails => contactDetails.Phone); + }); + + ownedNavigationBuilder.UpdateUsingStoredProcedure( + "Contacts_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("PersonId"); + storedProcedureBuilder.HasParameter(contactDetails => contactDetails.Phone); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + ownedNavigationBuilder.DeleteUsingStoredProcedure( + "Contacts_Delete", storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("PersonId"); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + + ownedNavigationBuilder.OwnsOne( + contactDetails => contactDetails.Address, + ownedOwnedNavigationBuilder => + { + ownedOwnedNavigationBuilder.ToTable("Addresses"); + + if (UseStoredProcedures) + { + ownedOwnedNavigationBuilder.InsertUsingStoredProcedure( + "Addresses_Insert", storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter("ContactDetailsPersonId"); + storedProcedureBuilder.HasParameter(address => address.Street); + storedProcedureBuilder.HasParameter(address => address.City); + storedProcedureBuilder.HasParameter(address => address.Postcode); + storedProcedureBuilder.HasParameter(address => address.Country); + }); + + ownedOwnedNavigationBuilder.UpdateUsingStoredProcedure( + "Addresses_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("ContactDetailsPersonId"); + storedProcedureBuilder.HasParameter(address => address.Street); + storedProcedureBuilder.HasParameter(address => address.City); + storedProcedureBuilder.HasParameter(address => address.Postcode); + storedProcedureBuilder.HasParameter(address => address.Country); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + ownedOwnedNavigationBuilder.DeleteUsingStoredProcedure( + "Addresses_Delete", storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("ContactDetailsPersonId"); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + }); + }); + } + + public async Task Seed() + { + var kentBeck = new Person("Kent Beck") + { + Contact = new() { Address = new Address("1 Smalltalk Ave", "Camberwick Green", "CW1 5ZH", "UK"), Phone = "01632 12346" } + }; + + var joshuaBloch = new Person("Joshua Bloch") + { + Contact = new() { Address = new Address("1 AFS Walk", "Chigley", "CW1 5ZH", "UK"), Phone = "01632 12347" } + }; + + var nealGafter = new Person("Neal Gafter") + { + Contact = new() { Address = new Address("1 Merlin Closure", "Chigley", "CW1 5ZH", "UK"), Phone = "01632 12348" } + }; + + var simonRockman = new Person("Simon Rockman") + { + Contact = new() { Address = new Address("1 Copper Run", "Camberwick Green", "CW1 5ZH", "UK"), Phone = "01632 12349" } + }; + + var documents = new List + { + new Book("Extreme Programming Explained", 190, new DateTime(2000, 1, 1), null) + { + Isbn = "201-61641-6", Authors = { kentBeck } + }, + new Book("Java Puzzlers", 283, new DateTime(2005, 1, 1), null) + { + Isbn = "0-321-33678-X", Authors = { joshuaBloch, nealGafter } + }, + new Book("Effective Java", 252, new DateTime(2001, 1, 1), new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) + { + Isbn = "0-201-31005-8", Authors = { joshuaBloch } + }, + new Book("Test-Driven Development By Example", 220, new DateTime(2003, 1, 1), null) + { + Isbn = "0-321-14653-0", Authors = { kentBeck } + }, + new Magazine("Amstrad Computer User", 95, new DateTime(1986, 1, 12), new byte[] { 1, 2, 3 }, 15) + { + CoverPrice = 0.95m, Editor = simonRockman + }, + new Magazine("Amiga Computing", 90, new DateTime(1988, 5, 16), null, 1) { CoverPrice = 1.95m, Editor = simonRockman } + }; + + await AddRangeAsync(documents); + await SaveChangesAsync(); + } +} + +public class TphDocumentsContext : DocumentsContext +{ + public override MappingStrategy MappingStrategy => MappingStrategy.Tph; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity( + entityTypeBuilder => + { + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Document_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter("Discriminator"); + storedProcedureBuilder.HasParameter(document => document.Title); + storedProcedureBuilder.HasParameter(document => document.NumberOfPages); + storedProcedureBuilder.HasParameter(document => document.PublicationDate); + storedProcedureBuilder.HasParameter(document => document.CoverArt); + storedProcedureBuilder.HasResultColumn(document => document.Id); + storedProcedureBuilder.HasParameter((Book document) => document.Isbn); + storedProcedureBuilder.HasParameter((Magazine document) => document.CoverPrice); + storedProcedureBuilder.HasParameter((Magazine document) => document.IssueNumber); + storedProcedureBuilder.HasParameter("EditorId"); + storedProcedureBuilder.HasResultColumn(document => document.FirstRecordedOn); + storedProcedureBuilder.HasResultColumn(document => document.RetrievedOn); + storedProcedureBuilder.HasResultColumn(document => document.RowVersion); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Document_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(document => document.Id); + storedProcedureBuilder.HasOriginalValueParameter( + document => document.RowVersion, + parameterBuilder => parameterBuilder.HasName("RowVersion_Original")); + storedProcedureBuilder.HasParameter(document => document.Title); + storedProcedureBuilder.HasParameter(document => document.NumberOfPages); + storedProcedureBuilder.HasParameter(document => document.PublicationDate); + storedProcedureBuilder.HasParameter(document => document.CoverArt); + storedProcedureBuilder.HasParameter(document => ((Book)document).Isbn); + storedProcedureBuilder.HasParameter(document => ((Magazine)document).CoverPrice); + storedProcedureBuilder.HasParameter(document => ((Magazine)document).IssueNumber); + storedProcedureBuilder.HasParameter("EditorId"); + storedProcedureBuilder.HasParameter(document => document.FirstRecordedOn); + storedProcedureBuilder.HasParameter( + document => document.RetrievedOn, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasParameter( + document => document.RowVersion, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Document_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(document => document.Id); + storedProcedureBuilder.HasOriginalValueParameter(document => document.RowVersion); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + modelBuilder.Entity( + entityTypeBuilder => + { + entityTypeBuilder + .HasMany(document => document.Authors) + .WithMany(author => author.PublishedWorks) + .UsingEntity>( + "BookPerson", + builder => builder.HasOne().WithMany().OnDelete(DeleteBehavior.Cascade), + builder => builder.HasOne().WithMany().OnDelete(DeleteBehavior.ClientCascade), + joinTypeBuilder => + { + if (UseStoredProcedures) + { + joinTypeBuilder.InsertUsingStoredProcedure( + "BookPerson_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter("AuthorsId"); + storedProcedureBuilder.HasParameter("PublishedWorksId"); + }); + + joinTypeBuilder.DeleteUsingStoredProcedure( + "BookPerson_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("AuthorsId"); + storedProcedureBuilder.HasOriginalValueParameter("PublishedWorksId"); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + }); + + base.OnModelCreating(modelBuilder); + } +} + +public class TptDocumentsContext : DocumentsContext +{ + public override MappingStrategy MappingStrategy => MappingStrategy.Tpt; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().UseTptMappingStrategy(); + + modelBuilder.Entity( + entityTypeBuilder => + { + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Document_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter(document => document.Title); + storedProcedureBuilder.HasParameter(document => document.NumberOfPages); + storedProcedureBuilder.HasParameter(document => document.PublicationDate); + storedProcedureBuilder.HasParameter(document => document.CoverArt); + storedProcedureBuilder.HasResultColumn(document => document.Id); + storedProcedureBuilder.HasResultColumn(document => document.FirstRecordedOn); + storedProcedureBuilder.HasResultColumn(document => document.RetrievedOn); + storedProcedureBuilder.HasResultColumn(document => document.RowVersion); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Document_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(document => document.Id); + storedProcedureBuilder.HasOriginalValueParameter( + document => document.RowVersion, + parameterBuilder => parameterBuilder.HasName("RowVersion_Original")); + storedProcedureBuilder.HasParameter(document => document.Title); + storedProcedureBuilder.HasParameter(document => document.NumberOfPages); + storedProcedureBuilder.HasParameter(document => document.PublicationDate); + storedProcedureBuilder.HasParameter(document => document.CoverArt); + storedProcedureBuilder.HasParameter(document => document.FirstRecordedOn); + storedProcedureBuilder.HasParameter( + magazine => magazine.RetrievedOn, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasParameter( + magazine => magazine.RowVersion, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Document_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(document => document.Id); + storedProcedureBuilder.HasOriginalValueParameter(document => document.RowVersion); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + modelBuilder.Entity( + entityTypeBuilder => + { + entityTypeBuilder + .HasMany(document => document.Authors) + .WithMany(author => author.PublishedWorks) + .UsingEntity>( + "BookPerson", + builder => builder.HasOne().WithMany().OnDelete(DeleteBehavior.Cascade), + builder => builder.HasOne().WithMany().OnDelete(DeleteBehavior.ClientCascade), + joinTypeBuilder => + { + if (UseStoredProcedures) + { + joinTypeBuilder.InsertUsingStoredProcedure( + "BookPerson_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter("AuthorsId"); + storedProcedureBuilder.HasParameter("PublishedWorksId"); + }); + + joinTypeBuilder.DeleteUsingStoredProcedure( + "BookPerson_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("AuthorsId"); + storedProcedureBuilder.HasOriginalValueParameter("PublishedWorksId"); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Book_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter(book => book.Id); + storedProcedureBuilder.HasParameter(book => book.Isbn); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Book_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(book => book.Id); + storedProcedureBuilder.HasParameter(book => book.Isbn); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Book_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(book => book.Id); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + modelBuilder.Entity( + entityTypeBuilder => + { + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Magazine_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter(magazine => magazine.Id); + storedProcedureBuilder.HasParameter(magazine => magazine.CoverPrice); + storedProcedureBuilder.HasParameter(magazine => magazine.IssueNumber); + storedProcedureBuilder.HasParameter("EditorId"); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Magazine_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(magazine => magazine.Id); + storedProcedureBuilder.HasParameter(magazine => magazine.CoverPrice); + storedProcedureBuilder.HasParameter(magazine => magazine.IssueNumber); + storedProcedureBuilder.HasParameter("EditorId"); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Magazine_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(magazine => magazine.Id); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + base.OnModelCreating(modelBuilder); + } +} + +public class TpcDocumentsContext : DocumentsContext +{ + public override MappingStrategy MappingStrategy => MappingStrategy.Tpc; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().UseTpcMappingStrategy(); + + modelBuilder.Entity( + entityTypeBuilder => + { + entityTypeBuilder + .HasMany(document => document.Authors) + .WithMany(author => author.PublishedWorks) + .UsingEntity>( + "BookPerson", + builder => builder.HasOne().WithMany().OnDelete(DeleteBehavior.Cascade), + builder => builder.HasOne().WithMany().OnDelete(DeleteBehavior.ClientCascade), + joinTypeBuilder => + { + if (UseStoredProcedures) + { + joinTypeBuilder.InsertUsingStoredProcedure( + "BookPerson_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter("AuthorsId"); + storedProcedureBuilder.HasParameter("PublishedWorksId"); + }); + + joinTypeBuilder.DeleteUsingStoredProcedure( + "BookPerson_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter("AuthorsId"); + storedProcedureBuilder.HasOriginalValueParameter("PublishedWorksId"); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Book_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter(book => book.Title); + storedProcedureBuilder.HasParameter(book => book.NumberOfPages); + storedProcedureBuilder.HasParameter(book => book.PublicationDate); + storedProcedureBuilder.HasParameter(book => book.CoverArt); + storedProcedureBuilder.HasParameter(book => book.Isbn); + storedProcedureBuilder.HasResultColumn(book => book.Id); + storedProcedureBuilder.HasResultColumn(book => book.FirstRecordedOn); + storedProcedureBuilder.HasResultColumn(book => book.RetrievedOn); + storedProcedureBuilder.HasResultColumn(book => book.RowVersion); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Book_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(book => book.Id); + storedProcedureBuilder.HasOriginalValueParameter( + magazine => magazine.RowVersion, + parameterBuilder => parameterBuilder.HasName("RowVersion_Original")); + storedProcedureBuilder.HasParameter(book => book.Title); + storedProcedureBuilder.HasParameter(book => book.NumberOfPages); + storedProcedureBuilder.HasParameter(book => book.PublicationDate); + storedProcedureBuilder.HasParameter(book => book.CoverArt); + storedProcedureBuilder.HasParameter(book => book.FirstRecordedOn); + storedProcedureBuilder.HasParameter(book => book.Isbn); + storedProcedureBuilder.HasParameter( + magazine => magazine.RetrievedOn, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasParameter( + magazine => magazine.RowVersion, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Book_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(book => book.Id); + storedProcedureBuilder.HasOriginalValueParameter(book => book.RowVersion); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + modelBuilder.Entity( + entityTypeBuilder => + { + if (UseStoredProcedures) + { + entityTypeBuilder.InsertUsingStoredProcedure( + "Magazine_Insert", + storedProcedureBuilder => + { + storedProcedureBuilder.HasParameter(magazine => magazine.Title); + storedProcedureBuilder.HasParameter(magazine => magazine.NumberOfPages); + storedProcedureBuilder.HasParameter(magazine => magazine.PublicationDate); + storedProcedureBuilder.HasParameter(magazine => magazine.CoverArt); + storedProcedureBuilder.HasResultColumn(magazine => magazine.Id); + storedProcedureBuilder.HasParameter(magazine => magazine.CoverPrice); + storedProcedureBuilder.HasParameter(magazine => magazine.IssueNumber); + storedProcedureBuilder.HasParameter("EditorId"); + storedProcedureBuilder.HasResultColumn(magazine => magazine.FirstRecordedOn); + storedProcedureBuilder.HasResultColumn(magazine => magazine.RetrievedOn); + storedProcedureBuilder.HasResultColumn(magazine => magazine.RowVersion); + }); + + entityTypeBuilder.UpdateUsingStoredProcedure( + "Magazine_Update", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(magazine => magazine.Id); + storedProcedureBuilder.HasOriginalValueParameter( + magazine => magazine.RowVersion, + parameterBuilder => parameterBuilder.HasName("RowVersion_Original")); + storedProcedureBuilder.HasParameter(magazine => magazine.Title); + storedProcedureBuilder.HasParameter(magazine => magazine.NumberOfPages); + storedProcedureBuilder.HasParameter(magazine => magazine.PublicationDate); + storedProcedureBuilder.HasParameter(magazine => magazine.CoverArt); + storedProcedureBuilder.HasParameter(magazine => magazine.CoverPrice); + storedProcedureBuilder.HasParameter(magazine => magazine.IssueNumber); + storedProcedureBuilder.HasParameter("EditorId"); + storedProcedureBuilder.HasParameter(magazine => magazine.FirstRecordedOn); + storedProcedureBuilder.HasParameter( + magazine => magazine.RetrievedOn, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasParameter( + magazine => magazine.RowVersion, parameterBuilder => parameterBuilder.IsOutput()); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + + entityTypeBuilder.DeleteUsingStoredProcedure( + "Magazine_Delete", + storedProcedureBuilder => + { + storedProcedureBuilder.HasOriginalValueParameter(magazine => magazine.Id); + storedProcedureBuilder.HasOriginalValueParameter(magazine => magazine.RowVersion); + storedProcedureBuilder.HasRowsAffectedResultColumn(); + }); + } + }); + + base.OnModelCreating(modelBuilder); + } +} diff --git a/samples/core/Miscellaneous/NewInEFCore7/DocumentsContextStoredProcedures.cs b/samples/core/Miscellaneous/NewInEFCore7/DocumentsContextStoredProcedures.cs new file mode 100644 index 0000000000..5c5ad9a905 --- /dev/null +++ b/samples/core/Miscellaneous/NewInEFCore7/DocumentsContextStoredProcedures.cs @@ -0,0 +1,474 @@ +namespace NewInEfCore7; + +public static class DocumentsContextStoredProcedures +{ + public static async Task CreateStoredProcedures(this DocumentsContext context) + { + switch (context.MappingStrategy) + { + case MappingStrategy.Tph: + // Document + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Document_Insert] + @Discriminator [nvarchar](max), + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @Isbn [nvarchar](max), + @CoverPrice [decimal](18,2), + @IssueNumber [int], + @EditorId [int] +AS +BEGIN + INSERT INTO [Documents] ([Discriminator], [CoverArt], [NumberOfPages], [PublicationDate], [Title], [Isbn], [CoverPrice], [IssueNumber], [EditorId]) + OUTPUT INSERTED.[Id], INSERTED.[FirstRecordedOn], INSERTED.[RetrievedOn], INSERTED.[RowVersion] + VALUES (@Discriminator, @CoverArt, @NumberOfPages, @PublicationDate, @Title, @Isbn, @CoverPrice, @IssueNumber, @EditorId); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Document_Update] + @Id [int], + @RowVersion_Original [rowversion], + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @Isbn [nvarchar](max), + @CoverPrice [decimal](18,2), + @IssueNumber [int], + @EditorId [int], + @FirstRecordedOn [datetime2], + @RetrievedOn [datetime2] OUT, + @RowVersion [rowversion] OUT +AS +BEGIN + UPDATE [Documents] SET + [Title] = @Title, + [NumberOfPages] = @NumberOfPages, + [PublicationDate] = @PublicationDate, + [CoverArt] = @CoverArt, + [FirstRecordedOn] = @FirstRecordedOn, + [Isbn] = @Isbn, + [CoverPrice] = @CoverPrice, + [IssueNumber] = @IssueNumber, + [EditorId] = @EditorId, + @RetrievedOn = [RetrievedOn], + @RowVersion = [RowVersion] + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Document_Delete] + @Id [int], + @RowVersion_Original [rowversion] +AS +BEGIN + DELETE FROM [Documents] + OUTPUT 1 + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original; +END"); + break; + case MappingStrategy.Tpt: + // Document + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Document_Insert] + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max) +AS +BEGIN + INSERT INTO [Documents] ([CoverArt], [NumberOfPages], [PublicationDate], [Title]) + OUTPUT INSERTED.[Id], INSERTED.[FirstRecordedOn], INSERTED.[RetrievedOn], INSERTED.[RowVersion] + VALUES (@CoverArt, @NumberOfPages, @PublicationDate, @Title); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Document_Update] + @Id [int], + @RowVersion_Original [rowversion], + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @FirstRecordedOn [datetime2], + @RetrievedOn [datetime2] OUT, + @RowVersion [rowversion] OUT +AS +BEGIN + UPDATE [Documents] SET + [Title] = @Title, + [NumberOfPages] = @NumberOfPages, + [PublicationDate] = @PublicationDate, + [CoverArt] = @CoverArt, + [FirstRecordedOn] = @FirstRecordedOn, + @RetrievedOn = [RetrievedOn], + @RowVersion = [RowVersion] + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Document_Delete] + @Id [int], + @RowVersion_Original [rowversion] +AS +BEGIN + DELETE FROM [Documents] + OUTPUT 1 + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original; +END"); + + // Book + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Book_Insert] + @Id [int], + @Isbn [nvarchar](max) +AS +BEGIN + INSERT INTO [Books] ([Id], [Isbn]) + VALUES (@Id, @Isbn); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Book_Update] + @Id [int], + @Isbn [nvarchar](max) +AS +BEGIN + UPDATE [Books] SET + [Isbn] = @Isbn + WHERE [Id] = @Id + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Book_Delete] + @Id [int] +AS +BEGIN + DELETE FROM [Books] + OUTPUT 1 + WHERE [Id] = @Id; +END"); + + // Magazine + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Magazine_Insert] + @Id [int], + @CoverPrice [decimal](18,2), + @IssueNumber [int], + @EditorId [int] +AS +BEGIN + INSERT INTO [Magazines] ([Id], [CoverPrice], [IssueNumber], [EditorId]) + VALUES (@Id, @CoverPrice, @IssueNumber, @EditorId); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Magazine_Update] + @Id [int], + @CoverPrice [decimal](18,2), + @IssueNumber [int], + @EditorId [int] +AS +BEGIN + UPDATE [Magazines] SET + [CoverPrice] = @CoverPrice, + [IssueNumber] = @IssueNumber, + [EditorId] = @EditorId + WHERE [Id] = @Id + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Magazine_Delete] + @Id [int] +AS +BEGIN + DELETE FROM [Magazines] + OUTPUT 1 + WHERE [Id] = @Id; +END"); + break; + case MappingStrategy.Tpc: + // Book + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Book_Insert] + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @Isbn [nvarchar](max) +AS +BEGIN + INSERT INTO [Books] ([CoverArt], [Isbn], [NumberOfPages], [PublicationDate], [Title]) + OUTPUT INSERTED.[Id], INSERTED.[FirstRecordedOn], INSERTED.[RetrievedOn], INSERTED.[RowVersion] + VALUES (@CoverArt, @Isbn, @NumberOfPages, @PublicationDate, @Title); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Book_Update] + @Id [int], + @RowVersion_Original [rowversion], + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @FirstRecordedOn [datetime2], + @Isbn [nvarchar](max), + @RetrievedOn [datetime2] OUT, + @RowVersion [rowversion] OUT +AS +BEGIN + UPDATE [Books] SET + [Title] = @Title, + [NumberOfPages] = @NumberOfPages, + [PublicationDate] = @PublicationDate, + [CoverArt] = @CoverArt, + [FirstRecordedOn] = @FirstRecordedOn, + [Isbn] = @Isbn, + @RetrievedOn = [RetrievedOn], + @RowVersion = [RowVersion] + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Book_Delete] + @Id [int], + @RowVersion_Original [rowversion] +AS +BEGIN + DELETE FROM [Books] + OUTPUT 1 + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original; +END"); + + // Magazine + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Magazine_Insert] + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @CoverPrice [decimal](18,2), + @IssueNumber [int], + @EditorId [int] +AS +BEGIN + INSERT INTO [Magazines] ([CoverArt], [NumberOfPages], [PublicationDate], [Title], [CoverPrice], [IssueNumber], [EditorId]) + OUTPUT INSERTED.[Id], INSERTED.[FirstRecordedOn], INSERTED.[RetrievedOn], INSERTED.[RowVersion] + VALUES (@CoverArt, @NumberOfPages, @PublicationDate, @Title, @CoverPrice, @IssueNumber, @EditorId); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Magazine_Update] + @Id [int], + @RowVersion_Original [rowversion], + @Title [nvarchar](max), + @NumberOfPages [int], + @PublicationDate [datetime2], + @CoverArt [varbinary](max), + @CoverPrice [decimal](18,2), + @IssueNumber [int], + @EditorId [int], + @FirstRecordedOn [datetime2], + @RetrievedOn [datetime2] OUT, + @RowVersion [rowversion] OUT +AS +BEGIN + UPDATE [Magazines] SET + [Title] = @Title, + [NumberOfPages] = @NumberOfPages, + [PublicationDate] = @PublicationDate, + [CoverArt] = @CoverArt, + [FirstRecordedOn] = @FirstRecordedOn, + [CoverPrice] = @CoverPrice, + [IssueNumber] = @IssueNumber, + [EditorId] = @EditorId, + @RetrievedOn = [RetrievedOn], + @RowVersion = [RowVersion] + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Magazine_Delete] + @Id [int], + @RowVersion_Original [rowversion] +AS +BEGIN + DELETE FROM [Magazines] + OUTPUT 1 + WHERE [Id] = @Id AND [RowVersion] = @RowVersion_Original; +END"); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + // Person + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Person_Insert] + @Name [nvarchar](max) +AS +BEGIN + INSERT INTO [People] ([Name]) + OUTPUT INSERTED.[Id] + VALUES (@Name); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Person_Update] + @Id [int], + @Name_Original [nvarchar](max), + @Name [nvarchar](max) +AS +BEGIN + UPDATE [People] SET + [Name] = @Name + WHERE [Id] = @Id AND [Name] = @Name_Original + SELECT @@ROWCOUNT +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Person_Delete] + @Id [int], + @Name_Original [nvarchar](max) +AS +BEGIN + DELETE FROM [People] + OUTPUT 1 + WHERE [Id] = @Id AND [Name] = @Name_Original; +END"); + + // Contacts + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Contacts_Insert] + @PersonId [int], + @Phone [nvarchar](max) +AS +BEGIN + INSERT INTO [Contacts] ([PersonId], [Phone]) + VALUES (@PersonId, @Phone); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Contacts_Update] + @PersonId [int], + @Phone [nvarchar](max) +AS +BEGIN + UPDATE [Contacts] SET + [Phone] = @Phone + WHERE [PersonId] = @PersonId + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Contacts_Delete] + @PersonId [int] +AS +BEGIN + DELETE FROM [Contacts] + OUTPUT 1 + WHERE [PersonId] = @PersonId; +END"); + + // Addresses + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Addresses_Insert] + @ContactDetailsPersonId [int], + @Street [nvarchar](max), + @City [nvarchar](max), + @Postcode [nvarchar](max), + @Country [nvarchar](max) +AS +BEGIN + INSERT INTO [Addresses] ([ContactDetailsPersonId], [City], [Country], [Postcode], [Street]) + VALUES (@ContactDetailsPersonId, @City, @Country, @Postcode, @Street); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Addresses_Update] + @ContactDetailsPersonId [int], + @Street [nvarchar](max), + @City [nvarchar](max), + @Postcode [nvarchar](max), + @Country [nvarchar](max) +AS +BEGIN + UPDATE [Addresses] SET + [Street] = @Street, + [City] = @City, + [Postcode] = @Postcode, + [Country] = @Country + WHERE [ContactDetailsPersonId] = @ContactDetailsPersonId + SELECT @@ROWCOUNT; +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[Addresses_Delete] + @ContactDetailsPersonId [int] +AS +BEGIN + DELETE FROM [Addresses] + OUTPUT 1 + WHERE [ContactDetailsPersonId] = @ContactDetailsPersonId; +END"); + + // BookPerson join table + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[BookPerson_Insert] + @AuthorsId [int], + @PublishedWorksId [int] +AS +BEGIN + INSERT INTO [BookPerson] ([AuthorsId], [PublishedWorksId]) + VALUES (@AuthorsId, @PublishedWorksId); +END"); + + await context.Database.ExecuteSqlRawAsync( + @" +CREATE PROCEDURE [dbo].[BookPerson_Delete] + @AuthorsId [int], + @PublishedWorksId [int] +AS +BEGIN + DELETE FROM [BookPerson] + OUTPUT 1 + WHERE [AuthorsId] = @AuthorsId AND [PublishedWorksId] = @PublishedWorksId; +END"); + } +} diff --git a/samples/core/Miscellaneous/NewInEFCore7/ExecuteDeleteSample.cs b/samples/core/Miscellaneous/NewInEFCore7/ExecuteDeleteSample.cs index 3b4141440e..84e2b69477 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/ExecuteDeleteSample.cs +++ b/samples/core/Miscellaneous/NewInEFCore7/ExecuteDeleteSample.cs @@ -4,33 +4,25 @@ public static class ExecuteDeleteSample { public static Task ExecuteDelete() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteDelete)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteDeleteTest(); } public static Task ExecuteDeleteTpt() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteDelete)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteDeleteTest(); } public static Task ExecuteDeleteTpc() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteDelete)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteDeleteTest(); } public static Task ExecuteDeleteSqlite() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteDelete)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteDeleteTest(); } @@ -274,6 +266,12 @@ await context.Posts.Where(p => p.Author!.Posts.Count <= 1) $"Authors after delete: {string.Join(", ", await context.Authors.AsNoTracking().Select(e => "'" + e.Name + "'").ToListAsync())}"); Console.WriteLine(); } + + private static void PrintSampleName([CallerMemberName] string? methodName = null) + { + Console.WriteLine($">>>> Sample: {methodName}"); + Console.WriteLine(); + } } public class TphBlogsContext : BlogsContext diff --git a/samples/core/Miscellaneous/NewInEFCore7/ExecuteUpdateSample.cs b/samples/core/Miscellaneous/NewInEFCore7/ExecuteUpdateSample.cs index 68598a221a..a6d4e2cbe0 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/ExecuteUpdateSample.cs +++ b/samples/core/Miscellaneous/NewInEFCore7/ExecuteUpdateSample.cs @@ -4,33 +4,25 @@ public static class ExecuteUpdateSample { public static Task ExecuteUpdate() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteUpdate)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteUpdateTest(); } public static Task ExecuteUpdateTpt() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteUpdate)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteUpdateTest(); } public static Task ExecuteUpdateTpc() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteUpdate)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteUpdateTest(); } public static Task ExecuteUpdateSqlite() { - Console.WriteLine($">>>> Sample: {nameof(ExecuteUpdate)}"); - Console.WriteLine(); - + PrintSampleName(); return ExecuteUpdateTest(); } @@ -179,16 +171,22 @@ private static async Task ResetPostPublishedOnToDefault() $"Posts before update: {string.Join(", ", await context.Posts.AsNoTracking().Select(e => "'..." + e.Title.Substring(e.Title.Length - 12) + "' " + e.PublishedOn.Date).ToListAsync())}"); Console.WriteLine(); - context.LoggingEnabled = true; - await context.Set() - .ExecuteUpdateAsync( - setPropertyCalls => setPropertyCalls - .SetProperty(post => post.PublishedOn, post => EF.Default())); - context.LoggingEnabled = false; + // context.LoggingEnabled = true; + // await context.Set() + // .ExecuteUpdateAsync( + // setPropertyCalls => setPropertyCalls + // .SetProperty(post => post.PublishedOn, post => EF.Default())); + // context.LoggingEnabled = false; Console.WriteLine(); Console.WriteLine( $"Posts after update: {string.Join(", ", await context.Posts.AsNoTracking().Select(e => "'..." + e.Title.Substring(e.Title.Length - 12) + "' " + e.PublishedOn.Date).ToListAsync())}"); Console.WriteLine(); } + + private static void PrintSampleName([CallerMemberName] string? methodName = null) + { + Console.WriteLine($">>>> Sample: {methodName}"); + Console.WriteLine(); + } } diff --git a/samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs b/samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs index c0e62e2fd2..dbc2f133e1 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs +++ b/samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs @@ -6,17 +6,13 @@ public static class JsonColumnsSample { public static Task Json_columns_with_TPH() { - Console.WriteLine($">>>> Sample: {nameof(Json_columns_with_TPH)}"); - Console.WriteLine(); - + PrintSampleName(); return JsonColumnsTest(); } public static Task Json_columns_with_TPH_on_SQLite() { - Console.WriteLine($">>>> Sample: {nameof(Json_columns_with_TPH_on_SQLite)}"); - Console.WriteLine(); - + PrintSampleName(); return JsonColumnsTest(); } @@ -170,6 +166,12 @@ private static async Task JsonColumnsTest() await context.SaveChangesAsync(); } + + private static void PrintSampleName([CallerMemberName] string? methodName = null) + { + Console.WriteLine($">>>> Sample: {methodName}"); + Console.WriteLine(); + } } public abstract class JsonBlogsContextBase : BlogsContext diff --git a/samples/core/Miscellaneous/NewInEFCore7/ModelBuildingConventionsSample.cs b/samples/core/Miscellaneous/NewInEFCore7/ModelBuildingConventionsSample.cs index 10fcfa029f..2e336359e4 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/ModelBuildingConventionsSample.cs +++ b/samples/core/Miscellaneous/NewInEFCore7/ModelBuildingConventionsSample.cs @@ -1,13 +1,10 @@ using System.Net; -using System.Runtime.CompilerServices; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Conventions; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #pragma warning disable CS0169 diff --git a/samples/core/Miscellaneous/NewInEFCore7/NewInEFCore7.csproj b/samples/core/Miscellaneous/NewInEFCore7/NewInEFCore7.csproj index 00b3a745c7..fb8680bc07 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/NewInEFCore7.csproj +++ b/samples/core/Miscellaneous/NewInEFCore7/NewInEFCore7.csproj @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -22,6 +22,7 @@ + diff --git a/samples/core/Miscellaneous/NewInEFCore7/Program.cs b/samples/core/Miscellaneous/NewInEFCore7/Program.cs index 32282229d7..6f0772c3b2 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/Program.cs +++ b/samples/core/Miscellaneous/NewInEFCore7/Program.cs @@ -4,32 +4,36 @@ public class Program { public static async Task Main() { - await TpcInheritanceSample.Inheritance_with_TPH(); - await TpcInheritanceSample.Inheritance_with_TPT(); - await TpcInheritanceSample.Inheritance_with_TPC(); - await TpcInheritanceSample.Inheritance_with_TPC_using_HiLo(); - await TpcInheritanceSample.Inheritance_with_TPC_using_Identity(); + // await TpcInheritanceSample.Inheritance_with_TPH(); + // await TpcInheritanceSample.Inheritance_with_TPT(); + // await TpcInheritanceSample.Inheritance_with_TPC(); + // await TpcInheritanceSample.Inheritance_with_TPC_using_HiLo(); + // await TpcInheritanceSample.Inheritance_with_TPC_using_Identity(); + // + // await ExecuteDeleteSample.ExecuteDelete(); + // await ExecuteDeleteSample.ExecuteDeleteTpt(); + // await ExecuteDeleteSample.ExecuteDeleteTpc(); + // await ExecuteDeleteSample.ExecuteDeleteSqlite(); + // + // await ExecuteUpdateSample.ExecuteUpdate(); + // await ExecuteUpdateSample.ExecuteUpdateTpt(); + // await ExecuteUpdateSample.ExecuteUpdateTpc(); + // await ExecuteUpdateSample.ExecuteUpdateSqlite(); + // + // await JsonColumnsSample.Json_columns_with_TPH(); + // + // // Issue https://github.com/dotnet/efcore/issues/28816 (Json: add support for Sqlite provider) + // // await JsonColumnsSample.Json_columns_with_TPH_on_SQLite(); + // + // await ModelBuildingConventionsSample.No_foreign_key_index_convention(); + // await ModelBuildingConventionsSample.Discriminator_length_convention(); + // await ModelBuildingConventionsSample.Max_string_length_convention(); + // await ModelBuildingConventionsSample.Map_members_explicitly_by_attribute_convention(); + // await ModelBuildingConventionsSample.Custom_model_validation_convention(); + // await ModelBuildingConventionsSample.No_cascade_delete_convention(); - await ExecuteDeleteSample.ExecuteDelete(); - await ExecuteDeleteSample.ExecuteDeleteTpt(); - await ExecuteDeleteSample.ExecuteDeleteTpc(); - await ExecuteDeleteSample.ExecuteDeleteSqlite(); - - await ExecuteUpdateSample.ExecuteUpdate(); - await ExecuteUpdateSample.ExecuteUpdateTpt(); - await ExecuteUpdateSample.ExecuteUpdateTpc(); - await ExecuteUpdateSample.ExecuteUpdateSqlite(); - - await JsonColumnsSample.Json_columns_with_TPH(); - - // Issue https://github.com/dotnet/efcore/issues/28816 (Json: add support for Sqlite provider) - // await JsonColumnsSample.Json_columns_with_TPH_on_SQLite(); - - await ModelBuildingConventionsSample.No_foreign_key_index_convention(); - await ModelBuildingConventionsSample.Discriminator_length_convention(); - await ModelBuildingConventionsSample.Max_string_length_convention(); - await ModelBuildingConventionsSample.Map_members_explicitly_by_attribute_convention(); - await ModelBuildingConventionsSample.Custom_model_validation_convention(); - await ModelBuildingConventionsSample.No_cascade_delete_convention(); + await StoredProcedureMappingSample.Insert_Update_and_Delete_using_stored_procedures_with_TPH(); + await StoredProcedureMappingSample.Insert_Update_and_Delete_using_stored_procedures_with_TPT(); + await StoredProcedureMappingSample.Insert_Update_and_Delete_using_stored_procedures_with_TPC(); } } diff --git a/samples/core/Miscellaneous/NewInEFCore7/StoredProcedureMappingSample.cs b/samples/core/Miscellaneous/NewInEFCore7/StoredProcedureMappingSample.cs new file mode 100644 index 0000000000..91521d0f27 --- /dev/null +++ b/samples/core/Miscellaneous/NewInEFCore7/StoredProcedureMappingSample.cs @@ -0,0 +1,134 @@ +namespace NewInEfCore7; + +public static class StoredProcedureMappingSample +{ + public static Task Insert_Update_and_Delete_using_stored_procedures_with_TPH() + { + PrintSampleName(); + return SprocMappingTest(); + } + + public static Task Insert_Update_and_Delete_using_stored_procedures_with_TPT() + { + PrintSampleName(); + return SprocMappingTest(); + } + + public static Task Insert_Update_and_Delete_using_stored_procedures_with_TPC() + { + PrintSampleName(); + return SprocMappingTest(); + } + + private static async Task SprocMappingTest() + where TContext : DocumentsContext, new() + { + await using var context = new TContext(); + await context.Database.EnsureDeletedAsync(); + context.LoggingEnabled = true; + + Console.WriteLine("Creating database tables..."); + Console.WriteLine(); + + await context.Database.EnsureCreatedAsync(); + + Console.WriteLine(); + Console.WriteLine("Creating stored procedures..."); + Console.WriteLine(); + + await context.CreateStoredProcedures(); + + context.LoggingEnabled = true; + + Console.WriteLine(); + Console.WriteLine("Seeding the database..."); + Console.WriteLine(); + + await context.Seed(); + context.ChangeTracker.Clear(); + + Console.WriteLine(); + Console.WriteLine("Loading data..."); + Console.WriteLine(); + + await context.Documents + .Include(document => ((Book)document).Authors) + .Include(document => ((Magazine)document).Editor) + .LoadAsync(); + + Console.WriteLine(); + Console.WriteLine("Updating data..."); + Console.WriteLine(); + + context.RemoveRange(context.People.Local.Where(person => person.Contact.Address.City == "Chigley")); + context.RemoveRange(context.Magazines.Local.Where(magazine => magazine.Title.Contains("Amstrad"))); + context.RemoveRange(context.Books.Local.Where(book => book.NumberOfPages < 200)); + + foreach (var magazine in context.Magazines.Local) + { + magazine.CoverPrice += 1.0m; + } + + foreach (var book in context.Books.Local) + { + book.Title += " (New Edition!)"; + } + + foreach (var person in context.People.Local.Where(person => person.Contact.Address.Country == "UK")) + { + person.Name = "Dr. " + person.Name; + person.Contact.Phone = "+44 " + person.Contact.Phone!.Substring(1); + person.Contact.Address.Country = "United Kingdom"; + } + + await context.SaveChangesAsync(); + + Console.WriteLine(); + Console.WriteLine("Optimistic concurrency test 1..."); + Console.WriteLine(); + + try + { + await using var context2 = new TContext(); + (await context2.Books.SingleAsync(book => book.Title.StartsWith("Test"))).Isbn = "Mod1"; + + context.Books.Local.Single(book => book.Title.StartsWith("Test", StringComparison.Ordinal)).Isbn = null; + await context.SaveChangesAsync(); + + await context2.SaveChangesAsync(); + + } + catch (DbUpdateConcurrencyException exception) + { + Console.WriteLine($"Caught expected: " + exception.Message); + } + + Console.WriteLine(); + Console.WriteLine("Optimistic concurrency test 2..."); + Console.WriteLine(); + + try + { + await using var context2 = new TContext(); + (await context2.People.SingleAsync(person => person.Name.StartsWith("Dr. Kent"))).Name += ": Legend!"; + + context.People.Local.Single(person => person.Name.StartsWith("Dr. Kent", StringComparison.Ordinal)).Name += ": Hero!"; + await context.SaveChangesAsync(); + + await context2.SaveChangesAsync(); + + } + catch (DbUpdateConcurrencyException exception) + { + Console.WriteLine($"Caught expected: " + exception.Message); + } + + Console.WriteLine(); + } + + private static void PrintSampleName([CallerMemberName] string? methodName = null) + { + Console.WriteLine($">>>> Sample: {methodName}"); + Console.WriteLine(); + } +} diff --git a/samples/core/Miscellaneous/NewInEFCore7/TpcInheritanceSample.cs b/samples/core/Miscellaneous/NewInEFCore7/TpcInheritanceSample.cs index 0f9649307b..e1e9288d39 100644 --- a/samples/core/Miscellaneous/NewInEFCore7/TpcInheritanceSample.cs +++ b/samples/core/Miscellaneous/NewInEFCore7/TpcInheritanceSample.cs @@ -4,41 +4,31 @@ public static class TpcInheritanceSample { public static Task Inheritance_with_TPH() { - Console.WriteLine($">>>> Sample: {nameof(Inheritance_with_TPH)}"); - Console.WriteLine(); - + PrintSampleName(); return ManyToManyTest(); } public static Task Inheritance_with_TPT() { - Console.WriteLine($">>>> Sample: {nameof(Inheritance_with_TPT)}"); - Console.WriteLine(); - + PrintSampleName(); return ManyToManyTest(); } public static Task Inheritance_with_TPC() { - Console.WriteLine($">>>> Sample: {nameof(Inheritance_with_TPC)}"); - Console.WriteLine(); - + PrintSampleName(); return ManyToManyTest(); } public static Task Inheritance_with_TPC_using_HiLo() { - Console.WriteLine($">>>> Sample: {nameof(Inheritance_with_TPC_using_HiLo)}"); - Console.WriteLine(); - + PrintSampleName(); return ManyToManyTest(); } public static Task Inheritance_with_TPC_using_Identity() { - Console.WriteLine($">>>> Sample: {nameof(Inheritance_with_TPC_using_Identity)}"); - Console.WriteLine(); - + PrintSampleName(); return ManyToManyTest(); } @@ -434,4 +424,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) l => l.HasOne().WithMany().OnDelete(DeleteBehavior.ClientCascade)); } } + + private static void PrintSampleName([CallerMemberName] string? methodName = null) + { + Console.WriteLine($">>>> Sample: {methodName}"); + Console.WriteLine(); + } }