#3126 Implemented initial entity extensions system.

pull/3360/head
Halil İbrahim Kalkan 5 years ago
parent 582aa69ed0
commit 2a62708175

@ -18,6 +18,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.EntityFrameworkCore.EntityHistory; using Volo.Abp.EntityFrameworkCore.EntityHistory;
using Volo.Abp.EntityFrameworkCore.Extensions;
using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.EntityFrameworkCore.ValueConverters; using Volo.Abp.EntityFrameworkCore.ValueConverters;
using Volo.Abp.Guids; using Volo.Abp.Guids;
@ -155,6 +156,39 @@ namespace Volo.Abp.EntityFrameworkCore
ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges; ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges; ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
ChangeTracker.Tracked += ChangeTracker_Tracked;
}
protected virtual void ChangeTracker_Tracked(object sender, EntityTrackedEventArgs e)
{
FillExtraPropertiesForTrackedEntities(e);
}
private static void FillExtraPropertiesForTrackedEntities(EntityTrackedEventArgs e)
{
var entityType = e.Entry.Metadata.ClrType;
if (entityType == null)
{
return;
}
if (!(e.Entry.Entity is IHasExtraProperties entity))
{
return;
}
if (!e.FromQuery)
{
return;
}
var propertyNames = EntityExtensions.GetPropertyNames(entityType);
foreach (var propertyName in propertyNames)
{
entity.SetProperty(propertyName, e.Entry.CurrentValues[propertyName]);
}
} }
protected virtual EntityChangeReport ApplyAbpConcepts() protected virtual EntityChangeReport ApplyAbpConcepts()
@ -184,9 +218,37 @@ namespace Volo.Abp.EntityFrameworkCore
break; break;
} }
HandleExtraPropertiesOnSave(entry);
AddDomainEvents(changeReport, entry.Entity); AddDomainEvents(changeReport, entry.Entity);
} }
private void HandleExtraPropertiesOnSave(EntityEntry entry)
{
if (entry.State == EntityState.Deleted)
{
return;
}
var entityType = entry.Metadata.ClrType;
if (entityType == null)
{
return;
}
if (!(entry.Entity is IHasExtraProperties entity))
{
return;
}
var propertyNames = EntityExtensions.GetPropertyNames(entityType);
foreach (var propertyName in propertyNames)
{
entry.Property(propertyName).CurrentValue = entity.GetProperty(propertyName);
}
}
protected virtual void ApplyAbpConceptsForAddedEntity(EntityEntry entry, EntityChangeReport changeReport) protected virtual void ApplyAbpConceptsForAddedEntity(EntityEntry entry, EntityChangeReport changeReport)
{ {
CheckAndSetId(entry); CheckAndSetId(entry);

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
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 EntityExtensions
{
private static readonly Dictionary<Type, EntityExtensionInfo> ExtensionInfos;
//TODO: Use PropertyBuilder<TProperty> instead
static EntityExtensions()
{
ExtensionInfos = new Dictionary<Type, EntityExtensionInfo>();
}
public static void AddProperty<TEntity>(
string name,
Type propertyType,
Action<PropertyBuilder> propertyBuildAction)
{
var extensionInfo = ExtensionInfos
.GetOrAdd(typeof(TEntity), () => new EntityExtensionInfo());
var propertyExtensionInfo = extensionInfo.Properties
.GetOrAdd(name, () => new PropertyExtensionInfo(propertyType));
propertyExtensionInfo.Actions.Add(propertyBuildAction);
}
public static void ConfigureProperties<TEntity>(EntityTypeBuilder entityTypeBuilder)
{
var entityExtensionInfo = ExtensionInfos.GetOrDefault(typeof(TEntity));
if (entityExtensionInfo == null)
{
return;
}
foreach (var propertyExtensionInfo in entityExtensionInfo.Properties)
{
var property = entityTypeBuilder.Property(propertyExtensionInfo.Value.PropertyType, propertyExtensionInfo.Key);
foreach (var action in propertyExtensionInfo.Value.Actions)
{
action(property);
}
}
}
public static string[] GetPropertyNames(Type entityType)
{
var entityExtensionInfo = ExtensionInfos.GetOrDefault(entityType);
if (entityExtensionInfo == null)
{
return Array.Empty<string>();
}
return entityExtensionInfo
.Properties
.Select(p => p.Key)
.ToArray();
}
}
}

@ -58,7 +58,7 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling
{ {
b.Property<Dictionary<string, object>>(nameof(IHasExtraProperties.ExtraProperties)) b.Property<Dictionary<string, object>>(nameof(IHasExtraProperties.ExtraProperties))
.HasColumnName(nameof(IHasExtraProperties.ExtraProperties)) .HasColumnName(nameof(IHasExtraProperties.ExtraProperties))
.HasConversion(new AbpJsonValueConverter<Dictionary<string, object>>()) .HasConversion(new ExtraPropertiesValueConverter(b.Metadata.ClrType))
.Metadata.SetValueComparer(new AbpDictionaryValueComparer<string, object>()); .Metadata.SetValueComparer(new AbpDictionaryValueComparer<string, object>());
} }
} }

@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Newtonsoft.Json; using Newtonsoft.Json;
using Volo.Abp.Data;
namespace Volo.Abp.EntityFrameworkCore.ValueConverters namespace Volo.Abp.EntityFrameworkCore.ValueConverters
{ {
@ -7,9 +8,20 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters
{ {
public AbpJsonValueConverter() public AbpJsonValueConverter()
: base( : base(
d => JsonConvert.SerializeObject(d, Formatting.None), d => SerializeObject(d),
s => JsonConvert.DeserializeObject<TPropertyType>(s)) s => DeserializeObject(s))
{ {
}
private static string SerializeObject(TPropertyType d)
{
return JsonConvert.SerializeObject(d, Formatting.None);
}
private static TPropertyType DeserializeObject(string s)
{
return JsonConvert.DeserializeObject<TPropertyType>(s);
} }
} }
} }

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Newtonsoft.Json;
using Volo.Abp.EntityFrameworkCore.Extensions;
namespace Volo.Abp.EntityFrameworkCore.ValueConverters
{
public class ExtraPropertiesValueConverter : ValueConverter<Dictionary<string, object>, string>
{
public ExtraPropertiesValueConverter(Type entityType)
: base(
d => SerializeObject(d, entityType),
s => DeserializeObject(s))
{
}
private static string SerializeObject(Dictionary<string, object> extraProperties, Type entityType)
{
var copyDictionary = new Dictionary<string, object>(extraProperties);
if (entityType != null)
{
var propertyNames = EntityExtensions.GetPropertyNames(entityType);
foreach (var propertyName in propertyNames)
{
copyDictionary.Remove(propertyName);
}
}
return JsonConvert.SerializeObject(copyDictionary, Formatting.None);
}
private static Dictionary<string, object> DeserializeObject(string extraPropertiesAsJson)
{
return JsonConvert.DeserializeObject<Dictionary<string, object>>(extraPropertiesAsJson);
}
}
}

@ -1,6 +1,7 @@
using System; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Extensions;
using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Users.EntityFrameworkCore; using Volo.Abp.Users.EntityFrameworkCore;
@ -36,11 +37,13 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
b.Property(u => u.LockoutEnabled).HasDefaultValue(false).HasColumnName(nameof(IdentityUser.LockoutEnabled)); b.Property(u => u.LockoutEnabled).HasDefaultValue(false).HasColumnName(nameof(IdentityUser.LockoutEnabled));
b.Property(u => u.AccessFailedCount).HasDefaultValue(0).HasColumnName(nameof(IdentityUser.AccessFailedCount)); 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.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired(); b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
b.HasMany(u => u.Tokens).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); b.HasMany(u => u.Tokens).WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
b.HasIndex(u => u.NormalizedUserName); b.HasIndex(u => u.NormalizedUserName);
b.HasIndex(u => u.NormalizedEmail); b.HasIndex(u => u.NormalizedEmail);
b.HasIndex(u => u.UserName); b.HasIndex(u => u.UserName);

Loading…
Cancel
Save