mirror of https://github.com/abpframework/abp.git
15 changed files with 184 additions and 69 deletions
@ -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<string, PropertyInfo> CachedObjectProperties = |
||||
|
new ConcurrentDictionary<string, PropertyInfo>(); |
||||
|
|
||||
|
public static void TrySetProperty<TObject, TValue>( |
||||
|
TObject obj, |
||||
|
Expression<Func<TObject, TValue>> propertySelector, |
||||
|
Func<TValue> valueFactory, |
||||
|
params Type[] ignoreAttributeTypes) |
||||
|
{ |
||||
|
TrySetProperty(obj, propertySelector, x => valueFactory(), ignoreAttributeTypes); |
||||
|
} |
||||
|
|
||||
|
public static void TrySetProperty<TObject, TValue>( |
||||
|
TObject obj, |
||||
|
Expression<Func<TObject, TValue>> propertySelector, |
||||
|
Func<TObject, TValue> 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<MemberExpression>(); |
||||
|
|
||||
|
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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue