Skip to content

Commit

Permalink
Generates ER attribute and relationship and entity for enum propertie…
Browse files Browse the repository at this point in the history
…s from Ef Core
  • Loading branch information
ebjornset committed Feb 11, 2024
1 parent 7b4f9b1 commit d2cfde0
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/DryGen.Core/AssemblyLoadException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace DryGen.Core;

[Serializable]
[ExcludeFromCodeCoverage]
public sealed class AssemblyLoadException : Exception
{
public AssemblyLoadException(string? message) : base(message)
{
}

[ExcludeFromCodeCoverage]
private AssemblyLoadException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
Expand Down
2 changes: 1 addition & 1 deletion src/DryGen.Core/InvalidContentException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace DryGen.Core;

[Serializable]
[ExcludeFromCodeCoverage]
public sealed class InvalidContentException : Exception
{
public InvalidContentException(string? message) : base(message)
{
}

[ExcludeFromCodeCoverage]
private InvalidContentException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
Expand Down
2 changes: 1 addition & 1 deletion src/DryGen.Core/TypeLoadException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace DryGen.Core;

[Serializable]
[ExcludeFromCodeCoverage]
public sealed class TypeLoadException : Exception
{
public TypeLoadException(string? message) : base(message)
{
}

[ExcludeFromCodeCoverage]
private TypeLoadException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
Expand Down
2 changes: 1 addition & 1 deletion src/DryGen.Core/TypeMemberException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace DryGen.Core;

[Serializable]
[ExcludeFromCodeCoverage]
public sealed class TypeMemberException : Exception
{
public TypeMemberException(string? message) : base(message)
{
}

[ExcludeFromCodeCoverage]
private TypeMemberException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
Expand Down
12 changes: 9 additions & 3 deletions src/DryGen.MermaidFromCSharp/ErDiagram/ErDiagramExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public static bool IsNotMany(this ErDiagramRelationshipCardinality cardinality)
return cardinality == ErDiagramRelationshipCardinality.ZeroOrOne || cardinality == ErDiagramRelationshipCardinality.ExactlyOne;
}

public static void AppendToEntities(this Dictionary<Type, ErDiagramEntity> enumEntities, IList<ErDiagramEntity> entities)
public static void AppendToEntities<TEntityType>(this Dictionary<Type, TEntityType> enumEntities, IList<TEntityType> entities) where TEntityType: ErDiagramEntity
{
foreach (var enumEntity in enumEntities.Values)
{
Expand All @@ -115,14 +115,20 @@ public static void AppendToEntities(this Dictionary<Type, ErDiagramEntity> enumE
}
}

public static bool AddToEntityAsRelationshipIfEnum(this Type enumType, string attributeName, bool isNullable, ErDiagramEntity entity, Dictionary<Type, ErDiagramEntity> enumEntities)
public static bool AddToEntityAsRelationshipIfEnum<TEntityType>(
this Type enumType,
string attributeName,
bool isNullable,
ErDiagramEntity entity,
Dictionary<Type, TEntityType> enumEntities,
Func<Type, TEntityType> enumEntityFactory) where TEntityType : ErDiagramEntity
{
var isEnum = enumType.IsEnum;
if (isEnum)
{
if (!enumEntities.TryGetValue(enumType, out var toEntity))
{
toEntity = new ErDiagramEntity(enumType.Name, enumType);
toEntity = enumEntityFactory(enumType);
enumEntities[enumType] = toEntity;
}
var toCardinality = isNullable ? ErDiagramRelationshipCardinality.ZeroOrOne : ErDiagramRelationshipCardinality.ExactlyOne;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private void GeneratieErStructure(IList<ErDiagramEntity> entities, IReadOnlyList
var isNullable = nullableUnderlyingType != null;
var isPrimaryKey = property.CustomAttributes.Any(x => x.IsKeyAttribute());
var enumType = nullableUnderlyingType ?? propertyType;
var isEnum = enumType.AddToEntityAsRelationshipIfEnum(attributeName, isNullable, entity, enumEntities);
var isEnum = enumType.AddToEntityAsRelationshipIfEnum(attributeName, isNullable, entity, enumEntities, x => new ErDiagramEntity(x.Name, x));
entity.AddAttribute(new ErDiagramAttribute(attributeType, attributeName, isNullable, isPrimaryKey, isForeignKey: isEnum));
}
}
Expand Down
28 changes: 19 additions & 9 deletions src/DryGen.MermaidFromEfCore/ErDiagramStructureBuilderByEfCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,26 @@ public IReadOnlyList<ErDiagramEntity> GenerateErStructure(Assembly assembly, IRe
.Select(et => new EfCoreErEntity(nameRewriter?.Rewrite(et.ClrType.Name) ?? et.ClrType.Name, et))
.OrderBy(nt => nt.Name)
.ThenBy(nt => nt.Type.Name)
.ThenBy(nt => nt.Type.Namespace).ToArray();
.ThenBy(nt => nt.Type.Namespace).ToList();
GenerateErStructure(result, attributeFilters);
return result;
}

private static void GenerateErStructure(IReadOnlyList<EfCoreErEntity> entities, IReadOnlyList<IPropertyFilter> attributeFilters)
private static void GenerateErStructure(IList<EfCoreErEntity> entities, IReadOnlyList<IPropertyFilter> attributeFilters)
{
var enumEntities = new Dictionary<Type, EfCoreErEntity>();
var entityLookup = entities.ToDictionary(x => x.Type, x => x);
foreach (var entity in entities)
{
GenerateErAttributes(attributeFilters, entity);
GenerateErAttributes(attributeFilters, entity, enumEntities);
GenerateErRelationships(entityLookup, entity);
}
enumEntities.AppendToEntities(entities);
}

private static void GenerateErRelationships(Dictionary<Type, EfCoreErEntity> entityLookup, EfCoreErEntity entity)
{
foreach (var foreignKey in entity.EntityType.GetForeignKeys())
foreach (var foreignKey in entity.EntityType?.GetForeignKeys() ?? throw new TypeMemberException($"EntityType was null for entity '{entity.Name}'"))
{
// Generate relationships from the principal side, since it makes the diagram flow from the principals to the dependents
var principalType = foreignKey.PrincipalEntityType.ClrType;
Expand Down Expand Up @@ -110,19 +112,23 @@ private static void CreateRelationshipForNonCollection(EfCoreErEntity entity, Mo
principalEntity.AddRelationship(entity, fromCardianlity, toCardianlity, label, propertyName, isIdentifying);
}

private static void GenerateErAttributes(IReadOnlyList<IPropertyFilter> attributeFilters, EfCoreErEntity entity)
private static void GenerateErAttributes(IReadOnlyList<IPropertyFilter> attributeFilters, EfCoreErEntity entity, Dictionary<Type, EfCoreErEntity> enumEntities)
{
foreach (var efProperty in entity.EntityType.GetProperties().Where(p => p?.PropertyInfo != null && attributeFilters.All(f => f.Accepts(p.PropertyInfo))))
foreach (var efProperty in entity.EntityType?.GetProperties().Where(p => p?.PropertyInfo != null && attributeFilters.All(f => f.Accepts(p.PropertyInfo)))
?? throw new TypeMemberException($"EntityType was null for entity '{entity.Name}'"))
{
var propertyType = efProperty.ClrType;
if (propertyType.IsErDiagramAttributePropertyType())
{
var attributeType = propertyType.GetErDiagramAttributeTypeName();
var attributeName = efProperty.Name;
var isNullable = Nullable.GetUnderlyingType(propertyType) != null;
var nullableUnderlyingType = Nullable.GetUnderlyingType(propertyType);
var isNullable = nullableUnderlyingType != null;
var isPrimaryKey = efProperty.IsPrimaryKey();
var isAlternateKey = !isPrimaryKey && efProperty.IsKey();
var isForeignKey = efProperty.IsForeignKey();
var enumType = nullableUnderlyingType ?? propertyType;
var isEnum = enumType.AddToEntityAsRelationshipIfEnum(attributeName, isNullable, entity, enumEntities, x => new EfCoreErEntity(x.Name, x));
var isForeignKey = efProperty.IsForeignKey() || isEnum;
entity.AddAttribute(new ErDiagramAttribute(attributeType, attributeName, isNullable, isPrimaryKey, isAlternateKey, isForeignKey));
}
}
Expand Down Expand Up @@ -246,7 +252,11 @@ public EfCoreErEntity(string name, ModelEntityType entityType) : base(name, enti
EntityType = entityType;
}

public ModelEntityType EntityType { get; }
public EfCoreErEntity(string name, Type type) : base(name, type)
{
}

public ModelEntityType? EntityType { get; }
}

private sealed class ModelEntityTypeEqualityComparer : IEqualityComparer<ModelEntityType>
Expand Down
4 changes: 1 addition & 3 deletions src/DryGen/OptionsException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ namespace DryGen;

[Serializable]
#endif
[ExcludeFromCodeCoverage]
public sealed class OptionsException : Exception
{
public OptionsException(string message) : base(message)
{
}

#if (NET6_0 || NET7_0)

[ExcludeFromCodeCoverage]
private OptionsException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}

#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ Scenario: Generates ER attributes only for public properties
public DbSet<Order> Orders { get; set; }
public TestDbContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasNoKey();
{
modelBuilder.Entity<Order>().HasNoKey();
}
}
}
Expand Down Expand Up @@ -59,8 +59,8 @@ Scenario: Generates ER attributes only for properties with getter and setter
public DbSet<Order> Orders { get; set; }
public TestDbContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasNoKey();
{
modelBuilder.Entity<Order>().HasNoKey();
}
}
}
Expand Down Expand Up @@ -96,8 +96,8 @@ Scenario: Generates ER attributes only for non collection properties
public DbSet<Order> Orders { get; set; }
public TestDbContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasNoKey();
{
modelBuilder.Entity<Order>().HasNoKey();
}
}
}
Expand Down Expand Up @@ -222,8 +222,8 @@ Examples:
| Structure builder | Property type | Attribute metadata | To cardinality |
| Reflection | Status | FK | \|\| |
| Reflection | Status? | FK "Null" | o\| |
#| EfCore | Status | FK | \|\| |
#| EfCore | Status? | FK "Null" | o\| |
| EfCore | Status | FK | \|\| |
| EfCore | Status? | FK "Null" | o\| |

Scenario: Generates ER attributes with null comment for nullable well known type properties
Given this C# source code
Expand All @@ -247,8 +247,8 @@ Scenario: Generates ER attributes with null comment for nullable well known type
public DbSet<Order> Orders { get; set; }
public TestDbContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasNoKey();
{
modelBuilder.Entity<Order>().HasNoKey();
}
}
}
Expand Down Expand Up @@ -291,8 +291,8 @@ Scenario: Generates ER attributes as PK for properties with key attribute
public DbSet<A> Aaaa { get; set; }
public TestDbContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//modelBuilder.Entity<A>().HasNoKey();
{
//modelBuilder.Entity<A>().HasNoKey();
}
}
}
Expand Down

0 comments on commit d2cfde0

Please sign in to comment.