public void OwnedTypesDoNotNeedToBeIncluded() { var newBillingAddress = new Address { City = "Krakow", ZipPostCode = "04-218" }; var newDeliveryAddress = new Address { City = "Warsaw", ZipPostCode = "00-001" }; var newOrderInfo = new OrderInfo { BillingAddress = newBillingAddress, DeliveryAddress = newDeliveryAddress, OrderNumber = "#1" }; var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); var dbCtxOptions = dbConnectionString .AsSqlConnectionString <ShopContext>() .EnsureDb(); using (var context = dbCtxOptions.BuildDbContext().StartLogging(_testOutput.AsLineWriter())) { context.Add(newOrderInfo); context.SaveChanges(); } using (var context = dbCtxOptions.BuildDbContext().StartLogging(_testOutput.AsLineWriter())) { var oi = context .OrderInfos .Single(); oi.BillingAddress.Should().NotBeNull("because it's an owned type"); oi.DeliveryAddress.Should().NotBeNull("because it's also an owned type"); } }
public void CreatesOneToManyRelationship() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <ManufacturerContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var newUsbs = new[] { new UsbSlot { UsbVersion = "3.0" }, new UsbSlot { UsbVersion = "2.0" } }; var newNotebook = new Notebook { Name = "Pear", Usbs = newUsbs }; context.Add(newNotebook); context.SaveChanges(); context .Notebooks .Include(n => n.Usbs) .SingleOrDefault() .Should().NotBeNull("because has been successfully added"); }
public void CreatesOptionTwoOneToOneRelationship() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <ConventionContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var newAttendee = new Attendee { Name = "John Smith" }; // Attendee can exist without Ticket. context.Add(newAttendee); // var newAttendee is now being tracked. context.SaveChanges(); newAttendee.Ticket.Should().BeNull($"because it has not been provided and {nameof(Attendee)} can exist without {nameof(Ticket)}"); newAttendee.Ticket = new Ticket { Type = "normal" }; // Properties Attendee, AttendeeId and TicketId are handled by EF Core. context.SaveChanges(); var nameTicketTypePair = context .Attendees .Include(a => a.Ticket) // Now, Include() handles joining in the DB. .Select(a => new { a.Name, a.Ticket.Type }) // Much simpler than the one in OptionOne. .First(); nameTicketTypePair.Should().BeEquivalentTo(new { Name = "John Smith", Type = "normal" }, "because that's the expected output" + " of the above query"); }
public void CreatesManyToManyRelationshipByConvention() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <BookStoreContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var aFreemanAuthor = new Author { Name = "Adam Freeman" }; var eEvansAuthor = new Author { Name = "Eric Evans" }; var aspBook = new Book { Title = "...ASP.NET CORE..." }; var dddBook = new Book { Title = "...DDD..." }; context .Invoking(ctx => { ctx.AddRange(new[] { aFreemanAuthor, eEvansAuthor }); ctx.AddRange(new[] { aspBook, dddBook }); ctx.SaveChanges(); }) .Should().NotThrow("because it's actually the 'zero-or-one-or-many' relationship."); context.Entry(aspBook).State.Should().Be(EntityState.Unchanged, "because it has already been saved and" + " has not been modified afterwards"); aspBook.AuthorLinks.Should().BeNull("because tracking doesn't automatically loads navigation properties"); context.Entry(aspBook).Collection(a => a.AuthorLinks).Load(); // Load navigation property explicitly. context.Entry(aspBook).State.Should().Be(EntityState.Unchanged, "because it's still unchanged - just its property has been loaded"); aspBook.AuthorLinks.Should().NotBeNull("because already loaded"); aspBook.AuthorLinks.Count().Should().Be(0, "because no links have been specified"); aspBook.AuthorLinks = aspBook.AuthorLinks.Append(new BookAuthor { Author = aFreemanAuthor }).ToList(); context.Invoking(ctx => ctx.SaveChanges()).Should().NotThrow("because just Author must be specified in BookAuthor - the rest is" + " handled by EF Core"); aspBook.AuthorLinks = aspBook.AuthorLinks.Append(new BookAuthor { Author = eEvansAuthor }).ToList(); dddBook.AuthorLinks = new List <BookAuthor> { new BookAuthor { Author = eEvansAuthor }, new BookAuthor { Author = aFreemanAuthor } }; context.SaveChanges(); context.Set <BookAuthor>().Count().Should().Be(4, "because 2 authors * 2 books"); }
public void TablePerHierarchySupportsInheritance() { var cardPayment = new PaymentCard { Amount = 2000M, Receipt = "ęśąćż" }; var cashPayment = new PaymentCash { Amount = 100M }; var notebookSoldIt = new SoldIt { WhatSold = "Notebook", Payment = cardPayment }; var keyboardSoldIt = new SoldIt { WhatSold = "Keyboard", Payment = cashPayment }; var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); var dbCtxOptions = dbConnectionString .AsSqlConnectionString <ShippingContext>() .EnsureDb(); using (var context = dbCtxOptions.BuildDbContext().StartLogging(_testOutput.AsLineWriter())) { context.AddRange(new[] { notebookSoldIt, keyboardSoldIt }); context.SaveChanges(); } using (var context = dbCtxOptions.BuildDbContext().StartLogging(_testOutput.AsLineWriter())) { var notebookPayment = context .SoldIts .Where(si => si.WhatSold == "Notebook") .Select(si => si.Payment) .Single(); var keyboardPayment = context .SoldIts .Where(si => si.WhatSold == "Keyboard") .Select(si => si.Payment) .Single(); notebookPayment.Should().BeOfType <PaymentCard>("because the original type has been preserved"); notebookPayment.Type.Should().Be("card", $"because that's a discriminator value indicating {nameof(PaymentCard)} type"); keyboardPayment.Should().BeOfType <PaymentCash>("because the original type has been preserved"); keyboardPayment.Type.Should().Be("cash", $"because that's a discriminator value indicating {nameof(PaymentCash)} type"); context .Set <Payment>() .OfType <PaymentCard>() .First().Receipt .Should() .Be("ęśąćż", "because that's another way to handle a hierarchical table"); } }
public QueryingTheDbFixture(ITestOutputHelper testOutput) { _testOutput = testOutput; _dbConnectionString = DbConnectionString.Create(GetType().Name); _fact = DbContextFactoryManager <BookStoreContext> .Instance.GetDbContextFactory(nameof(QueryingTheDbFixture)); _fact.RegisterOnInit(dbCtxOpts => { dbCtxOpts.SeedWith(@"TestData\RawTestData1.json"); }); _fact.InitDb(_dbConnectionString); }
public ChangingTheDbContent(ITestOutputHelper testOutput) { _testOutput = testOutput; _fact = DbContextFactoryManager <BookStoreContext> .Instance.GetDbContextFactory(nameof(ChangingTheDbContent)); _fact.RegisterOnInit(dbCtxOpts => { dbCtxOpts.SeedWith(@"TestData\RawTestData1.json"); }); var dbConnectionStrings = ListFactMethodNames().Select(str => DbConnectionString.Create(GetType().Name, str)); _fact.InitDbAsync(dbConnectionStrings).Wait(); }
public void TableIsSplitIntoTwoEntities() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <BookStoreContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var newDetail = new BookDetail { Price = 49M }; var newSummary = new BookSummary { Title = "Entity Framework Core IN ACTION", Details = newDetail }; context.Add(newSummary); context.SaveChanges(); context .BookSummaries .Single() .Title .Should().Contain("Entity Framework"); var aloneSummary = new BookSummary { Title = "C# 8.0 in a Nutshell" }; context.Add(aloneSummary); context.Invoking(ctx => ctx.SaveChanges()).Should().NotThrow <Exception>("because not all parts of a split table must be saved at once"); context.Invoking(ctx => { ctx .BookSummaries .Include(bs => bs.Details) .Where(bs => bs.Title.ToLower().Contains("c#")) .Select(bs => bs.Details.Price) .Single(); }).Should().NotThrow("because EF Core returns the default values in this case"); }
public void CreatesOptionThreeOneToOneRelationship() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <ConventionContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var newAttendee = new Attendee { Name = "John Smith" }; // Attendee can exist without Ticket. context.Add(newAttendee); // var newAttendee is now being tracked. context.SaveChanges(); newAttendee.Ticket.Should().BeNull($"because it has not been provided and {nameof(Attendee)} can exist without {nameof(Ticket)}"); var newAttendeeEntry = context.Entry(newAttendee); newAttendeeEntry .Invoking(ae => ae.Property($"{nameof(Attendee.Ticket)}{nameof(Ticket.TicketId)}").CurrentValue) .Should() .Throw <Exception>("because there's no need for a shadow property to determine the relationship by EF Core"); newAttendee.Ticket = new Ticket { Type = "normal" }; // Properties Attendee, AttendeeId and TicketId are handled by EF Core. context.SaveChanges(); var nameTicketTypePair = context .Attendees .Include(a => a.Ticket) .Select(a => new { a.Name, a.Ticket.Type }) .First(); nameTicketTypePair.Should().BeEquivalentTo(new { Name = "John Smith", Type = "normal" }, "because that's the expected output" + " of the above query"); }
public void DependentEntityContainsPrincipalEntityIdAsTheShadowProperty() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringNonrelationalProperties), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <PrincipalDependentContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); Func <object> func = () => context .Entry(new Dependent()) .Property($"{nameof(Dependent.NavigationProp)}{nameof(Principal.PrincipalId)}") .CurrentValue; FluentActions .Invoking(func) .Should() .NotThrow($"because EF Core should have created the shadow property representing {nameof(Principal.PrincipalId)} " + $"to be able to link {nameof(Dependent)} to {nameof(Principal)}"); }
public void CreatesRelationshipUsingAlternateUniqueKey() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <ContactBookContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var newContact = new ContactInfo { MobileNumber = "1234567890" }; var newPerson = new Person { Name = "John Smith", UserId = "*****@*****.**", Contact = newContact }; context.Add(newPerson); context.SaveChanges(); newContact.EmailAddress.Should().Be(newPerson.UserId, "because it's the foreign key linking to Person by its UserId" + " which has already been set by EF Core on SaveChanges()"); }
public void CreatesOptionOneOneToOneRelationship() { var dbConnectionString = DbConnectionString.Create(nameof(ConfiguringRelationships), GetCallerName()); using var context = dbConnectionString .AsSqlConnectionString <ConventionContext>() .EnsureDb() .BuildDbContext() .StartLogging(_testOutput.AsLineWriter()); var newTicket = new Ticket { Type = "normal" }; context.Add(newTicket); context.SaveChanges(); newTicket.TicketId.Should().NotBe(0, "because new id has been assigned by the DB"); var newAttendee = new Attendee { Name = "John Smith", ConventionTicketId = newTicket.TicketId }; context.Add(newAttendee); context.SaveChanges(); // Inner join as an equivalent of EF Core Include() for an entity with no navigation property. var nameTicketTypePair = context .Attendees .Join(context.Set <Ticket>(), a => a.ConventionTicketId, t => t.TicketId, (a, t) => new { a.Name, t.Type }) .First(); nameTicketTypePair.Should().BeEquivalentTo(new { Name = "John Smith", Type = "normal" }, "because that's the expected output" + " of the above query"); }
private BookStoreContext CreateBookStoreContext() { var dbConnectionString = DbConnectionString.Create(GetType().Name, GetCallerName(1)); return(_fact.Create(dbConnectionString, _testOutput.AsLineWriter())); }