mirror of https://github.com/abpframework/abp.git
committed by
GitHub
80 changed files with 1545 additions and 273 deletions
@ -0,0 +1,3 @@ |
|||
# Object Extensions |
|||
|
|||
TODO |
|||
@ -0,0 +1,36 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace AutoMapper |
|||
{ |
|||
public static class AbpAutoMapperExtensibleDtoExtensions |
|||
{ |
|||
public static IMappingExpression<TSource, TDestination> MapExtraProperties<TSource, TDestination>( |
|||
this IMappingExpression<TSource, TDestination> mappingExpression, |
|||
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) |
|||
where TDestination : IHasExtraProperties |
|||
where TSource : IHasExtraProperties |
|||
{ |
|||
return mappingExpression |
|||
.ForMember( |
|||
x => x.ExtraProperties, |
|||
y => y.MapFrom( |
|||
(source, destination, extraProps) => |
|||
{ |
|||
var result = extraProps.IsNullOrEmpty() |
|||
? new Dictionary<string, object>() |
|||
: new Dictionary<string, object>(extraProps); |
|||
|
|||
HasExtraPropertiesObjectExtendingExtensions |
|||
.MapExtraPropertiesTo<TSource, TDestination>( |
|||
source.ExtraProperties, |
|||
result |
|||
); |
|||
|
|||
return result; |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleAuditedEntityDto<TPrimaryKey> : ExtensibleCreationAuditedEntityDto<TPrimaryKey>, IAuditedObject |
|||
{ |
|||
/// <inheritdoc />
|
|||
public DateTime? LastModificationTime { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public Guid? LastModifierId { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
[Serializable] |
|||
public abstract class ExtensibleAuditedEntityDto : ExtensibleCreationAuditedEntityDto, IAuditedObject |
|||
{ |
|||
/// <inheritdoc />
|
|||
public DateTime? LastModificationTime { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public Guid? LastModifierId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
|||
/// It has the <see cref="Creator"/> and <see cref="LastModifier"/> objects as a DTOs represent the related user.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
|||
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleAuditedEntityWithUserDto<TPrimaryKey, TUserDto> : ExtensibleAuditedEntityDto<TPrimaryKey>, IAuditedObject<TUserDto> |
|||
{ |
|||
/// <inheritdoc />
|
|||
public TUserDto Creator { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public TUserDto LastModifier { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
|||
/// It has the <see cref="Creator"/> and <see cref="LastModifier"/> objects as a DTOs represent the related user.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleAuditedEntityWithUserDto<TUserDto> : ExtensibleAuditedEntityDto, |
|||
IAuditedObject<TUserDto> |
|||
{ |
|||
/// <inheritdoc />
|
|||
public TUserDto Creator { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public TUserDto LastModifier { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject"/> interface.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleCreationAuditedEntityDto<TPrimaryKey> : ExtensibleEntityDto<TPrimaryKey>, ICreationAuditedObject |
|||
{ |
|||
/// <inheritdoc />
|
|||
public DateTime CreationTime { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public Guid? CreatorId { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject"/> interface.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
[Serializable] |
|||
public abstract class ExtensibleCreationAuditedEntityDto : ExtensibleEntityDto, ICreationAuditedObject |
|||
{ |
|||
/// <inheritdoc />
|
|||
public DateTime CreationTime { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public Guid? CreatorId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject{TCreator}"/> interface.
|
|||
/// It has the <see cref="Creator"/> object as a DTO represents the user.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
|||
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleCreationAuditedEntityWithUserDto<TPrimaryKey, TUserDto> : ExtensibleCreationAuditedEntityDto<TPrimaryKey>, ICreationAuditedObject<TUserDto> |
|||
{ |
|||
public TUserDto Creator { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject{TCreator}"/> interface.
|
|||
/// It has the <see cref="Creator"/> object as a DTO represents the user.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleCreationAuditedEntityWithUserDto<TUserDto> : ExtensibleCreationAuditedEntityDto, |
|||
ICreationAuditedObject<TUserDto> |
|||
{ |
|||
public TUserDto Creator { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
[Serializable] |
|||
public abstract class ExtensibleEntityDto<TKey> : ExtensibleObject, IEntityDto<TKey> |
|||
{ |
|||
/// <summary>
|
|||
/// Id of the entity.
|
|||
/// </summary>
|
|||
public TKey Id { get; set; } |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return $"[DTO: {GetType().Name}] Id = {Id}"; |
|||
} |
|||
} |
|||
|
|||
[Serializable] |
|||
public abstract class ExtensibleEntityDto : ExtensibleObject, IEntityDto |
|||
{ |
|||
public override string ToString() |
|||
{ |
|||
return $"[DTO: {GetType().Name}]"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IFullAuditedObject"/> interface.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleFullAuditedEntityDto<TPrimaryKey> : ExtensibleAuditedEntityDto<TPrimaryKey>, IFullAuditedObject |
|||
{ |
|||
/// <inheritdoc />
|
|||
public bool IsDeleted { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public Guid? DeleterId { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public DateTime? DeletionTime { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IFullAuditedObject"/> interface.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
[Serializable] |
|||
public abstract class ExtensibleFullAuditedEntityDto : ExtensibleAuditedEntityDto, IFullAuditedObject |
|||
{ |
|||
/// <inheritdoc />
|
|||
public bool IsDeleted { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public Guid? DeleterId { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public DateTime? DeletionTime { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IFullAuditedObject{TUser}"/> interface.
|
|||
/// It has the <see cref="Creator"/>, <see cref="LastModifier"/> and <see cref="Deleter"/> objects as a DTOs represent the related user.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
|||
/// <typeparam name="TUserDto">Type of the User</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleFullAuditedEntityWithUserDto<TPrimaryKey, TUserDto> : ExtensibleFullAuditedEntityDto<TPrimaryKey>, IFullAuditedObject<TUserDto> |
|||
{ |
|||
/// <inheritdoc />
|
|||
public TUserDto Creator { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public TUserDto LastModifier { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public TUserDto Deleter { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class can be inherited by DTO classes to implement <see cref="IFullAuditedObject{TUser}"/> interface.
|
|||
/// It has the <see cref="Creator"/>, <see cref="LastModifier"/> and <see cref="Deleter"/> objects as a DTOs represent the related user.
|
|||
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
|||
/// </summary>
|
|||
/// <typeparam name="TUserDto">Type of the User</typeparam>
|
|||
[Serializable] |
|||
public abstract class ExtensibleFullAuditedEntityWithUserDto<TUserDto> : ExtensibleFullAuditedEntityDto, |
|||
IFullAuditedObject<TUserDto> |
|||
{ |
|||
/// <inheritdoc />
|
|||
public TUserDto Creator { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public TUserDto LastModifier { get; set; } |
|||
|
|||
/// <inheritdoc />
|
|||
public TUserDto Deleter { get; set; } |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
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>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,133 +0,0 @@ |
|||
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 static class EntityExtensionManager |
|||
{ |
|||
private static readonly Dictionary<Type, EntityExtensionInfo> ExtensionInfos; |
|||
|
|||
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>( |
|||
[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(entityType, () => new EntityExtensionInfo()); |
|||
|
|||
var propertyExtensionInfo = extensionInfo.Properties |
|||
.GetOrAdd(propertyName, () => new PropertyExtensionInfo(propertyType)); |
|||
|
|||
propertyExtensionInfo.Action = propertyBuildAction; |
|||
} |
|||
|
|||
/// <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 |
|||
{ |
|||
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; |
|||
} |
|||
|
|||
foreach (var propertyExtensionInfo in entityExtensionInfo.Properties) |
|||
{ |
|||
var propertyName = propertyExtensionInfo.Key; |
|||
var propertyType = propertyExtensionInfo.Value.PropertyType; |
|||
|
|||
/* Prevent multiple calls to the entityTypeBuilder.Property(...) method */ |
|||
if (entityTypeBuilder.Metadata.FindProperty(propertyName) != null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var property = entityTypeBuilder.Property( |
|||
propertyType, |
|||
propertyName |
|||
); |
|||
|
|||
propertyExtensionInfo.Value.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(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public static class EfCoreObjectExtensionInfoExtensions |
|||
{ |
|||
public static ObjectExtensionInfo MapEfCoreProperty<TProperty>( |
|||
[NotNull] this ObjectExtensionInfo objectExtensionInfo, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<PropertyBuilder> propertyBuildAction) |
|||
{ |
|||
return objectExtensionInfo.MapEfCoreProperty( |
|||
typeof(TProperty), |
|||
propertyName, |
|||
propertyBuildAction |
|||
); |
|||
} |
|||
|
|||
public static ObjectExtensionInfo MapEfCoreProperty( |
|||
[NotNull] this ObjectExtensionInfo objectExtensionInfo, |
|||
[NotNull] Type propertyType, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<PropertyBuilder> propertyBuildAction) |
|||
{ |
|||
Check.NotNull(objectExtensionInfo, nameof(objectExtensionInfo)); |
|||
|
|||
return objectExtensionInfo.AddOrUpdateProperty( |
|||
propertyType, |
|||
propertyName, |
|||
options => |
|||
{ |
|||
options.MapEfCore( |
|||
propertyBuildAction |
|||
); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public static class EfCoreObjectExtensionManagerExtensions |
|||
{ |
|||
public static ObjectExtensionManager MapEfCoreProperty<TEntity, TProperty>( |
|||
[NotNull] this ObjectExtensionManager objectExtensionManager, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<PropertyBuilder> propertyBuildAction = null) |
|||
where TEntity : IHasExtraProperties, IEntity |
|||
{ |
|||
return objectExtensionManager.MapEfCoreProperty( |
|||
typeof(TEntity), |
|||
typeof(TProperty), |
|||
propertyName, |
|||
propertyBuildAction |
|||
); |
|||
} |
|||
|
|||
public static ObjectExtensionManager MapEfCoreProperty( |
|||
[NotNull] this ObjectExtensionManager objectExtensionManager, |
|||
[NotNull] Type entityType, |
|||
[NotNull] Type propertyType, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<PropertyBuilder> propertyBuildAction = null) |
|||
{ |
|||
Check.NotNull(objectExtensionManager, nameof(objectExtensionManager)); |
|||
|
|||
return objectExtensionManager.AddOrUpdateProperty( |
|||
entityType, |
|||
propertyType, |
|||
propertyName, |
|||
options => |
|||
{ |
|||
options.MapEfCore( |
|||
propertyBuildAction |
|||
); |
|||
} |
|||
); |
|||
} |
|||
|
|||
public static void ConfigureEfCoreEntity( |
|||
[NotNull] this ObjectExtensionManager objectExtensionManager, |
|||
[NotNull] EntityTypeBuilder typeBuilder) |
|||
{ |
|||
Check.NotNull(objectExtensionManager, nameof(objectExtensionManager)); |
|||
Check.NotNull(typeBuilder, nameof(typeBuilder)); |
|||
|
|||
var objectExtension = objectExtensionManager.GetOrNull(typeBuilder.Metadata.ClrType); |
|||
if (objectExtension == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var property in objectExtension.GetProperties()) |
|||
{ |
|||
var efCoreMapping = property.GetEfCoreMappingOrNull(); |
|||
if (efCoreMapping == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
/* Prevent multiple calls to the entityTypeBuilder.Property(...) method */ |
|||
if (typeBuilder.Metadata.FindProperty(property.Name) != null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var propertyBuilder = typeBuilder.Property(property.Type, property.Name); |
|||
|
|||
efCoreMapping.PropertyBuildAction?.Invoke(propertyBuilder); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public static class EfCoreObjectExtensionPropertyInfoExtensions |
|||
{ |
|||
public const string EfCorePropertyConfigurationName = "EfCoreMapping"; |
|||
|
|||
[NotNull] |
|||
public static ObjectExtensionPropertyInfo MapEfCore( |
|||
[NotNull] this ObjectExtensionPropertyInfo propertyExtension, |
|||
[CanBeNull] Action<PropertyBuilder> propertyBuildAction = null) |
|||
{ |
|||
Check.NotNull(propertyExtension, nameof(propertyExtension)); |
|||
|
|||
propertyExtension.Configuration[EfCorePropertyConfigurationName] = |
|||
new ObjectExtensionPropertyInfoEfCoreMappingOptions( |
|||
propertyExtension, |
|||
propertyBuildAction |
|||
); |
|||
|
|||
return propertyExtension; |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public static ObjectExtensionPropertyInfoEfCoreMappingOptions GetEfCoreMappingOrNull( |
|||
[NotNull] this ObjectExtensionPropertyInfo propertyExtension) |
|||
{ |
|||
Check.NotNull(propertyExtension, nameof(propertyExtension)); |
|||
|
|||
return propertyExtension |
|||
.Configuration |
|||
.GetOrDefault(EfCorePropertyConfigurationName) |
|||
as ObjectExtensionPropertyInfoEfCoreMappingOptions; |
|||
} |
|||
|
|||
public static bool IsMappedToFieldForEfCore( |
|||
[NotNull] this ObjectExtensionPropertyInfo propertyExtension) |
|||
{ |
|||
Check.NotNull(propertyExtension, nameof(propertyExtension)); |
|||
|
|||
return propertyExtension |
|||
.Configuration |
|||
.ContainsKey(EfCorePropertyConfigurationName); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class ObjectExtensionPropertyInfoEfCoreMappingOptions |
|||
{ |
|||
[NotNull] |
|||
public ObjectExtensionPropertyInfo ExtensionProperty { get; } |
|||
|
|||
[NotNull] |
|||
public ObjectExtensionInfo ObjectExtension => ExtensionProperty.ObjectExtension; |
|||
|
|||
[CanBeNull] |
|||
public Action<PropertyBuilder> PropertyBuildAction { get; set; } |
|||
|
|||
public ObjectExtensionPropertyInfoEfCoreMappingOptions( |
|||
[NotNull] ObjectExtensionPropertyInfo extensionProperty, |
|||
[CanBeNull] Action<PropertyBuilder> propertyBuildAction = null) |
|||
{ |
|||
ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty)); |
|||
|
|||
PropertyBuildAction = propertyBuildAction; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,3 @@ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
[assembly: InternalsVisibleTo("Volo.Abp.ObjectExtending.Tests")] |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.ObjectExtending</AssemblyName> |
|||
<PackageId>Volo.Abp.ObjectExtending</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class AbpObjectExtendingModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
[Serializable] |
|||
public class ExtensibleObject : IHasExtraProperties |
|||
{ |
|||
public Dictionary<string, object> ExtraProperties { get; protected set; } |
|||
|
|||
public ExtensibleObject() |
|||
{ |
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,175 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public static class HasExtraPropertiesObjectExtendingExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Copies extra properties from the <paramref name="source"/> object
|
|||
/// to the <paramref name="destination"/> object.
|
|||
///
|
|||
/// Checks property definitions (over the <see cref="ObjectExtensionManager"/>)
|
|||
/// based on the <paramref name="definitionChecks"/> preference.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSource">Source class type</typeparam>
|
|||
/// <typeparam name="TDestination">Destination class type</typeparam>
|
|||
/// <param name="source">The source object</param>
|
|||
/// <param name="destination">The destination object</param>
|
|||
/// <param name="definitionChecks">
|
|||
/// Controls which properties to map.
|
|||
/// </param>
|
|||
public static void MapExtraPropertiesTo<TSource, TDestination>( |
|||
[NotNull] this TSource source, |
|||
[NotNull] TDestination destination, |
|||
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) |
|||
where TSource : IHasExtraProperties |
|||
where TDestination : IHasExtraProperties |
|||
{ |
|||
Check.NotNull(source, nameof(source)); |
|||
Check.NotNull(destination, nameof(destination)); |
|||
|
|||
MapExtraPropertiesTo( |
|||
typeof(TSource), |
|||
typeof(TDestination), |
|||
source.ExtraProperties, |
|||
destination.ExtraProperties, |
|||
definitionChecks |
|||
); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copies extra properties from the <paramref name="sourceDictionary"/> object
|
|||
/// to the <paramref name="destinationDictionary"/> object.
|
|||
///
|
|||
/// Checks property definitions (over the <see cref="ObjectExtensionManager"/>)
|
|||
/// based on the <paramref name="definitionChecks"/> preference.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSource">Source class type (for definition check)</typeparam>
|
|||
/// <typeparam name="TDestination">Destination class type (for definition check)</typeparam>
|
|||
/// <param name="sourceDictionary">The source dictionary object</param>
|
|||
/// <param name="destinationDictionary">The destination dictionary object</param>
|
|||
/// <param name="definitionChecks">
|
|||
/// Controls which properties to map.
|
|||
/// </param>
|
|||
public static void MapExtraPropertiesTo<TSource, TDestination>( |
|||
[NotNull] Dictionary<string, object> sourceDictionary, |
|||
[NotNull] Dictionary<string, object> destinationDictionary, |
|||
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) |
|||
where TSource : IHasExtraProperties |
|||
where TDestination : IHasExtraProperties |
|||
{ |
|||
MapExtraPropertiesTo( |
|||
typeof(TSource), |
|||
typeof(TDestination), |
|||
sourceDictionary, |
|||
destinationDictionary, |
|||
definitionChecks |
|||
); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copies extra properties from the <paramref name="sourceDictionary"/> object
|
|||
/// to the <paramref name="destinationDictionary"/> object.
|
|||
///
|
|||
/// Checks property definitions (over the <see cref="ObjectExtensionManager"/>)
|
|||
/// based on the <paramref name="definitionChecks"/> preference.
|
|||
/// </summary>
|
|||
/// <param name="sourceType">Source type (for definition check)</param>
|
|||
/// <param name="destinationType">Destination class type (for definition check)</param>
|
|||
/// <param name="sourceDictionary">The source dictionary object</param>
|
|||
/// <param name="destinationDictionary">The destination dictionary object</param>
|
|||
/// <param name="definitionChecks">
|
|||
/// Controls which properties to map.
|
|||
/// </param>
|
|||
public static void MapExtraPropertiesTo( |
|||
[NotNull] Type sourceType, |
|||
[NotNull] Type destinationType, |
|||
[NotNull] Dictionary<string, object> sourceDictionary, |
|||
[NotNull] Dictionary<string, object> destinationDictionary, |
|||
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) |
|||
{ |
|||
Check.AssignableTo<IHasExtraProperties>(sourceType, nameof(sourceType)); |
|||
Check.AssignableTo<IHasExtraProperties>(destinationType, nameof(destinationType)); |
|||
Check.NotNull(sourceDictionary, nameof(sourceDictionary)); |
|||
Check.NotNull(destinationDictionary, nameof(destinationDictionary)); |
|||
|
|||
var sourceObjectExtension = ObjectExtensionManager.Instance.GetOrNull(sourceType); |
|||
if (definitionChecks.HasFlag(MappingPropertyDefinitionChecks.Source) && |
|||
sourceObjectExtension == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var destinationObjectExtension = ObjectExtensionManager.Instance.GetOrNull(destinationType); |
|||
if (definitionChecks.HasFlag(MappingPropertyDefinitionChecks.Destination) && |
|||
destinationObjectExtension == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (definitionChecks == MappingPropertyDefinitionChecks.None) |
|||
{ |
|||
foreach (var keyValue in sourceDictionary) |
|||
{ |
|||
destinationDictionary[keyValue.Key] = keyValue.Value; |
|||
} |
|||
} |
|||
else if (definitionChecks == MappingPropertyDefinitionChecks.Source) |
|||
{ |
|||
Debug.Assert(sourceObjectExtension != null, nameof(sourceObjectExtension) + " != null"); |
|||
|
|||
foreach (var property in sourceObjectExtension.GetProperties()) |
|||
{ |
|||
if (!sourceDictionary.ContainsKey(property.Name)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
destinationDictionary[property.Name] = sourceDictionary[property.Name]; |
|||
} |
|||
} |
|||
else if (definitionChecks == MappingPropertyDefinitionChecks.Destination) |
|||
{ |
|||
Debug.Assert(destinationObjectExtension != null, nameof(destinationObjectExtension) + " != null"); |
|||
|
|||
foreach (var keyValue in sourceDictionary) |
|||
{ |
|||
if (!destinationObjectExtension.HasProperty(keyValue.Key)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
destinationDictionary[keyValue.Key] = keyValue.Value; |
|||
} |
|||
} |
|||
else if (definitionChecks == MappingPropertyDefinitionChecks.Both) |
|||
{ |
|||
Debug.Assert(sourceObjectExtension != null, nameof(sourceObjectExtension) + " != null"); |
|||
Debug.Assert(destinationObjectExtension != null, nameof(destinationObjectExtension) + " != null"); |
|||
|
|||
foreach (var property in sourceObjectExtension.GetProperties()) |
|||
{ |
|||
if (!sourceDictionary.ContainsKey(property.Name)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (!destinationObjectExtension.HasProperty(property.Name)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
destinationDictionary[property.Name] = sourceDictionary[property.Name]; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
throw new NotImplementedException(definitionChecks + " was not implemented!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
[Flags] |
|||
public enum MappingPropertyDefinitionChecks : byte |
|||
{ |
|||
/// <summary>
|
|||
/// No check. Copy all extra properties from the source to the destination.
|
|||
/// </summary>
|
|||
None = 0, |
|||
|
|||
/// <summary>
|
|||
/// Copy the extra properties defined for the source class.
|
|||
/// </summary>
|
|||
Source = 1, |
|||
|
|||
/// <summary>
|
|||
/// Copy the extra properties defined for the destination class.
|
|||
/// </summary>
|
|||
Destination = 2, |
|||
|
|||
/// <summary>
|
|||
/// Copy extra properties defined for both of the source and destination classes.
|
|||
/// </summary>
|
|||
Both = Source | Destination |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class ObjectExtensionInfo |
|||
{ |
|||
[NotNull] |
|||
public Type Type { get; } |
|||
|
|||
[NotNull] |
|||
protected Dictionary<string, ObjectExtensionPropertyInfo> Properties { get; } |
|||
|
|||
[NotNull] |
|||
public Dictionary<object, object> Configuration { get; } |
|||
|
|||
public ObjectExtensionInfo([NotNull] Type type) |
|||
{ |
|||
Type = Check.AssignableTo<IHasExtraProperties>(type, nameof(type)); |
|||
Properties = new Dictionary<string, ObjectExtensionPropertyInfo>(); |
|||
Configuration = new Dictionary<object, object>(); |
|||
} |
|||
|
|||
public virtual bool HasProperty(string propertyName) |
|||
{ |
|||
return Properties.ContainsKey(propertyName); |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual ObjectExtensionInfo AddOrUpdateProperty<TProperty>( |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<ObjectExtensionPropertyInfo> configureAction = null) |
|||
{ |
|||
return AddOrUpdateProperty( |
|||
typeof(TProperty), |
|||
propertyName, |
|||
configureAction |
|||
); |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual ObjectExtensionInfo AddOrUpdateProperty( |
|||
[NotNull] Type propertyType, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<ObjectExtensionPropertyInfo> configureAction = null) |
|||
{ |
|||
Check.NotNull(propertyType, nameof(propertyType)); |
|||
Check.NotNull(propertyName, nameof(propertyName)); |
|||
|
|||
var propertyInfo = Properties.GetOrAdd( |
|||
propertyName, |
|||
() => new ObjectExtensionPropertyInfo(this, propertyType, propertyName) |
|||
); |
|||
|
|||
configureAction?.Invoke(propertyInfo); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual ImmutableList<ObjectExtensionPropertyInfo> GetProperties() |
|||
{ |
|||
return Properties.Values.ToImmutableList(); |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public virtual ObjectExtensionPropertyInfo GetPropertyOrNull( |
|||
[NotNull] string propertyName) |
|||
{ |
|||
Check.NotNullOrEmpty(propertyName, nameof(propertyName)); |
|||
|
|||
return Properties.GetOrDefault(propertyName); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class ObjectExtensionManager |
|||
{ |
|||
public static ObjectExtensionManager Instance { get; set; } = new ObjectExtensionManager(); |
|||
|
|||
protected Dictionary<Type, ObjectExtensionInfo> ObjectsExtensions { get; } |
|||
|
|||
protected internal ObjectExtensionManager() |
|||
{ |
|||
ObjectsExtensions = new Dictionary<Type, ObjectExtensionInfo>(); |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual ObjectExtensionManager AddOrUpdate<TObject>( |
|||
[CanBeNull] Action<ObjectExtensionInfo> configureAction = null) |
|||
where TObject : IHasExtraProperties |
|||
{ |
|||
return AddOrUpdate(typeof(TObject), configureAction); |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual ObjectExtensionManager AddOrUpdate( |
|||
[NotNull] Type type, |
|||
[CanBeNull] Action<ObjectExtensionInfo> configureAction = null) |
|||
{ |
|||
Check.AssignableTo<IHasExtraProperties>(type, nameof(type)); |
|||
|
|||
var extensionInfo = ObjectsExtensions.GetOrAdd( |
|||
type, |
|||
() => new ObjectExtensionInfo(type) |
|||
); |
|||
|
|||
configureAction?.Invoke(extensionInfo); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public virtual ObjectExtensionInfo GetOrNull<TObject>() |
|||
where TObject : IHasExtraProperties |
|||
{ |
|||
return GetOrNull(typeof(TObject)); |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public virtual ObjectExtensionInfo GetOrNull([NotNull] Type type) |
|||
{ |
|||
Check.AssignableTo<IHasExtraProperties>(type, nameof(type)); |
|||
|
|||
return ObjectsExtensions.GetOrDefault(type); |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual ImmutableList<ObjectExtensionInfo> GetExtendedObjects() |
|||
{ |
|||
return ObjectsExtensions.Values.ToImmutableList(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public static class ObjectExtensionManagerExtensions |
|||
{ |
|||
public static ObjectExtensionManager AddOrUpdateProperty<TObject, TProperty>( |
|||
[NotNull] this ObjectExtensionManager objectExtensionManager, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<ObjectExtensionPropertyInfo> configureAction = null) |
|||
where TObject : IHasExtraProperties |
|||
{ |
|||
return objectExtensionManager.AddOrUpdateProperty( |
|||
typeof(TObject), |
|||
typeof(TProperty), |
|||
propertyName, |
|||
configureAction |
|||
); |
|||
} |
|||
|
|||
public static ObjectExtensionManager AddOrUpdateProperty( |
|||
[NotNull] this ObjectExtensionManager objectExtensionManager, |
|||
[NotNull] Type objectType, |
|||
[NotNull] Type propertyType, |
|||
[NotNull] string propertyName, |
|||
[CanBeNull] Action<ObjectExtensionPropertyInfo> configureAction = null) |
|||
{ |
|||
Check.NotNull(objectExtensionManager, nameof(objectExtensionManager)); |
|||
|
|||
return objectExtensionManager.AddOrUpdate( |
|||
objectType, |
|||
options => |
|||
{ |
|||
options.AddOrUpdateProperty( |
|||
propertyType, |
|||
propertyName, |
|||
configureAction |
|||
); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class ObjectExtensionPropertyInfo |
|||
{ |
|||
[NotNull] |
|||
public ObjectExtensionInfo ObjectExtension { get; } |
|||
|
|||
[NotNull] |
|||
public string Name { get; } |
|||
|
|||
[NotNull] |
|||
public Type Type { get; } |
|||
|
|||
[NotNull] |
|||
public Dictionary<object, object> Configuration { get; } |
|||
|
|||
public ObjectExtensionPropertyInfo( |
|||
[NotNull] ObjectExtensionInfo objectExtension, |
|||
[NotNull] Type type, |
|||
[NotNull] string name) |
|||
{ |
|||
ObjectExtension = Check.NotNull(objectExtension, nameof(objectExtension)); |
|||
Type = Check.NotNull(type, nameof(type)); |
|||
Name = Check.NotNull(name, nameof(name)); |
|||
|
|||
Configuration = new Dictionary<object, object>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.ObjectExtending.TestObjects; |
|||
using Volo.Abp.Testing; |
|||
using Xunit; |
|||
|
|||
namespace AutoMapper |
|||
{ |
|||
public class AbpAutoMapperExtensibleDtoExtensions_Tests : AbpIntegratedTest<AutoMapperTestModule> |
|||
{ |
|||
private readonly Volo.Abp.ObjectMapping.IObjectMapper _objectMapper; |
|||
|
|||
public AbpAutoMapperExtensibleDtoExtensions_Tests() |
|||
{ |
|||
_objectMapper = ServiceProvider.GetRequiredService<Volo.Abp.ObjectMapping.IObjectMapper>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void MapExtraPropertiesTo_Should_Only_Map_Defined_Properties_By_Default() |
|||
{ |
|||
var person = new ExtensibleTestPerson() |
|||
.SetProperty("Name", "John") |
|||
.SetProperty("Age", 42) |
|||
.SetProperty("ChildCount", 2) |
|||
.SetProperty("Sex", "male"); |
|||
|
|||
var personDto = new ExtensibleTestPersonDto() |
|||
.SetProperty("ExistingDtoProperty", "existing-value"); |
|||
|
|||
_objectMapper.Map(person, personDto); |
|||
|
|||
personDto.GetProperty<string>("Name").ShouldBe("John"); //Defined in both classes
|
|||
personDto.HasProperty("Age").ShouldBeFalse(); //Not defined on the destination
|
|||
personDto.HasProperty("ChildCount").ShouldBeFalse(); //Not defined in the source
|
|||
personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
|
|||
personDto.GetProperty<string>("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public abstract class AbpObjectExtendingTestBase : AbpIntegratedTest<AbpObjectExtendingTestModule> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.ObjectExtending.TestObjects; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpObjectExtendingModule), |
|||
typeof(AbpTestBaseModule) |
|||
)] |
|||
public class AbpObjectExtendingTestModule : AbpModule |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
ObjectExtensionManager.Instance |
|||
.AddOrUpdateProperty<ExtensibleTestPerson, string>("Name") |
|||
.AddOrUpdateProperty<ExtensibleTestPerson, int>("Age") |
|||
.AddOrUpdateProperty<ExtensibleTestPersonDto, string>("Name") |
|||
.AddOrUpdateProperty<ExtensibleTestPersonDto, int>("ChildCount"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
using Shouldly; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.ObjectExtending.TestObjects; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class HasExtraPropertiesObjectExtendingExtensions_Tests : AbpObjectExtendingTestBase |
|||
{ |
|||
private readonly ExtensibleTestPerson _person; |
|||
private readonly ExtensibleTestPersonDto _personDto; |
|||
|
|||
public HasExtraPropertiesObjectExtendingExtensions_Tests() |
|||
{ |
|||
_person = new ExtensibleTestPerson() |
|||
.SetProperty("Name", "John") |
|||
.SetProperty("Age", 42) |
|||
.SetProperty("ChildCount", 2) |
|||
.SetProperty("Sex", "male"); |
|||
|
|||
_personDto = new ExtensibleTestPersonDto() |
|||
.SetProperty("ExistingDtoProperty", "existing-value"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void MapExtraPropertiesTo_Should_Only_Map_Defined_Properties_By_Default() |
|||
{ |
|||
_person.MapExtraPropertiesTo(_personDto); |
|||
|
|||
_personDto.GetProperty<string>("Name").ShouldBe("John"); //Defined in both classes
|
|||
_personDto.HasProperty("Age").ShouldBeFalse(); //Not defined on the destination
|
|||
_personDto.HasProperty("ChildCount").ShouldBeFalse(); //Not defined in the source
|
|||
_personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
|
|||
_personDto.GetProperty<string>("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values
|
|||
} |
|||
|
|||
[Fact] |
|||
public void MapExtraPropertiesTo_Should_Only_Map_Source_Defined_Properties_If_Requested() |
|||
{ |
|||
_person.MapExtraPropertiesTo(_personDto, MappingPropertyDefinitionChecks.Source); |
|||
|
|||
_personDto.GetProperty<string>("Name").ShouldBe("John"); //Defined in both classes
|
|||
_personDto.GetProperty<int>("Age").ShouldBe(42); //Defined in source
|
|||
_personDto.HasProperty("ChildCount").ShouldBeFalse(); //Not defined in the source
|
|||
_personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
|
|||
_personDto.GetProperty<string>("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values
|
|||
} |
|||
|
|||
[Fact] |
|||
public void MapExtraPropertiesTo_Should_Only_Map_Destination_Defined_Properties_If_Requested() |
|||
{ |
|||
_person.MapExtraPropertiesTo(_personDto, MappingPropertyDefinitionChecks.Destination); |
|||
|
|||
_personDto.GetProperty<string>("Name").ShouldBe("John"); //Defined in both classes
|
|||
_personDto.GetProperty<int>("ChildCount").ShouldBe(2); //Defined in destination
|
|||
_personDto.HasProperty("Age").ShouldBeFalse(); //Not defined in destination
|
|||
_personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
|
|||
_personDto.GetProperty<string>("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values
|
|||
} |
|||
|
|||
[Fact] |
|||
public void MapExtraPropertiesTo_Should_Copy_all_With_No_Property_Definition_Check() |
|||
{ |
|||
_person.MapExtraPropertiesTo(_personDto, MappingPropertyDefinitionChecks.None); |
|||
|
|||
_personDto.GetProperty<string>("Name").ShouldBe("John"); |
|||
_personDto.GetProperty<int>("Age").ShouldBe(42); |
|||
_personDto.GetProperty<int>("ChildCount").ShouldBe(2); |
|||
_personDto.GetProperty<string>("Sex").ShouldBe("male"); |
|||
_personDto.GetProperty<string>("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
using System.Linq; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class ObjectExtensionManager_Tests |
|||
{ |
|||
private readonly ObjectExtensionManager _objectExtensionManager; |
|||
|
|||
public ObjectExtensionManager_Tests() |
|||
{ |
|||
_objectExtensionManager = new ObjectExtensionManager(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Add_Same_Property_Multiple_Times() |
|||
{ |
|||
_objectExtensionManager |
|||
.AddOrUpdateProperty<MyExtensibleObject, string>("TestProp") |
|||
.AddOrUpdateProperty<MyExtensibleObject, string>("TestProp"); |
|||
|
|||
var objectExtension = _objectExtensionManager.GetOrNull<MyExtensibleObject>(); |
|||
objectExtension.ShouldNotBeNull(); |
|||
|
|||
var properties = objectExtension.GetProperties(); |
|||
properties.Count.ShouldBe(1); |
|||
properties.FirstOrDefault(p => p.Name == "TestProp").ShouldNotBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Update_Property_Configuration() |
|||
{ |
|||
_objectExtensionManager |
|||
.AddOrUpdateProperty<MyExtensibleObject, string>( |
|||
"TestProp", |
|||
options => |
|||
{ |
|||
options.Configuration["TestConfig1"] = "TestConfig1-Value"; |
|||
} |
|||
).AddOrUpdateProperty<MyExtensibleObject, string>( |
|||
"TestProp", |
|||
options => |
|||
{ |
|||
options.Configuration["TestConfig2"] = "TestConfig2-Value"; |
|||
} |
|||
); |
|||
|
|||
var objectExtension = _objectExtensionManager.GetOrNull<MyExtensibleObject>(); |
|||
objectExtension.ShouldNotBeNull(); |
|||
|
|||
var property = objectExtension.GetPropertyOrNull("TestProp"); |
|||
property.ShouldNotBeNull(); |
|||
property.Configuration["TestConfig1"].ShouldBe("TestConfig1-Value"); |
|||
property.Configuration["TestConfig2"].ShouldBe("TestConfig2-Value"); |
|||
} |
|||
|
|||
private class MyExtensibleObject : ExtensibleObject |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.ObjectExtending.TestObjects |
|||
{ |
|||
public class ExtensibleTestPerson : ExtensibleObject |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.ObjectExtending.TestObjects |
|||
{ |
|||
public class ExtensibleTestPersonDto : ExtensibleObject |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.ObjectExtending; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace MyCompanyName.MyProjectName |
|||
{ |
|||
public static class MyProjectNameDtoExtensions |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public static void Configure() |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
/* You can add extension properties to DTOs |
|||
* defined in the depended modules. |
|||
* |
|||
* Example: |
|||
* |
|||
* ObjectExtensionManager.Instance |
|||
* .AddOrUpdateProperty<IdentityRoleDto, string>("Title"); |
|||
* |
|||
* See the documentation for more: |
|||
* https://docs.abp.io/en/abp/latest/Object-Extensions
|
|||
*/ |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.ObjectExtending; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace MyCompanyName.MyProjectName.MongoDB |
|||
{ |
|||
public static class MyProjectNameEntityExtensions |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public static void Configure() |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
/* You can extension properties to entities |
|||
* defined in the depended modules. |
|||
* |
|||
* Example: |
|||
* |
|||
* ObjectExtensionManager.Instance |
|||
* .AddOrUpdateProperty<IdentityRole, string>("Title"); |
|||
* |
|||
* See the documentation for more: |
|||
* https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities
|
|||
*/ |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue