|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 |
|
| 4 | +using System.ComponentModel; |
4 | 5 | using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; |
5 | 6 |
|
6 | 7 | namespace Microsoft.EntityFrameworkCore; |
@@ -2053,10 +2054,11 @@ protected static EntityEntry<TEntity> TrackFromQuery<TEntity>(DbContext context, |
2053 | 2054 | protected virtual Task ExecuteWithStrategyInTransactionAsync( |
2054 | 2055 | Func<DbContext, Task> testOperation, |
2055 | 2056 | Func<DbContext, Task>? nestedTestOperation1 = null, |
2056 | | - Func<DbContext, Task>? nestedTestOperation2 = null) |
| 2057 | + Func<DbContext, Task>? nestedTestOperation2 = null, |
| 2058 | + Func<DbContext, Task>? nestedTestOperation3 = null) |
2057 | 2059 | => TestHelpers.ExecuteWithStrategyInTransactionAsync( |
2058 | 2060 | CreateContext, UseTransaction, |
2059 | | - testOperation, nestedTestOperation1, nestedTestOperation2); |
| 2061 | + testOperation, nestedTestOperation1, nestedTestOperation2, nestedTestOperation3); |
2060 | 2062 |
|
2061 | 2063 | protected virtual void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) |
2062 | 2064 | { |
@@ -2397,6 +2399,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con |
2397 | 2399 | }); |
2398 | 2400 | }); |
2399 | 2401 | }); |
| 2402 | + |
| 2403 | + modelBuilder.Entity<EntityWithOptionalMultiPropComplex>(b => |
| 2404 | + { |
| 2405 | + b.ComplexProperty(e => e.ComplexProp); |
| 2406 | + }); |
2400 | 2407 | } |
2401 | 2408 | } |
2402 | 2409 |
|
@@ -4373,4 +4380,72 @@ protected static FieldPubWithReadonlyStructCollections CreateFieldCollectionPubW |
4373 | 4380 | ], |
4374 | 4381 | FeaturedTeam = new TeamReadonlyStruct("Not In This Lifetime", ["Slash", "Axl"]) |
4375 | 4382 | }; |
| 4383 | + |
| 4384 | + [ConditionalTheory(), InlineData(false), InlineData(true)] |
| 4385 | + public virtual async Task Can_save_default_values_in_optional_complex_property_with_multiple_properties(bool async) |
| 4386 | + { |
| 4387 | + await ExecuteWithStrategyInTransactionAsync( |
| 4388 | + async context => |
| 4389 | + { |
| 4390 | + var entity = Fixture.UseProxies |
| 4391 | + ? context.CreateProxy<EntityWithOptionalMultiPropComplex>() |
| 4392 | + : new EntityWithOptionalMultiPropComplex(); |
| 4393 | + |
| 4394 | + entity.Id = Guid.NewGuid(); |
| 4395 | + entity.ComplexProp = null; |
| 4396 | + |
| 4397 | + _ = async ? await context.AddAsync(entity) : context.Add(entity); |
| 4398 | + _ = async ? await context.SaveChangesAsync() : context.SaveChanges(); |
| 4399 | + |
| 4400 | + Assert.Null(entity.ComplexProp); |
| 4401 | + }, |
| 4402 | + async context => |
| 4403 | + { |
| 4404 | + var entity = async |
| 4405 | + ? await context.Set<EntityWithOptionalMultiPropComplex>().SingleAsync() |
| 4406 | + : context.Set<EntityWithOptionalMultiPropComplex>().Single(); |
| 4407 | + |
| 4408 | + Assert.Null(entity.ComplexProp); |
| 4409 | + |
| 4410 | + // Set the complex property with default values |
| 4411 | + entity.ComplexProp = new MultiPropComplex |
| 4412 | + { |
| 4413 | + IntValue = 0, |
| 4414 | + BoolValue = false, |
| 4415 | + DateValue = default |
| 4416 | + }; |
| 4417 | + |
| 4418 | + _ = async ? await context.SaveChangesAsync() : context.SaveChanges(); |
| 4419 | + |
| 4420 | + Assert.NotNull(entity.ComplexProp); |
| 4421 | + Assert.Equal(0, entity.ComplexProp.IntValue); |
| 4422 | + Assert.False(entity.ComplexProp.BoolValue); |
| 4423 | + Assert.Equal(default, entity.ComplexProp.DateValue); |
| 4424 | + }, |
| 4425 | + async context => |
| 4426 | + { |
| 4427 | + var entity = async |
| 4428 | + ? await context.Set<EntityWithOptionalMultiPropComplex>().SingleAsync() |
| 4429 | + : context.Set<EntityWithOptionalMultiPropComplex>().Single(); |
| 4430 | + |
| 4431 | + // Complex types with more than one property should materialize even with default values |
| 4432 | + Assert.NotNull(entity.ComplexProp); |
| 4433 | + Assert.Equal(0, entity.ComplexProp.IntValue); |
| 4434 | + Assert.False(entity.ComplexProp.BoolValue); |
| 4435 | + Assert.Equal(default, entity.ComplexProp.DateValue); |
| 4436 | + }); |
| 4437 | + } |
| 4438 | + |
| 4439 | + public class EntityWithOptionalMultiPropComplex |
| 4440 | + { |
| 4441 | + public virtual Guid Id { get; set; } |
| 4442 | + public virtual MultiPropComplex? ComplexProp { get; set; } |
| 4443 | + } |
| 4444 | + |
| 4445 | + public class MultiPropComplex |
| 4446 | + { |
| 4447 | + public int IntValue { get; set; } |
| 4448 | + public bool BoolValue { get; set; } |
| 4449 | + public DateTimeOffset DateValue { get; set; } |
| 4450 | + } |
4376 | 4451 | } |
0 commit comments