Skip to content

Commit

Permalink
Handle updating complex type properties in ExecuteUpdate
Browse files Browse the repository at this point in the history
Closes #32058
  • Loading branch information
roji committed Dec 4, 2023
1 parent 5ce2e99 commit 13bb7d2
Show file tree
Hide file tree
Showing 9 changed files with 669 additions and 163 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,121 @@ public virtual Task Update_projected_complex_type_via_OrderBy_Skip_throws(bool a
s => s.SetProperty(c => c.ZipCode, 12345),
rowsAffectedCount: 3));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_complex_type_to_parameter(bool async)
{
var newAddress = new Address
{
AddressLine1 = "New AddressLine1",
AddressLine2 = "New AddressLine2",
ZipCode = 99999,
Country = new()
{
Code = "FR",
FullName = "France"
},
Tags = new List<string> { "new_tag1", "new_tag2" }
};

return AssertUpdate(
async,
ss => ss.Set<Customer>(),
c => c,
s => s.SetProperty(x => x.ShippingAddress, newAddress),
rowsAffectedCount: 3);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_nested_complex_type_to_parameter(bool async)
{
var newCountry = new Country
{
Code = "FR",
FullName = "France"
};

return AssertUpdate(
async,
ss => ss.Set<Customer>(),
c => c,
s => s.SetProperty(x => x.ShippingAddress.Country, newCountry),
rowsAffectedCount: 3);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_complex_type_to_another_database_complex_type(bool async)
=> AssertUpdate(
async,
ss => ss.Set<Customer>(),
c => c,
s => s.SetProperty(x => x.ShippingAddress, x => x.BillingAddress),
rowsAffectedCount: 3);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_complex_type_to_inline_without_lambda(bool async)
=> AssertUpdate(
async,
ss => ss.Set<Customer>(),
c => c,
s => s.SetProperty(x => x.ShippingAddress, new Address
{
AddressLine1 = "New AddressLine1",
AddressLine2 = "New AddressLine2",
ZipCode = 99999,
Country = new()
{
Code = "FR",
FullName = "France"
},
Tags = new List<string> { "new_tag1", "new_tag2" }
}),
rowsAffectedCount: 3);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_complex_type_to_inline_with_lambda(bool async)
=> AssertUpdate(
async,
ss => ss.Set<Customer>(),
c => c,
s => s.SetProperty(x => x.ShippingAddress, x => new Address
{
AddressLine1 = "New AddressLine1",
AddressLine2 = "New AddressLine2",
ZipCode = 99999,
Country = new()
{
Code = "FR",
FullName = "France"
},
Tags = new List<string> { "new_tag1", "new_tag2" }
}),
rowsAffectedCount: 3);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_complex_type_to_another_database_complex_type_with_subquery(bool async)
=> AssertUpdate(
async,
ss => ss.Set<Customer>().OrderBy(c => c.Id).Skip(1),
c => c,
s => s.SetProperty(x => x.ShippingAddress, x => x.BillingAddress),
rowsAffectedCount: 2);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_collection_inside_complex_type(bool async)
=> AssertUpdate(
async,
ss => ss.Set<Customer>(),
c => c,
s => s.SetProperty(x => x.ShippingAddress.Tags, new List<string> { "new_tag1", "new_tag2" }),
rowsAffectedCount: 3);

private void ClearLog()
=> Fixture.TestSqlLoggerFactory.Clear();
}
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ void RewriteSourceWithNewBaseline(string fileName, int lineNumber)
indentBuilder.Append(" ");
var indent = indentBuilder.ToString();
var newBaseLine = $@"Assert{(forUpdate ? "ExecuteUpdate" : "")}Sql(
{string.Join("," + Environment.NewLine + indent + "//" + Environment.NewLine, SqlStatements.Skip(offset).Take(count).Select(sql => "\"\"\"" + Environment.NewLine + sql + Environment.NewLine + "\"\"\""))})";
{string.Join("," + Environment.NewLine + indent + "//" + Environment.NewLine, SqlStatements.Skip(offset).Take(count).Select(sql => indent + "\"\"\"" + Environment.NewLine + sql + Environment.NewLine + "\"\"\""))})";
var numNewlinesInRewritten = newBaseLine.Count(c => c is '\n' or '\r');

writer.Write(newBaseLine);
Expand Down
34 changes: 28 additions & 6 deletions test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,15 @@ public virtual Task Complex_type_equals_constant(bool async)
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
Country = new Country { FullName = "United States", Code = "US" }
}));
Country = new Country { FullName = "United States", Code = "US" },
Tags = new List<string> { "foo", "bar" }
}),
ss => ss.Set<Customer>().Where(
c =>
c.ShippingAddress.AddressLine1 == "804 S. Lakeshore Road"
&& c.ShippingAddress.ZipCode == 38654
&& c.ShippingAddress.Country == new Country { FullName = "United States", Code = "US" }
&& c.ShippingAddress.Tags.SequenceEqual(new List<string> { "foo", "bar" })));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
Expand All @@ -154,12 +161,19 @@ public virtual Task Complex_type_equals_parameter(bool async)
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
Country = new Country { FullName = "United States", Code = "US" }
Country = new Country { FullName = "United States", Code = "US" },
Tags = new List<string> { "foo", "bar" }
};

return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.ShippingAddress == address));
ss => ss.Set<Customer>().Where(c => c.ShippingAddress == address),
ss => ss.Set<Customer>().Where(
c =>
c.ShippingAddress.AddressLine1 == "804 S. Lakeshore Road"
&& c.ShippingAddress.ZipCode == 38654
&& c.ShippingAddress.Country == new Country { FullName = "United States", Code = "US" }
&& c.ShippingAddress.Tags.SequenceEqual(new List<string> { "foo", "bar" })));
}

[ConditionalTheory]
Expand Down Expand Up @@ -194,13 +208,21 @@ public virtual Task Contains_over_complex_type(bool async)
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
Country = new Country { FullName = "United States", Code = "US" }
Country = new Country { FullName = "United States", Code = "US" },
Tags = new List<string> { "foo", "bar" }
};

return AssertQuery(
async,
ss => ss.Set<Customer>().Where(
c => ss.Set<Customer>().Select(c => c.ShippingAddress).Contains(address)));
c => ss.Set<Customer>().Select(c => c.ShippingAddress).Contains(address)),
ss => ss.Set<Customer>().Where(
c => ss.Set<Customer>().Select(c => c.ShippingAddress).Any(
a =>
a.AddressLine1 == "804 S. Lakeshore Road"
&& a.ZipCode == 38654
&& a.Country == new Country { FullName = "United States", Code = "US" }
&& a.Tags.SequenceEqual(new List<string> { "foo", "bar" }))));
}

[ConditionalTheory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ private static IReadOnlyList<Customer> CreateCustomers()
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
Country = new Country { FullName = "United States", Code = "US" }
Country = new Country { FullName = "United States", Code = "US" },
Tags = new List<string> { "foo", "bar" }
};

var customer1 = new Customer
Expand All @@ -71,21 +72,24 @@ private static IReadOnlyList<Customer> CreateCustomers()
{
AddressLine1 = "72 Hickory Rd.",
ZipCode = 07728,
Country = new Country { FullName = "Germany", Code = "DE" }
Country = new Country { FullName = "Germany", Code = "DE" },
Tags = new List<string> { "baz" }
},
BillingAddress = new Address
{
AddressLine1 = "79 Main St.",
ZipCode = 29293,
Country = new Country { FullName = "Germany", Code = "DE" }
Country = new Country { FullName = "Germany", Code = "DE" },
Tags = new List<string> { "a1", "a2", "a3" }
}
};

var address3 = new Address
{
AddressLine1 = "79 Main St.",
ZipCode = 29293,
Country = new Country { FullName = "Germany", Code = "DE" }
Country = new Country { FullName = "Germany", Code = "DE" },
Tags = new List<string> { "foo", "moo" }
};

var customer3 = new Customer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public record Address
public required string AddressLine1 { get; set; }
public string? AddressLine2 { get; set; }
public int ZipCode { get; set; }
public List<string> Tags { get; set; } = new();

public required Country Country { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,134 @@ public override async Task Update_projected_complex_type_via_OrderBy_Skip_throws
AssertExecuteUpdateSql();
}

public override async Task Update_complex_type_to_parameter(bool async)
{
await base.Update_complex_type_to_parameter(async);

AssertExecuteUpdateSql(
"""
@__complex_type_newAddress_0_AddressLine1='New AddressLine1' (Size = 4000)
@__complex_type_newAddress_0_AddressLine2='New AddressLine2' (Size = 4000)
@__complex_type_newAddress_0_Tags='["new_tag1","new_tag2"]' (Size = 4000)
@__complex_type_newAddress_0_ZipCode='99999' (Nullable = true)
@__complex_type_newAddress_0_Code='FR' (Size = 4000)
@__complex_type_newAddress_0_FullName='France' (Size = 4000)
UPDATE [c]
SET [c].[ShippingAddress_AddressLine1] = @__complex_type_newAddress_0_AddressLine1,
[c].[ShippingAddress_AddressLine2] = @__complex_type_newAddress_0_AddressLine2,
[c].[ShippingAddress_Tags] = @__complex_type_newAddress_0_Tags,
[c].[ShippingAddress_ZipCode] = @__complex_type_newAddress_0_ZipCode,
[c].[ShippingAddress_Country_Code] = @__complex_type_newAddress_0_Code,
[c].[ShippingAddress_Country_FullName] = @__complex_type_newAddress_0_FullName
FROM [Customer] AS [c]
""");
}

public override async Task Update_nested_complex_type_to_parameter(bool async)
{
await base.Update_nested_complex_type_to_parameter(async);

AssertExecuteUpdateSql(
"""
@__complex_type_newCountry_0_Code='FR' (Size = 4000)
@__complex_type_newCountry_0_FullName='France' (Size = 4000)
UPDATE [c]
SET [c].[ShippingAddress_Country_Code] = @__complex_type_newCountry_0_Code,
[c].[ShippingAddress_Country_FullName] = @__complex_type_newCountry_0_FullName
FROM [Customer] AS [c]
""");
}

public override async Task Update_complex_type_to_another_database_complex_type(bool async)
{
await base.Update_complex_type_to_another_database_complex_type(async);

AssertExecuteUpdateSql(
"""
UPDATE [c]
SET [c].[ShippingAddress_AddressLine1] = [c].[BillingAddress_AddressLine1],
[c].[ShippingAddress_AddressLine2] = [c].[BillingAddress_AddressLine2],
[c].[ShippingAddress_Tags] = [c].[BillingAddress_Tags],
[c].[ShippingAddress_ZipCode] = [c].[BillingAddress_ZipCode],
[c].[ShippingAddress_Country_Code] = [c].[ShippingAddress_Country_Code],
[c].[ShippingAddress_Country_FullName] = [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
""");
}

public override async Task Update_complex_type_to_inline_without_lambda(bool async)
{
await base.Update_complex_type_to_inline_without_lambda(async);

AssertExecuteUpdateSql(
"""
UPDATE [c]
SET [c].[ShippingAddress_AddressLine1] = N'New AddressLine1',
[c].[ShippingAddress_AddressLine2] = N'New AddressLine2',
[c].[ShippingAddress_Tags] = N'["new_tag1","new_tag2"]',
[c].[ShippingAddress_ZipCode] = 99999,
[c].[ShippingAddress_Country_Code] = N'FR',
[c].[ShippingAddress_Country_FullName] = N'France'
FROM [Customer] AS [c]
""");
}

public override async Task Update_complex_type_to_inline_with_lambda(bool async)
{
await base.Update_complex_type_to_inline_with_lambda(async);

AssertExecuteUpdateSql(
"""
UPDATE [c]
SET [c].[ShippingAddress_AddressLine1] = N'New AddressLine1',
[c].[ShippingAddress_AddressLine2] = N'New AddressLine2',
[c].[ShippingAddress_Tags] = N'["new_tag1","new_tag2"]',
[c].[ShippingAddress_ZipCode] = 99999,
[c].[ShippingAddress_Country_Code] = N'FR',
[c].[ShippingAddress_Country_FullName] = N'France'
FROM [Customer] AS [c]
""");
}

public override async Task Update_complex_type_to_another_database_complex_type_with_subquery(bool async)
{
await base.Update_complex_type_to_another_database_complex_type_with_subquery(async);

AssertExecuteUpdateSql(
"""
@__p_0='1'
UPDATE [c]
SET [c].[ShippingAddress_AddressLine1] = [t].[BillingAddress_AddressLine1],
[c].[ShippingAddress_AddressLine2] = [t].[BillingAddress_AddressLine2],
[c].[ShippingAddress_Tags] = [t].[BillingAddress_Tags],
[c].[ShippingAddress_ZipCode] = [t].[BillingAddress_ZipCode],
[c].[ShippingAddress_Country_Code] = [t].[ShippingAddress_Country_Code],
[c].[ShippingAddress_Country_FullName] = [t].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
INNER JOIN (
SELECT [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c0]
ORDER BY [c0].[Id]
OFFSET @__p_0 ROWS
) AS [t] ON [c].[Id] = [t].[Id]
""");
}

public override async Task Update_collection_inside_complex_type(bool async)
{
await base.Update_collection_inside_complex_type(async);

AssertExecuteUpdateSql(
"""
UPDATE [c]
SET [c].[ShippingAddress_Tags] = N'["new_tag1","new_tag2"]'
FROM [Customer] AS [c]
""");
}

[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
Expand Down
Loading

0 comments on commit 13bb7d2

Please sign in to comment.