Browse Source

Refactor entity extensions.

pull/3360/head
Halil İbrahim Kalkan 6 years ago
parent
commit
7da5321d69
  1. 19
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  2. 14
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Extensions/EntityExtensionInfo.cs
  3. 116
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Extensions/EntityExtensionManager.cs
  4. 17
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Extensions/PropertyExtensionInfo.cs
  5. 2
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs
  6. 2
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs
  7. 5
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs

19
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -165,7 +165,7 @@ namespace Volo.Abp.EntityFrameworkCore
FillExtraPropertiesForTrackedEntities(e);
}
private static void FillExtraPropertiesForTrackedEntities(EntityTrackedEventArgs e)
protected virtual void FillExtraPropertiesForTrackedEntities(EntityTrackedEventArgs e)
{
var entityType = e.Entry.Metadata.ClrType;
if (entityType == null)
@ -187,7 +187,18 @@ namespace Volo.Abp.EntityFrameworkCore
foreach (var propertyName in propertyNames)
{
entity.SetProperty(propertyName, e.Entry.CurrentValues[propertyName]);
/* Checking "currentValue != null" has a good advantage:
* Assume that you we already using a named extra property,
* then decided to create a field (entity extension) for it.
* In this way, it prevents to delete old value in the JSON and
* updates the field on the next save!
*/
var currentValue = e.Entry.CurrentValues[propertyName];
if (currentValue != null)
{
entity.SetProperty(propertyName, currentValue);
}
}
}
@ -223,9 +234,9 @@ namespace Volo.Abp.EntityFrameworkCore
AddDomainEvents(changeReport, entry.Entity);
}
private void HandleExtraPropertiesOnSave(EntityEntry entry)
protected virtual void HandleExtraPropertiesOnSave(EntityEntry entry)
{
if (entry.State == EntityState.Deleted)
if (entry.State.IsIn(EntityState.Deleted, EntityState.Unchanged))
{
return;
}

14
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Extensions/EntityExtensionInfo.cs

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Volo.Abp.EntityFrameworkCore.Extensions
{
public class EntityExtensionInfo
{
public Dictionary<string, PropertyExtensionInfo> Properties { get; set; }
public EntityExtensionInfo()
{
Properties = new Dictionary<string, PropertyExtensionInfo>();
}
}
}

116
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Extensions/EntityExtensionManager.cs

@ -1,60 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Volo.Abp.Data;
namespace Volo.Abp.EntityFrameworkCore.Extensions
{
public class EntityExtensionInfo
{
public Dictionary<string, PropertyExtensionInfo> Properties { get; set; }
public EntityExtensionInfo()
{
Properties = new Dictionary<string, PropertyExtensionInfo>();
}
}
public class PropertyExtensionInfo
{
public List<Action<PropertyBuilder>> Actions { get; }
public Type PropertyType { get; }
public PropertyExtensionInfo(Type propertyType)
{
PropertyType = propertyType;
Actions = new List<Action<PropertyBuilder>>();
}
}
public static class EntityExtensionManager
{
private static readonly Dictionary<Type, EntityExtensionInfo> ExtensionInfos;
//TODO: Use PropertyBuilder<TProperty> instead
static EntityExtensionManager()
{
ExtensionInfos = new Dictionary<Type, EntityExtensionInfo>();
}
/// <summary>
/// Adds an extension property for an entity.
/// If it is already added, replaces the <paramref name="propertyBuildAction"/>
/// by the given one!
/// </summary>
/// <typeparam name="TEntity">Type of the entity</typeparam>
/// <typeparam name="TProperty">Type of the new property</typeparam>
/// <param name="propertyName">Name of the property</param>
/// <param name="propertyBuildAction">An action to configure the database mapping for the new property</param>
public static void AddProperty<TEntity, TProperty>(
string name,
Action<PropertyBuilder> propertyBuildAction)
[NotNull]string propertyName,
[NotNull]Action<PropertyBuilder> propertyBuildAction)
{
AddProperty(
typeof(TEntity),
typeof(TProperty),
propertyName,
propertyBuildAction
);
}
/// <summary>
/// Adds an extension property for an entity.
/// If it is already added, replaces the <paramref name="propertyBuildAction"/>
/// by the given one!
/// </summary>
/// <param name="entityType">Type of the entity</param>
/// <param name="propertyType">Type of the new property</param>
/// <param name="propertyName">Name of the property</param>
/// <param name="propertyBuildAction">An action to configure the database mapping for the new property</param>
public static void AddProperty(
Type entityType,
Type propertyType,
[NotNull]string propertyName,
[NotNull]Action<PropertyBuilder> propertyBuildAction)
{
Check.NotNull(entityType, nameof(entityType));
Check.NotNull(propertyType, nameof(propertyType));
Check.NotNullOrWhiteSpace(propertyName, nameof(propertyName));
Check.NotNull(propertyBuildAction, nameof(propertyBuildAction));
var extensionInfo = ExtensionInfos
.GetOrAdd(typeof(TEntity), () => new EntityExtensionInfo());
.GetOrAdd(entityType, () => new EntityExtensionInfo());
var propertyExtensionInfo = extensionInfo.Properties
.GetOrAdd(name, () => new PropertyExtensionInfo(typeof(TProperty)));
.GetOrAdd(propertyName, () => new PropertyExtensionInfo(propertyType));
propertyExtensionInfo.Actions.Add(propertyBuildAction);
propertyExtensionInfo.Action = propertyBuildAction;
}
public static void ConfigureProperties<TEntity>(EntityTypeBuilder entityTypeBuilder)
/// <summary>
/// Configures the entity mapping for the defined extensions.
/// </summary>
/// <typeparam name="TEntity">The entity tye</typeparam>
/// <param name="entityTypeBuilder">Entity type builder</param>
public static void ConfigureExtensions<TEntity>(
[NotNull] this EntityTypeBuilder<TEntity> entityTypeBuilder)
where TEntity : class, IHasExtraProperties
{
var entityExtensionInfo = ExtensionInfos.GetOrDefault(typeof(TEntity));
ConfigureExtensions(typeof(TEntity), entityTypeBuilder);
}
/// <summary>
/// Configures the entity mapping for the defined extensions.
/// </summary>
/// <param name="entityType">Type of the entity</param>
/// <param name="entityTypeBuilder">Entity type builder</param>
public static void ConfigureExtensions(
[NotNull] Type entityType,
[NotNull] EntityTypeBuilder entityTypeBuilder)
{
Check.NotNull(entityType, nameof(entityType));
Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder));
var entityExtensionInfo = ExtensionInfos.GetOrDefault(entityType);
if (entityExtensionInfo == null)
{
return;
@ -62,11 +98,21 @@ namespace Volo.Abp.EntityFrameworkCore.Extensions
foreach (var propertyExtensionInfo in entityExtensionInfo.Properties)
{
var property = entityTypeBuilder.Property(propertyExtensionInfo.Value.PropertyType, propertyExtensionInfo.Key);
foreach (var action in propertyExtensionInfo.Value.Actions)
var propertyName = propertyExtensionInfo.Key;
var propertyType = propertyExtensionInfo.Value.PropertyType;
/* Prevent multiple calls to the entityTypeBuilder.Property(...) method */
if (entityTypeBuilder.Metadata.FindProperty(propertyName) != null)
{
action(property);
continue;
}
var property = entityTypeBuilder.Property(
propertyType,
propertyName
);
propertyExtensionInfo.Value.Action(property);
}
}

17
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Extensions/PropertyExtensionInfo.cs

@ -0,0 +1,17 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Volo.Abp.EntityFrameworkCore.Extensions
{
public class PropertyExtensionInfo
{
public Action<PropertyBuilder> Action { get; set; }
public Type PropertyType { get; }
public PropertyExtensionInfo(Type propertyType)
{
PropertyType = propertyType;
}
}
}

2
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs

@ -37,7 +37,7 @@ namespace Volo.Abp.EntityFrameworkCore
modelBuilder.Entity<City>(b =>
{
EntityExtensionManager.ConfigureProperties<City>(b);
b.ConfigureExtensions();
b.OwnsMany(c => c.Districts, d =>
{

2
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs

@ -44,7 +44,7 @@ namespace Volo.Abp.TestApp.EntityFrameworkCore
modelBuilder.Entity<City>(b =>
{
EntityExtensionManager.ConfigureProperties<City>(b);
b.ConfigureExtensions();
b.OwnsMany(c => c.Districts, d =>
{

5
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs

@ -28,6 +28,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
b.ConfigureFullAuditedAggregateRoot();
b.ConfigureAbpUser();
b.ConfigureExtensions();
b.Property(u => u.NormalizedUserName).IsRequired().HasMaxLength(IdentityUserConsts.MaxNormalizedUserNameLength).HasColumnName(nameof(IdentityUser.NormalizedUserName));
b.Property(u => u.NormalizedEmail).IsRequired().HasMaxLength(IdentityUserConsts.MaxNormalizedEmailLength).HasColumnName(nameof(IdentityUser.NormalizedEmail));
@ -36,9 +37,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
b.Property(u => u.TwoFactorEnabled).HasDefaultValue(false).HasColumnName(nameof(IdentityUser.TwoFactorEnabled));
b.Property(u => u.LockoutEnabled).HasDefaultValue(false).HasColumnName(nameof(IdentityUser.LockoutEnabled));
b.Property(u => u.AccessFailedCount).HasDefaultValue(0).HasColumnName(nameof(IdentityUser.AccessFailedCount));
EntityExtensions.ConfigureProperties<IdentityUser>(b);
b.HasMany(u => u.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired();

Loading…
Cancel
Save