Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit daf0eb9

Browse files
committed
Allow adding attributes to property at runtime, gets reflected in AllAttributes or FirstAttribute.
1 parent e1bb688 commit daf0eb9

12 files changed

+129
-40
lines changed

lib/tests/ServiceStack.Client.dll

0 Bytes
Binary file not shown.

lib/tests/ServiceStack.Client.pdb

-28 KB
Binary file not shown.
512 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

lib/tests/ServiceStack.Common.dll

0 Bytes
Binary file not shown.

lib/tests/ServiceStack.Common.pdb

-18 KB
Binary file not shown.

lib/tests/ServiceStack.Interfaces.dll

0 Bytes
Binary file not shown.

lib/tests/ServiceStack.Server.dll

2 KB
Binary file not shown.

lib/tests/ServiceStack.Text.dll

1 KB
Binary file not shown.

lib/tests/ServiceStack.dll

3.5 KB
Binary file not shown.

src/ServiceStack.Text/Common/JsWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,13 @@ public WriteObjectDelegate GetValueTypeToStringMethod(Type type)
313313
return Serializer.WriteDecimal;
314314

315315
if (type.IsUnderlyingEnum())
316-
return type.FirstAttribute<FlagsAttribute>(false) != null
316+
return type.FirstAttribute<FlagsAttribute>() != null
317317
? (WriteObjectDelegate)Serializer.WriteEnumFlags
318318
: Serializer.WriteEnum;
319319

320320
Type nullableType;
321321
if ((nullableType = Nullable.GetUnderlyingType(type)) != null && nullableType.IsEnum())
322-
return nullableType.FirstAttribute<FlagsAttribute>(false) != null
322+
return nullableType.FirstAttribute<FlagsAttribute>() != null
323323
? (WriteObjectDelegate)Serializer.WriteEnumFlags
324324
: Serializer.WriteEnum;
325325

src/ServiceStack.Text/ReflectionExtensions.cs

Lines changed: 127 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//
1212

1313
using System;
14+
using System.Collections.Concurrent;
1415
using System.Collections.Generic;
1516
using System.ComponentModel;
1617
using System.Linq;
@@ -181,7 +182,7 @@ public static bool IsNumericType(this Type type)
181182
}
182183
return false;
183184
}
184-
185+
185186
public static bool IsIntegerType(this Type type)
186187
{
187188
if (type == null) return false;
@@ -233,7 +234,7 @@ public static Type GetTypeWithGenericInterfaceOf(this Type type, Type genericInt
233234
{
234235
foreach (var t in type.GetTypeInterfaces())
235236
{
236-
if (t.IsGeneric() && t.GetGenericTypeDefinition() == genericInterfaceType)
237+
if (t.IsGeneric() && t.GetGenericTypeDefinition() == genericInterfaceType)
237238
return t;
238239
}
239240

@@ -399,7 +400,7 @@ public static EmptyCtorDelegate GetConstructorMethodToCache(Type type)
399400
{
400401
var genericArgs = type.GetGenericArguments();
401402
var typeArgs = new Type[genericArgs.Length];
402-
for (var i=0; i<genericArgs.Length; i++)
403+
for (var i = 0; i < genericArgs.Length; i++)
403404
typeArgs[i] = typeof(object);
404405

405406
var realizedType = type.MakeGenericType(typeArgs);
@@ -488,7 +489,7 @@ public static object CreateInstance(this Type type)
488489
}
489490

490491
public static T CreateInstance<T>(this Type type)
491-
{
492+
{
492493
var ctorFn = GetConstructorMethod(type);
493494
return (T)ctorFn();
494495
}
@@ -549,9 +550,9 @@ public static PropertyInfo[] GetSerializableProperties(this Type type)
549550
if (type.IsDto())
550551
{
551552
return !Env.IsMono
552-
? publicReadableProperties.Where(attr =>
553+
? publicReadableProperties.Where(attr =>
553554
attr.IsDefined(typeof(DataMemberAttribute), false)).ToArray()
554-
: publicReadableProperties.Where(attr =>
555+
: publicReadableProperties.Where(attr =>
555556
attr.AllAttributes().Any(x => x.GetType().Name == DataMember)).ToArray();
556557
}
557558

@@ -561,18 +562,19 @@ public static PropertyInfo[] GetSerializableProperties(this Type type)
561562

562563
public static FieldInfo[] GetSerializableFields(this Type type)
563564
{
564-
if (type.IsDto()) {
565+
if (type.IsDto())
566+
{
565567
return new FieldInfo[0];
566568
}
567-
569+
568570
var publicFields = type.GetPublicFields();
569571

570572
// else return those properties that are not decorated with IgnoreDataMember
571573
return publicFields.Where(prop => prop.AllAttributes().All(attr => attr.GetType().Name != IgnoreDataMember)).ToArray();
572574
}
573-
574-
#if !SILVERLIGHT && !MONOTOUCH
575-
static readonly Dictionary<Type, FastMember.TypeAccessor> typeAccessorMap
575+
576+
#if !SILVERLIGHT && !MONOTOUCH
577+
static readonly Dictionary<Type, FastMember.TypeAccessor> typeAccessorMap
576578
= new Dictionary<Type, FastMember.TypeAccessor>();
577579
#endif
578580

@@ -626,7 +628,8 @@ public static DataContractAttribute GetWeakDataContract(this Type type)
626628
typeAccessorMap[attrType] = accessor = FastMember.TypeAccessor.Create(attr.GetType());
627629
}
628630

629-
return new DataContractAttribute {
631+
return new DataContractAttribute
632+
{
630633
Name = (string)accessor[attr, "Name"],
631634
Namespace = (string)accessor[attr, "Namespace"],
632635
};
@@ -648,8 +651,9 @@ public static DataMemberAttribute GetWeakDataMember(this PropertyInfo pi)
648651
typeAccessorMap[attrType] = accessor = FastMember.TypeAccessor.Create(attr.GetType());
649652
}
650653

651-
var newAttr = new DataMemberAttribute {
652-
Name = (string) accessor[attr, "Name"],
654+
var newAttr = new DataMemberAttribute
655+
{
656+
Name = (string)accessor[attr, "Name"],
653657
EmitDefaultValue = (bool)accessor[attr, "EmitDefaultValue"],
654658
IsRequired = (bool)accessor[attr, "IsRequired"],
655659
};
@@ -929,12 +933,97 @@ public static PropertyInfo[] AllProperties(this Type type)
929933
#endif
930934
}
931935

936+
static readonly Dictionary<string, List<Attribute>> propertyAttributesMap
937+
= new Dictionary<string, List<Attribute>>();
938+
939+
internal static string UniqueKey(this PropertyInfo pi)
940+
{
941+
if (pi.DeclaringType == null)
942+
throw new ArgumentException("Property '{0}' has no DeclaringType".Fmt(pi.Name));
943+
944+
return pi.DeclaringType.Namespace + "." + pi.DeclaringType.Name + "." + pi.Name;
945+
}
946+
947+
public static void AddAttributes(this Type type, params Attribute[] attrs)
948+
{
949+
#if NETFX_CORE || SILVERLIGHT
950+
throw new NotSupportedException("Adding Attributes at runtime is not supported on this platform");
951+
#else
952+
TypeDescriptor.AddAttributes(type, attrs);
953+
#endif
954+
}
955+
956+
/// <summary>
957+
/// Add a Property attribute at runtime.
958+
/// <para>Not threadsafe, should only add attributes on Startup.</para>
959+
/// </summary>
960+
public static void AddAttributes(this PropertyInfo propertyInfo, params Attribute[] attrs)
961+
{
962+
List<Attribute> propertyAttrs;
963+
var key = propertyInfo.UniqueKey();
964+
if (!propertyAttributesMap.TryGetValue(key, out propertyAttrs))
965+
{
966+
propertyAttributesMap[key] = propertyAttrs = new List<Attribute>();
967+
}
968+
969+
propertyAttrs.AddRange(attrs);
970+
}
971+
972+
/// <summary>
973+
/// Add a Property attribute at runtime.
974+
/// <para>Not threadsafe, should only add attributes on Startup.</para>
975+
/// </summary>
976+
public static void ReplaceAttribute(this PropertyInfo propertyInfo, Attribute attr)
977+
{
978+
var key = propertyInfo.UniqueKey();
979+
980+
List<Attribute> propertyAttrs;
981+
if (!propertyAttributesMap.TryGetValue(key, out propertyAttrs))
982+
{
983+
propertyAttributesMap[key] = propertyAttrs = new List<Attribute>();
984+
}
985+
986+
propertyAttrs.RemoveAll(x => x.GetType() == attr.GetType());
987+
988+
propertyAttrs.Add(attr);
989+
}
990+
991+
public static List<TAttr> GetAttributes<TAttr>(this PropertyInfo propertyInfo)
992+
{
993+
List<Attribute> propertyAttrs;
994+
return !propertyAttributesMap.TryGetValue(propertyInfo.UniqueKey(), out propertyAttrs)
995+
? new List<TAttr>()
996+
: propertyAttrs.OfType<TAttr>().ToList();
997+
}
998+
999+
public static List<Attribute> GetAttributes(this PropertyInfo propertyInfo)
1000+
{
1001+
List<Attribute> propertyAttrs;
1002+
return !propertyAttributesMap.TryGetValue(propertyInfo.UniqueKey(), out propertyAttrs)
1003+
? new List<Attribute>()
1004+
: propertyAttrs.ToList();
1005+
}
1006+
1007+
public static List<Attribute> GetAttributes(this PropertyInfo propertyInfo, Type attrType)
1008+
{
1009+
List<Attribute> propertyAttrs;
1010+
return !propertyAttributesMap.TryGetValue(propertyInfo.UniqueKey(), out propertyAttrs)
1011+
? new List<Attribute>()
1012+
: propertyAttrs.Where(x => x.GetType() == attrType).ToList();
1013+
}
1014+
9321015
public static object[] AllAttributes(this PropertyInfo propertyInfo)
9331016
{
9341017
#if NETFX_CORE
9351018
return propertyInfo.GetCustomAttributes(true).ToArray();
9361019
#else
937-
return propertyInfo.GetCustomAttributes(true);
1020+
var attrs = propertyInfo.GetCustomAttributes(true);
1021+
var runtimeAttrs = propertyInfo.GetAttributes();
1022+
if (runtimeAttrs.Count == 0)
1023+
return attrs;
1024+
1025+
runtimeAttrs.AddRange(attrs.Cast<Attribute>());
1026+
return runtimeAttrs.Cast<object>().ToArray();
9381027
#endif
9391028
}
9401029

@@ -943,7 +1032,13 @@ public static object[] AllAttributes(this PropertyInfo propertyInfo, Type attrTy
9431032
#if NETFX_CORE
9441033
return propertyInfo.GetCustomAttributes(true).Where(x => x.GetType() == attrType).ToArray();
9451034
#else
946-
return propertyInfo.GetCustomAttributes(attrType, true);
1035+
var attrs = propertyInfo.GetCustomAttributes(attrType, true);
1036+
var runtimeAttrs = propertyInfo.GetAttributes(attrType);
1037+
if (runtimeAttrs.Count == 0)
1038+
return attrs;
1039+
1040+
runtimeAttrs.AddRange(attrs.Cast<Attribute>());
1041+
return runtimeAttrs.Cast<object>().ToArray();
9471042
#endif
9481043
}
9491044

@@ -1020,46 +1115,40 @@ public static TAttr[] AllAttributes<TAttr>(this PropertyInfo pi)
10201115
return pi.AllAttributes(typeof(TAttr)).Cast<TAttr>().ToArray();
10211116
}
10221117

1023-
public static TAttr[] AllAttributes<TAttr>(this Type type, bool inherit = true)
1118+
public static TAttr[] AllAttributes<TAttr>(this Type type)
10241119
{
10251120
#if NETFX_CORE
1026-
return type.GetTypeInfo().GetCustomAttributes<T>(inherit).ToArray();
1121+
return type.GetTypeInfo().GetCustomAttributes<T>(true).ToArray();
10271122
#elif SILVERLIGHT
10281123
return type.GetCustomAttributes(typeof(TAttr), true).Cast<TAttr>().ToArray();
10291124
#else
1030-
return type.GetCustomAttributes(inherit).OfType<TAttr>().ToArray();
1125+
return TypeDescriptor.GetAttributes(type).OfType<TAttr>().ToArray();
10311126
#endif
10321127
}
10331128

1034-
public static TAttr FirstAttribute<TAttr>(this Type type, bool inherit = true)
1129+
public static TAttr FirstAttribute<TAttr>(this Type type)
10351130
{
10361131
#if NETFX_CORE
1037-
return (TAttr)type.GetTypeInfo().GetCustomAttributes(typeof(TAttr), inherit)
1132+
return (TAttr)type.GetTypeInfo().GetCustomAttributes(typeof(TAttr), true)
10381133
.FirstOrDefault();
10391134
#elif SILVERLIGHT
1040-
return (TAttr)type.GetCustomAttributes(typeof(TAttr), inherit)
1135+
return (TAttr)type.GetCustomAttributes(typeof(TAttr), true)
10411136
.FirstOrDefault();
10421137
#else
10431138
return TypeDescriptor.GetAttributes(type).OfType<TAttr>().FirstOrDefault();
10441139
#endif
10451140
}
1046-
1047-
public static TAttribute FirstAttribute<TAttribute>(this PropertyInfo propertyInfo)
1048-
{
1049-
return propertyInfo.FirstAttribute<TAttribute>(true);
1050-
}
10511141

1052-
public static TAttribute FirstAttribute<TAttribute>(this PropertyInfo propertyInfo, bool inherit)
1142+
public static TAttribute FirstAttribute<TAttribute>(this PropertyInfo propertyInfo)
10531143
{
10541144
#if NETFX_CORE
10551145
var attrs = propertyInfo.GetCustomAttributes<TAttribute>(inherit);
10561146
return (TAttribute)(attrs.Count() > 0 ? attrs.ElementAt(0) : null);
1057-
#else
1058-
var attrs = propertyInfo.GetCustomAttributes(typeof(TAttribute), inherit);
1059-
return (TAttribute)(attrs.Length > 0 ? attrs[0] : null);
1147+
#else
1148+
return propertyInfo.AllAttributes<TAttribute>().FirstOrDefault();
10601149
#endif
10611150
}
1062-
1151+
10631152
public static Type FirstGenericTypeDefinition(this Type type)
10641153
{
10651154
while (type != null)
@@ -1132,7 +1221,7 @@ public static FieldInfo GetPublicStaticField(this Type type, string fieldName)
11321221
#endif
11331222
}
11341223

1135-
public static Delegate MakeDelegate(this MethodInfo mi, Type delegateType, bool throwOnBindFailure=true)
1224+
public static Delegate MakeDelegate(this MethodInfo mi, Type delegateType, bool throwOnBindFailure = true)
11361225
{
11371226
#if NETFX_CORE
11381227
return mi.CreateDelegate(delegateType);
@@ -1167,7 +1256,7 @@ public static bool AssignableFrom(this Type type, Type fromType)
11671256
return type.IsAssignableFrom(fromType);
11681257
#endif
11691258
}
1170-
1259+
11711260
public static bool IsStandardClass(this Type type)
11721261
{
11731262
#if NETFX_CORE
@@ -1240,7 +1329,7 @@ public static bool InstanceOfType(this Type type, object instance)
12401329
return type.IsInstanceOfType(instance);
12411330
#endif
12421331
}
1243-
1332+
12441333
public static bool IsClass(this Type type)
12451334
{
12461335
#if NETFX_CORE
@@ -1264,7 +1353,7 @@ public static bool IsEnumFlags(this Type type)
12641353
#if NETFX_CORE
12651354
return type.GetTypeInfo().IsEnum && type.FirstAttribute<FlagsAttribute>(false) != null;
12661355
#else
1267-
return type.IsEnum && type.FirstAttribute<FlagsAttribute>(false) != null;
1356+
return type.IsEnum && type.FirstAttribute<FlagsAttribute>() != null;
12681357
#endif
12691358
}
12701359

@@ -1306,8 +1395,8 @@ public static List<U> ConvertAll<T, U>(this List<T> list, Func<T, U> converter)
13061395
return result;
13071396
}
13081397
#endif
1309-
1310-
1398+
1399+
13111400
}
13121401

13131402
}

0 commit comments

Comments
 (0)