diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs index 1863c8f938..841a7f63de 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs @@ -48,7 +48,7 @@ namespace Volo.Abp.Auditing if (objectWithCreationTime.CreationTime == default) { - objectWithCreationTime.CreationTime = Clock.Now; + ObjectHelper.TrySetProperty(objectWithCreationTime, x => x.CreationTime, () => Clock.Now); } } @@ -82,7 +82,7 @@ namespace Volo.Abp.Auditing return; } - mayHaveCreatorObject.CreatorId = CurrentUser.Id; + ObjectHelper.TrySetProperty(mayHaveCreatorObject, x => x.CreatorId, () => CurrentUser.Id); } else if (targetObject is IMustHaveCreator mustHaveCreatorObject) { @@ -91,7 +91,7 @@ namespace Volo.Abp.Auditing return; } - mustHaveCreatorObject.CreatorId = CurrentUser.Id.Value; + ObjectHelper.TrySetProperty(mustHaveCreatorObject, x => x.CreatorId, () => CurrentUser.Id.Value); } } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasCreationTime.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasCreationTime.cs index 58e64cabef..07e9a7525b 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasCreationTime.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasCreationTime.cs @@ -10,6 +10,6 @@ namespace Volo.Abp.Auditing /// /// Creation time. /// - DateTime CreationTime { get; set; } + DateTime CreationTime { get; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMayHaveCreator.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMayHaveCreator.cs index 7960827d77..4fb4f9f49d 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMayHaveCreator.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMayHaveCreator.cs @@ -9,7 +9,7 @@ namespace Volo.Abp.Auditing /// Reference to the creator. /// [CanBeNull] - TCreator Creator { get; set; } + TCreator Creator { get; } } /// @@ -20,6 +20,6 @@ namespace Volo.Abp.Auditing /// /// Id of the creator. /// - Guid? CreatorId { get; set; } + Guid? CreatorId { get; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMustHaveCreator.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMustHaveCreator.cs index 87671f97d6..14c6050dcd 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMustHaveCreator.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMustHaveCreator.cs @@ -12,7 +12,7 @@ namespace Volo.Abp.Auditing /// Reference to the creator. /// [NotNull] - TCreator Creator { get; set; } + TCreator Creator { get; } } /// @@ -23,6 +23,6 @@ namespace Volo.Abp.Auditing /// /// Id of the creator. /// - Guid CreatorId { get; set; } + Guid CreatorId { get; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/ObjectHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/ObjectHelper.cs new file mode 100644 index 0000000000..643c950b8a --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/ObjectHelper.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Volo.Abp +{ + public static class ObjectHelper + { + private static readonly ConcurrentDictionary CachedObjectProperties = + new ConcurrentDictionary(); + + public static void TrySetProperty( + TObject obj, + Expression> propertySelector, + Func valueFactory, + params Type[] ignoreAttributeTypes) + { + TrySetProperty(obj, propertySelector, x => valueFactory(), ignoreAttributeTypes); + } + + public static void TrySetProperty( + TObject obj, + Expression> propertySelector, + Func valueFactory, + params Type[] ignoreAttributeTypes) + { + var property = CachedObjectProperties.GetOrAdd( + $"{obj.GetType().FullName}-{propertySelector}{(ignoreAttributeTypes != null ? string.Join("-", ignoreAttributeTypes.Select(x => x.FullName)) : "")}", () => + { + if (propertySelector.Body.NodeType != ExpressionType.MemberAccess) + { + return null; + } + + var memberExpression = propertySelector.Body.As(); + + var propertyInfo = obj.GetType().GetProperties().FirstOrDefault(x => x.Name == memberExpression.Member.Name && x.DeclaringType == obj.GetType()); + if (propertyInfo == null || propertyInfo.GetSetMethod(true) == null) + { + return null; + } + + if (ignoreAttributeTypes != null && ignoreAttributeTypes.Any(ignoreAttribute => propertyInfo.IsDefined(ignoreAttribute, true))) + { + return null; + } + + return propertyInfo; + }); + + property?.SetValue(obj, valueFactory(obj)); + } + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedAggregateRootWithUser.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedAggregateRootWithUser.cs index 477f8750fd..69e6961630 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedAggregateRootWithUser.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedAggregateRootWithUser.cs @@ -12,7 +12,7 @@ namespace Volo.Abp.Domain.Entities.Auditing where TUser : IEntity { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -28,7 +28,7 @@ namespace Volo.Abp.Domain.Entities.Auditing where TUser : IEntity { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -44,4 +44,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedEntityWithUser.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedEntityWithUser.cs index cc54209595..1aa12a17da 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedEntityWithUser.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/AuditedEntityWithUser.cs @@ -12,7 +12,7 @@ namespace Volo.Abp.Domain.Entities.Auditing where TUser : IEntity { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -28,7 +28,7 @@ namespace Volo.Abp.Domain.Entities.Auditing where TUser : IEntity { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -44,4 +44,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs index 084c907606..4e42738b85 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs @@ -10,10 +10,10 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedAggregateRoot : AggregateRoot, ICreationAuditedObject { /// - public virtual DateTime CreationTime { get; set; } + public virtual DateTime CreationTime { get; protected set; } /// - public virtual Guid? CreatorId { get; set; } + public virtual Guid? CreatorId { get; protected set; } } /// diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRootWithUser.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRootWithUser.cs index 7d491d7be1..f576ba8452 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRootWithUser.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedAggregateRootWithUser.cs @@ -11,7 +11,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedAggregateRootWithUser : CreationAuditedAggregateRoot, ICreationAuditedObject { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } } /// @@ -23,7 +23,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedAggregateRootWithUser : CreationAuditedAggregateRoot, ICreationAuditedObject { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } protected CreationAuditedAggregateRootWithUser() { @@ -36,4 +36,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntity.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntity.cs index 41802c78b7..d039318fc1 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntity.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntity.cs @@ -10,10 +10,10 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedEntity : Entity, ICreationAuditedObject { /// - public virtual DateTime CreationTime { get; set; } + public virtual DateTime CreationTime { get; protected set; } /// - public virtual Guid? CreatorId { get; set; } + public virtual Guid? CreatorId { get; protected set; } } /// @@ -24,10 +24,10 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedEntity : Entity, ICreationAuditedObject { /// - public virtual DateTime CreationTime { get; set; } + public virtual DateTime CreationTime { get; protected set; } /// - public virtual Guid? CreatorId { get; set; } + public virtual Guid? CreatorId { get; protected set; } protected CreationAuditedEntity() { @@ -40,4 +40,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntityWithUser.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntityWithUser.cs index 45d844efa7..c193f9486c 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntityWithUser.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/CreationAuditedEntityWithUser.cs @@ -11,7 +11,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedEntityWithUser : CreationAuditedEntity, ICreationAuditedObject { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } } /// @@ -23,7 +23,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public abstract class CreationAuditedEntityWithUser : CreationAuditedEntity, ICreationAuditedObject { /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } protected CreationAuditedEntityWithUser() { @@ -36,4 +36,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedAggregateRootWithUser.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedAggregateRootWithUser.cs index c6bfefbb23..312c3abd51 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedAggregateRootWithUser.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedAggregateRootWithUser.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public virtual TUser Deleter { get; set; } /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -34,7 +34,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public virtual TUser Deleter { get; set; } /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -50,4 +50,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedEntityWithUser.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedEntityWithUser.cs index f7ec2b672f..1ce198304d 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedEntityWithUser.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Auditing/FullAuditedEntityWithUser.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.Domain.Entities.Auditing public virtual TUser Deleter { get; set; } /// - public virtual TUser Creator { get; set; } + public virtual TUser Creator { get; protected set; } /// public virtual TUser LastModifier { get; set; } @@ -34,8 +34,8 @@ namespace Volo.Abp.Domain.Entities.Auditing public virtual TUser Deleter { get; set; } /// - public virtual TUser Creator { get; set; } - + public virtual TUser Creator { get; protected set; } + /// public virtual TUser LastModifier { get; set; } @@ -50,4 +50,4 @@ namespace Volo.Abp.Domain.Entities.Auditing } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index 65f2992257..0175498c9f 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -13,11 +13,8 @@ namespace Volo.Abp.Domain.Entities /// /// Some helper methods for entities. /// - public static class EntityHelper + public static partial class EntityHelper { - private static readonly ConcurrentDictionary CachedIdProperties = - new ConcurrentDictionary(); - public static bool EntityEquals(IEntity entity1, IEntity entity2) { if (entity1 == null || entity2 == null) @@ -77,7 +74,7 @@ namespace Volo.Abp.Domain.Entities { var entity1Key = entity1Keys[i]; var entity2Key = entity2Keys[i]; - + if (entity1Key == null) { if (entity2Key == null) @@ -89,13 +86,13 @@ namespace Volo.Abp.Domain.Entities //entity2Key is not null! return false; } - + if (entity2Key == null) { //entity1Key was not null! return false; } - + if (TypeHelper.IsDefaultValue(entity1Key) && TypeHelper.IsDefaultValue(entity2Key)) { return false; @@ -241,30 +238,13 @@ namespace Volo.Abp.Domain.Entities Func idFactory, bool checkForDisableIdGenerationAttribute = false) { - var property = CachedIdProperties.GetOrAdd( - $"{entity.GetType().FullName}-{checkForDisableIdGenerationAttribute}", () => - { - var idProperty = entity - .GetType() - .GetProperties() - .FirstOrDefault(x => x.Name == nameof(entity.Id) && - x.GetSetMethod(true) != null); - - if (idProperty == null) - { - return null; - } - - if (checkForDisableIdGenerationAttribute && - idProperty.IsDefined(typeof(DisableIdGenerationAttribute), true)) - { - return null; - } - - return idProperty; - }); - - property?.SetValue(entity, idFactory()); + ObjectHelper.TrySetProperty( + entity, + x => x.Id, + idFactory, + checkForDisableIdGenerationAttribute + ? new Type[] { typeof(DisableIdGenerationAttribute) } + : new Type[] { }); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/ObjectHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/ObjectHelper_Tests.cs new file mode 100644 index 0000000000..facefbe915 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/ObjectHelper_Tests.cs @@ -0,0 +1,78 @@ +using System.Runtime.Serialization; +using Shouldly; +using Xunit; + +namespace Volo.Abp +{ + public class ObjectHelper_Tests + { + [Fact] + public void TrySetProperty_Test() + { + var testClass = new MyClass(); + + ObjectHelper.TrySetProperty(testClass, x => x.Name, () => "NewName"); + testClass.Name.ShouldBe("NewName"); + + ObjectHelper.TrySetProperty(testClass, x => x.Name2, () => "NewName2"); + testClass.Name2.ShouldBe("NewName2"); + + ObjectHelper.TrySetProperty(testClass, x => x.Name3, () => "NewName3"); + testClass.Name3.ShouldBe("NewName3"); + + ObjectHelper.TrySetProperty(testClass, x => x.Name4, () => "NewName4"); + testClass.Name4.ShouldBe("Name4"); // readonly + + ObjectHelper.TrySetProperty(testClass, x => x.Name5, () => "NewName5", ignoreAttributeTypes: typeof(IgnoreDataMemberAttribute)); + testClass.Name5.ShouldNotBe("NewName5"); // ignore by attribute + + ObjectHelper.TrySetProperty(testClass, x => x.ChildClass.Name, () => "NewChildName"); + testClass.ChildClass.Name.ShouldNotBe("NewChildName"); + + ObjectHelper.TrySetProperty(testClass.ChildClass, x => x.Name, () => "NewChildName"); + testClass.ChildClass.Name.ShouldBe("NewChildName"); + + ObjectHelper.TrySetProperty(testClass.ChildClass, x => x, () => new MyChildClass + { + Name = "NewChildName" + }); + testClass.ChildClass.Name.ShouldBe("NewChildName"); + } + + class MyClass + { + public string Name { get; set; } + + public string Name2 { get; protected set; } + + public string Name3 { get; private set; } + + public string Name4 { get; } + + [IgnoreDataMember] + public string Name5 { get; } + + public MyChildClass ChildClass { get; set; } + + public MyClass() + { + Name = "Name"; + Name2 = "Name2"; + Name3 = "Name3"; + Name4 = "Name4"; + Name5 = "Name5"; + ChildClass = new MyChildClass(); + } + } + + class MyChildClass + { + public string Name { get; set; } + + public MyChildClass() + { + Name = "Name"; + } + } + } +}