|
1 | 1 | // Copyright (c) .NET Foundation. All rights reserved. |
2 | 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. |
3 | 3 |
|
| 4 | +using System; |
| 5 | +using System.Collections.Generic; |
4 | 6 | using System.Linq; |
| 7 | +using Microsoft.EntityFrameworkCore.Design; |
| 8 | +using Microsoft.EntityFrameworkCore.Infrastructure; |
5 | 9 | using Microsoft.EntityFrameworkCore.Internal; |
| 10 | +using Microsoft.EntityFrameworkCore.Metadata; |
| 11 | +using Microsoft.EntityFrameworkCore.SqlServer.Design.Internal; |
| 12 | +using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal; |
| 13 | +using Microsoft.Extensions.DependencyInjection; |
| 14 | +using Microsoft.Extensions.DependencyInjection.Extensions; |
6 | 15 | using Xunit; |
7 | 16 |
|
8 | 17 | namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal |
@@ -1402,5 +1411,277 @@ public partial class Post |
1402 | 1411 | Assert.Equal("OriginalPosts", originalInverseNavigation.Name); |
1403 | 1412 | }); |
1404 | 1413 | } |
| 1414 | + |
| 1415 | + [ConditionalFact] |
| 1416 | + public void Entity_with_custom_annotation() |
| 1417 | + { |
| 1418 | + Test( |
| 1419 | + modelBuilder => modelBuilder |
| 1420 | + .Entity( |
| 1421 | + "EntityWithAnnotation", |
| 1422 | + x => |
| 1423 | + { |
| 1424 | + x.HasAnnotation("Custom:EntityAnnotation", "first argument"); |
| 1425 | + x.Property<int>("Id"); |
| 1426 | + x.HasKey("Id"); |
| 1427 | + }), |
| 1428 | + new ModelCodeGenerationOptions { UseDataAnnotations = true }, |
| 1429 | + code => |
| 1430 | + { |
| 1431 | + AssertFileContents( |
| 1432 | + @"using System; |
| 1433 | +using System.Collections.Generic; |
| 1434 | +using System.ComponentModel.DataAnnotations; |
| 1435 | +using System.ComponentModel.DataAnnotations.Schema; |
| 1436 | +using Microsoft.EntityFrameworkCore; |
| 1437 | +
|
| 1438 | +#nullable disable |
| 1439 | +
|
| 1440 | +namespace TestNamespace |
| 1441 | +{ |
| 1442 | + [CustomEntityDataAnnotation(""first argument"")] |
| 1443 | + public partial class EntityWithAnnotation |
| 1444 | + { |
| 1445 | + [Key] |
| 1446 | + public int Id { get; set; } |
| 1447 | + } |
| 1448 | +} |
| 1449 | +", |
| 1450 | + code.AdditionalFiles.Single(f => f.Path == "EntityWithAnnotation.cs")); |
| 1451 | + |
| 1452 | + AssertFileContents( |
| 1453 | + @"using System; |
| 1454 | +using Microsoft.EntityFrameworkCore; |
| 1455 | +using Microsoft.EntityFrameworkCore.Metadata; |
| 1456 | +
|
| 1457 | +#nullable disable |
| 1458 | +
|
| 1459 | +namespace TestNamespace |
| 1460 | +{ |
| 1461 | + public partial class TestDbContext : DbContext |
| 1462 | + { |
| 1463 | + public TestDbContext() |
| 1464 | + { |
| 1465 | + } |
| 1466 | +
|
| 1467 | + public TestDbContext(DbContextOptions<TestDbContext> options) |
| 1468 | + : base(options) |
| 1469 | + { |
| 1470 | + } |
| 1471 | +
|
| 1472 | + public virtual DbSet<EntityWithAnnotation> EntityWithAnnotation { get; set; } |
| 1473 | +
|
| 1474 | + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) |
| 1475 | + { |
| 1476 | + if (!optionsBuilder.IsConfigured) |
| 1477 | + { |
| 1478 | +#warning " |
| 1479 | + + DesignStrings.SensitiveInformationWarning |
| 1480 | + + @" |
| 1481 | + optionsBuilder.UseSqlServer(""Initial Catalog=TestDatabase""); |
| 1482 | + } |
| 1483 | + } |
| 1484 | +
|
| 1485 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 1486 | + { |
| 1487 | + modelBuilder.Entity<EntityWithAnnotation>(entity => |
| 1488 | + { |
| 1489 | + entity.Property(e => e.Id).UseIdentityColumn(); |
| 1490 | + }); |
| 1491 | +
|
| 1492 | + OnModelCreatingPartial(modelBuilder); |
| 1493 | + } |
| 1494 | +
|
| 1495 | + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); |
| 1496 | + } |
| 1497 | +} |
| 1498 | +", |
| 1499 | + code.ContextFile); |
| 1500 | + }, |
| 1501 | + assertModel: null, |
| 1502 | + skipBuild: true); |
| 1503 | + } |
| 1504 | + |
| 1505 | + [ConditionalFact] |
| 1506 | + public void Entity_property_with_custom_annotation() |
| 1507 | + { |
| 1508 | + Test( |
| 1509 | + modelBuilder => modelBuilder |
| 1510 | + .Entity( |
| 1511 | + "EntityWithPropertyAnnotation", |
| 1512 | + x => |
| 1513 | + { |
| 1514 | + x.Property<int>("Id") |
| 1515 | + .HasAnnotation("Custom:PropertyAnnotation", "first argument"); |
| 1516 | + x.HasKey("Id"); |
| 1517 | + }), |
| 1518 | + new ModelCodeGenerationOptions { UseDataAnnotations = true }, |
| 1519 | + code => |
| 1520 | + { |
| 1521 | + AssertFileContents( |
| 1522 | + @"using System; |
| 1523 | +using System.Collections.Generic; |
| 1524 | +using System.ComponentModel.DataAnnotations; |
| 1525 | +using System.ComponentModel.DataAnnotations.Schema; |
| 1526 | +using Microsoft.EntityFrameworkCore; |
| 1527 | +
|
| 1528 | +#nullable disable |
| 1529 | +
|
| 1530 | +namespace TestNamespace |
| 1531 | +{ |
| 1532 | + public partial class EntityWithPropertyAnnotation |
| 1533 | + { |
| 1534 | + [Key] |
| 1535 | + [CustomPropertyDataAnnotation(""first argument"")] |
| 1536 | + public int Id { get; set; } |
| 1537 | + } |
| 1538 | +} |
| 1539 | +", |
| 1540 | + code.AdditionalFiles.Single(f => f.Path == "EntityWithPropertyAnnotation.cs")); |
| 1541 | + |
| 1542 | + AssertFileContents( |
| 1543 | + @"using System; |
| 1544 | +using Microsoft.EntityFrameworkCore; |
| 1545 | +using Microsoft.EntityFrameworkCore.Metadata; |
| 1546 | +
|
| 1547 | +#nullable disable |
| 1548 | +
|
| 1549 | +namespace TestNamespace |
| 1550 | +{ |
| 1551 | + public partial class TestDbContext : DbContext |
| 1552 | + { |
| 1553 | + public TestDbContext() |
| 1554 | + { |
| 1555 | + } |
| 1556 | +
|
| 1557 | + public TestDbContext(DbContextOptions<TestDbContext> options) |
| 1558 | + : base(options) |
| 1559 | + { |
| 1560 | + } |
| 1561 | +
|
| 1562 | + public virtual DbSet<EntityWithPropertyAnnotation> EntityWithPropertyAnnotation { get; set; } |
| 1563 | +
|
| 1564 | + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) |
| 1565 | + { |
| 1566 | + if (!optionsBuilder.IsConfigured) |
| 1567 | + { |
| 1568 | +#warning " |
| 1569 | + + DesignStrings.SensitiveInformationWarning |
| 1570 | + + @" |
| 1571 | + optionsBuilder.UseSqlServer(""Initial Catalog=TestDatabase""); |
| 1572 | + } |
| 1573 | + } |
| 1574 | +
|
| 1575 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 1576 | + { |
| 1577 | + modelBuilder.Entity<EntityWithPropertyAnnotation>(entity => |
| 1578 | + { |
| 1579 | + entity.Property(e => e.Id).UseIdentityColumn(); |
| 1580 | + }); |
| 1581 | +
|
| 1582 | + OnModelCreatingPartial(modelBuilder); |
| 1583 | + } |
| 1584 | +
|
| 1585 | + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); |
| 1586 | + } |
| 1587 | +} |
| 1588 | +", |
| 1589 | + code.ContextFile); |
| 1590 | + }, |
| 1591 | + assertModel: null, |
| 1592 | + skipBuild: true); |
| 1593 | + } |
| 1594 | + |
| 1595 | + protected override void AddModelServices(IServiceCollection services) |
| 1596 | + { |
| 1597 | + services.Replace(ServiceDescriptor.Singleton<IRelationalAnnotationProvider, ModelAnnotationProvider>()); |
| 1598 | + } |
| 1599 | + |
| 1600 | + protected override void AddScaffoldingServices(IServiceCollection services) |
| 1601 | + { |
| 1602 | + services.Replace(ServiceDescriptor.Singleton<IAnnotationCodeGenerator, ModelAnnotationCodeGenerator>()); |
| 1603 | + } |
| 1604 | + |
| 1605 | + public class ModelAnnotationProvider : SqlServerAnnotationProvider |
| 1606 | + { |
| 1607 | + public ModelAnnotationProvider(RelationalAnnotationProviderDependencies dependencies) |
| 1608 | + : base(dependencies) |
| 1609 | + { |
| 1610 | + } |
| 1611 | + |
| 1612 | + /// <inheritdoc /> |
| 1613 | + public override IEnumerable<IAnnotation> For(ITable table) |
| 1614 | + { |
| 1615 | + foreach (var annotation in base.For(table)) |
| 1616 | + { |
| 1617 | + yield return annotation; |
| 1618 | + } |
| 1619 | + |
| 1620 | + var entityType = table.EntityTypeMappings.First().EntityType; |
| 1621 | + |
| 1622 | + foreach (var annotation in entityType.GetAnnotations().Where(a => a.Name == "Custom:EntityAnnotation")) |
| 1623 | + { |
| 1624 | + yield return annotation; |
| 1625 | + } |
| 1626 | + } |
| 1627 | + |
| 1628 | + /// <inheritdoc /> |
| 1629 | + public override IEnumerable<IAnnotation> For(IColumn column) |
| 1630 | + { |
| 1631 | + foreach (var annotation in base.For(column)) |
| 1632 | + { |
| 1633 | + yield return annotation; |
| 1634 | + } |
| 1635 | + |
| 1636 | + var properties = column.PropertyMappings.Select(m => m.Property); |
| 1637 | + var annotations = properties.SelectMany(p => p.GetAnnotations()).GroupBy(a => a.Name).Select(g => g.First()); |
| 1638 | + |
| 1639 | + foreach (var annotation in annotations.Where(a => a.Name == "Custom:PropertyAnnotation")) |
| 1640 | + { |
| 1641 | + yield return annotation; |
| 1642 | + } |
| 1643 | + } |
| 1644 | + } |
| 1645 | + |
| 1646 | + public class ModelAnnotationCodeGenerator : SqlServerAnnotationCodeGenerator |
| 1647 | + { |
| 1648 | + public ModelAnnotationCodeGenerator(AnnotationCodeGeneratorDependencies dependencies) |
| 1649 | + : base(dependencies) |
| 1650 | + { |
| 1651 | + } |
| 1652 | + |
| 1653 | + protected override AttributeCodeFragment GenerateDataAnnotation(IEntityType entityType, IAnnotation annotation) |
| 1654 | + => annotation.Name switch |
| 1655 | + { |
| 1656 | + "Custom:EntityAnnotation" => new AttributeCodeFragment( |
| 1657 | + typeof(CustomEntityDataAnnotationAttribute), new object[] { annotation.Value as string }), |
| 1658 | + _ => base.GenerateDataAnnotation(entityType, annotation) |
| 1659 | + }; |
| 1660 | + |
| 1661 | + protected override AttributeCodeFragment GenerateDataAnnotation(IProperty property, IAnnotation annotation) |
| 1662 | + => annotation.Name switch |
| 1663 | + { |
| 1664 | + "Custom:PropertyAnnotation" => new AttributeCodeFragment(typeof(CustomPropertyDataAnnotationAttribute), new object[] {annotation.Value as string}), |
| 1665 | + _ => base.GenerateDataAnnotation(property, annotation) |
| 1666 | + }; |
| 1667 | + } |
| 1668 | + |
| 1669 | + [AttributeUsage(AttributeTargets.Class)] |
| 1670 | + public class CustomEntityDataAnnotationAttribute : Attribute |
| 1671 | + { |
| 1672 | + public CustomEntityDataAnnotationAttribute(string argument) |
| 1673 | + => Argument = argument; |
| 1674 | + |
| 1675 | + public virtual string Argument { get; } |
| 1676 | + } |
| 1677 | + |
| 1678 | + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] |
| 1679 | + public class CustomPropertyDataAnnotationAttribute : Attribute |
| 1680 | + { |
| 1681 | + public CustomPropertyDataAnnotationAttribute(string argument) |
| 1682 | + => Argument = argument; |
| 1683 | + |
| 1684 | + public virtual string Argument { get; } |
| 1685 | + } |
1405 | 1686 | } |
1406 | 1687 | } |
0 commit comments