Skip to content

Commit

Permalink
Merge pull request #121 from Krzysztofz01/infrastructure-entity-build…
Browse files Browse the repository at this point in the history
…er-api-improvement

Infrastructure entity builder api improvement and sqlite support
  • Loading branch information
Krzysztofz01 committed Dec 19, 2022
2 parents 38f9f5d + 44847da commit e7d25df
Show file tree
Hide file tree
Showing 24 changed files with 4,382 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<ProjectReference Include="..\AccessPointMap.Application.Pcap.ApmPcapNative\AccessPointMap.Application.Pcap.ApmPcapNative.csproj" />
<ProjectReference Include="..\AccessPointMap.Application\AccessPointMap.Application.csproj" />
<ProjectReference Include="..\AccessPointMap.Infrastructure.MySql\AccessPointMap.Infrastructure.MySql.csproj" />
<ProjectReference Include="..\AccessPointMap.Infrastructure.Sqlite\AccessPointMap.Infrastructure.Sqlite.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.10" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,50 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using AccessPointMap.Domain.Core.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Linq.Expressions;

namespace AccessPointMap.Infrastructure.Core.Extensions
{
#nullable enable
public static class EntityTypeBuilderExtensions
{
#nullable enable
public static void OwnsRequiredOne<TEntity, TRelatedEntity>(this EntityTypeBuilder<TEntity> entityTypeBuilder, Expression<Func<TEntity, TRelatedEntity?>> navigationExpression) where TRelatedEntity : class where TEntity : class
public static KeyBuilder HasPublicKey<TAggregateRoot>(this EntityTypeBuilder<TAggregateRoot> entityTypeBuilder)
where TAggregateRoot : AggregateRoot
{
var keyBuilder = entityTypeBuilder.HasKey(e => e.Id);
entityTypeBuilder.Property(e => e.Id).ValueGeneratedNever();

return keyBuilder;
}

public static void UseSoftDelete<TAggragateRoot>(this EntityTypeBuilder<TAggragateRoot> entityTypeBuilder)
where TAggragateRoot : AggregateRoot
{
entityTypeBuilder.Property(e => e.DeletedAt).HasDefaultValue(null);
entityTypeBuilder.HasQueryFilter(e => e.DeletedAt == null);
}

public static OwnedNavigationBuilder<TEntity, TValueObject> OwnsOneValueObject<TEntity, TValueObject>(this EntityTypeBuilder<TEntity> entityTypeBuilder, Expression<Func<TEntity, TValueObject?>> navigationExpression)
where TEntity : Entity
where TValueObject : ValueObject<TValueObject>
{
entityTypeBuilder.OwnsOne(navigationExpression);
var navigationBuilder = entityTypeBuilder.OwnsOne(navigationExpression);
entityTypeBuilder.Navigation(navigationExpression).IsRequired();

return navigationBuilder;
}

// TODO: Implement a generic constraint to ensure that the value object is in deed a shared value object
public static OwnedNavigationBuilder<TEntity, TValueObject> OwnsOneSharedValueObject<TEntity, TValueObject>(this EntityTypeBuilder<TEntity> entityTypeBuilder, Expression<Func<TEntity, TValueObject?>> navigationExpression)
where TEntity : Entity
where TValueObject : class
{
var navigationBuilder = entityTypeBuilder.OwnsOne(navigationExpression);
entityTypeBuilder.Navigation(navigationExpression).IsRequired();

return navigationBuilder;
}
#nullable disable
}
#nullable disable
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,56 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using AccessPointMap.Domain.Core.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Linq.Expressions;

namespace AccessPointMap.Infrastructure.Core.Extensions
{
#nullable enable
public static class OwnedNavigationBuilderExtensions
{
#nullable enable
public static void OwnsRequiredOne<TOwnerEntity, TDependentEntity, TNewDependentEntity>(this OwnedNavigationBuilder<TOwnerEntity, TDependentEntity> ownedNavigationBuilder, Expression<Func<TDependentEntity, TNewDependentEntity?>> navigationExpression) where TOwnerEntity : class where TDependentEntity : class where TNewDependentEntity : class
public static KeyBuilder HasPublicKey<TAggregateRoot, TEntity>(this OwnedNavigationBuilder<TAggregateRoot, TEntity> ownedNavigationBuilder)
where TAggregateRoot : AggregateRoot
where TEntity : Entity
{
ownedNavigationBuilder.OwnsOne(navigationExpression);
ownedNavigationBuilder.Navigation(navigationExpression).IsRequired();
var aggregateRootName = typeof(TAggregateRoot).Name.ToLower();
ownedNavigationBuilder.WithOwner().HasForeignKey($"{aggregateRootName}Id");

var keyBuilder = ownedNavigationBuilder.HasKey(e => e.Id);
ownedNavigationBuilder.Property(e => e.Id).ValueGeneratedNever();

return keyBuilder;
}

public static void UseSoftDelete<TParentEntity, TChildEntity>(this OwnedNavigationBuilder<TParentEntity, TChildEntity> ownedNavigationBuilder)
where TParentEntity : Entity
where TChildEntity : Entity
{
ownedNavigationBuilder.Property(e => e.DeletedAt).HasDefaultValue(null);
}

public static OwnedNavigationBuilder<TChildEntity, TValueObject> OwnsOneValueObject<TParentEntity, TChildEntity, TValueObject>(this OwnedNavigationBuilder<TParentEntity, TChildEntity> entityTypeBuilder, Expression<Func<TChildEntity, TValueObject?>> navigationExpression)
where TParentEntity : Entity
where TChildEntity : Entity
where TValueObject : ValueObject<TValueObject>
{
var navigationBuilder = entityTypeBuilder.OwnsOne(navigationExpression);
entityTypeBuilder.Navigation(navigationExpression).IsRequired();

return navigationBuilder;
}

// TODO: Implement a generic constraint to ensure that the value object is in deed a shared value object
public static OwnedNavigationBuilder<TChildEntity, TValueObject> OwnsOneSharedValueObject<TParentEntity, TChildEntity, TValueObject>(this OwnedNavigationBuilder<TParentEntity, TChildEntity> entityTypeBuilder, Expression<Func<TChildEntity, TValueObject?>> navigationExpression)
where TParentEntity : Entity
where TChildEntity : Entity
where TValueObject : class
{
var navigationBuilder = entityTypeBuilder.OwnsOne(navigationExpression);
entityTypeBuilder.Navigation(navigationExpression).IsRequired();

return navigationBuilder;
}
#nullable disable
}
#nullable disable
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using AccessPointMap.Domain.AccessPoints;
using AccessPointMap.Infrastructure.Core.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace AccessPointMap.Infrastructure.MySql.Builders
Expand All @@ -9,70 +8,63 @@ internal sealed class AccessPointTypeBuilder
{
public AccessPointTypeBuilder(EntityTypeBuilder<AccessPoint> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Id).ValueGeneratedNever();
builder.OwnsOne(e => e.Bssid).HasIndex(v => v.Value).IsUnique();
builder.Navigation(e => e.Bssid).IsRequired();
builder.HasPublicKey();

builder.OwnsRequiredOne(e => e.Manufacturer);
builder.OwnsRequiredOne(e => e.Ssid);
builder.OwnsOne(e => e.Frequency);
builder.OwnsRequiredOne(e => e.DeviceType);
builder.OwnsOne(e => e.ContributorId);
builder.OwnsOne(e => e.CreationTimestamp);
builder.OwnsOne(e => e.VersionTimestamp);
builder.OwnsOne(e => e.Positioning);
builder.OwnsOne(e => e.Security);
builder.OwnsRequiredOne(e => e.Note);
builder.OwnsRequiredOne(e => e.RunIdentifier);
builder.OwnsOne(e => e.DisplayStatus);
builder.OwnsOne(e => e.Presence);
builder.OwnsOneValueObject(e => e.Bssid).HasIndex(v => v.Value).IsUnique();
builder.OwnsOneValueObject(e => e.Manufacturer);
builder.OwnsOneValueObject(e => e.Ssid);
builder.OwnsOneValueObject(e => e.Frequency);
builder.OwnsOneValueObject(e => e.DeviceType);
builder.OwnsOneSharedValueObject(e => e.ContributorId);
builder.OwnsOneSharedValueObject(e => e.CreationTimestamp);
builder.OwnsOneSharedValueObject(e => e.VersionTimestamp);
builder.OwnsOneValueObject(e => e.Positioning);
builder.OwnsOneValueObject(e => e.Security);
builder.OwnsOneValueObject(e => e.Note);
builder.OwnsOneValueObject(e => e.RunIdentifier);
builder.OwnsOneValueObject(e => e.DisplayStatus);
builder.OwnsOneValueObject(e => e.Presence);

builder.OwnsMany(e => e.Stamps, e =>
{
e.WithOwner().HasForeignKey("accesspointId");
e.HasKey(e => e.Id);
e.Property(e => e.Id).ValueGeneratedNever();
e.OwnsRequiredOne(e => e.Ssid);
e.OwnsOne(e => e.Frequency);
e.OwnsRequiredOne(e => e.DeviceType);
e.OwnsOne(e => e.ContributorId);
e.OwnsOne(e => e.CreationTimestamp);
e.OwnsOne(e => e.Positioning);
e.OwnsOne(e => e.Security);
e.OwnsOne(e => e.Status);
e.OwnsRequiredOne(e => e.RunIdentifier);
e.HasPublicKey();
e.Property(e => e.DeletedAt).HasDefaultValue(null);
e.OwnsOneValueObject(e => e.Ssid);
e.OwnsOneValueObject(e => e.Frequency);
e.OwnsOneValueObject(e => e.DeviceType);
e.OwnsOneSharedValueObject(e => e.ContributorId);
e.OwnsOneSharedValueObject(e => e.CreationTimestamp);
e.OwnsOneValueObject(e => e.Positioning);
e.OwnsOneValueObject(e => e.Security);
e.OwnsOneValueObject(e => e.Status);
e.OwnsOneValueObject(e => e.RunIdentifier);
e.UseSoftDelete();
});

builder.OwnsMany(e => e.Adnnotations, e =>
{
e.WithOwner().HasForeignKey("accesspointId");
e.HasKey(e => e.Id);
e.Property(e => e.Id).ValueGeneratedNever();
e.OwnsRequiredOne(e => e.Title);
e.OwnsRequiredOne(e => e.Content);
e.OwnsOne(e => e.Timestamp);
e.HasPublicKey();
e.OwnsOneValueObject(e => e.Title);
e.OwnsOneValueObject(e => e.Content);
e.OwnsOneSharedValueObject(e => e.Timestamp);
e.Property(e => e.DeletedAt).HasDefaultValue(null);
e.UseSoftDelete();
});

builder.OwnsMany(e => e.Packets, e =>
{
e.WithOwner().HasForeignKey("accesspointId");
e.HasKey(e => e.Id);
e.Property(e => e.Id).ValueGeneratedNever();
e.OwnsRequiredOne(e => e.DestinationAddress);
e.OwnsRequiredOne(e => e.FrameType);
e.OwnsRequiredOne(e => e.Data);
e.HasPublicKey();
e.Property(e => e.DeletedAt).HasDefaultValue(null);
});
e.OwnsOneValueObject(e => e.DestinationAddress);
e.OwnsOneValueObject(e => e.FrameType);
e.OwnsOneValueObject(e => e.Data);
builder.Property(e => e.DeletedAt).HasDefaultValue(null);
e.UseSoftDelete();
});

builder.HasQueryFilter(e => e.DeletedAt == null);
builder.UseSoftDelete();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using AccessPointMap.Domain.Identities;
using AccessPointMap.Infrastructure.Core.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace AccessPointMap.Infrastructure.MySql.Builders
Expand All @@ -9,29 +8,26 @@ internal sealed class IdentityTypeBuilder
{
public IdentityTypeBuilder(EntityTypeBuilder<Identity> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Id).ValueGeneratedNever();
builder.OwnsOne(e => e.Name).Property(v => v.Value).HasMaxLength(40);
builder.Navigation(e => e.Name).IsRequired();
builder.OwnsOne(e => e.Email).HasIndex(v => v.Value).IsUnique();
builder.Navigation(e => e.Email).IsRequired();
builder.OwnsRequiredOne(e => e.PasswordHash);
builder.OwnsOne(e => e.LastLogin);
builder.OwnsOne(e => e.Role);
builder.OwnsOne(e => e.Activation);
builder.HasPublicKey();

builder.OwnsOneValueObject(e => e.Name).Property(v => v.Value).HasMaxLength(40);
builder.OwnsOneValueObject(e => e.Email).HasIndex(v => v.Value).IsUnique();
builder.OwnsOneValueObject(e => e.PasswordHash);
builder.OwnsOneValueObject(e => e.LastLogin);
builder.OwnsOneValueObject(e => e.Role);
builder.OwnsOneValueObject(e => e.Activation);

builder.OwnsMany(e => e.Tokens, e =>
{
e.WithOwner().HasForeignKey("identityId");
e.HasKey(e => e.Id);
e.Property(e => e.Id).ValueGeneratedNever();
e.HasPublicKey();
// TODO: Other values should be also included here in order to be more verbose, but the default config works
e.HasIndex(e => e.TokenHash).IsUnique();
e.Property(e => e.DeletedAt).HasDefaultValue(null);
});
builder.Property(e => e.DeletedAt).HasDefaultValue(null);
e.UseSoftDelete();
});

builder.HasQueryFilter(e => e.DeletedAt == null);
builder.UseSoftDelete();
}
}
}
Loading

0 comments on commit e7d25df

Please sign in to comment.