• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / EntityChange / 13417967502

19 Feb 2025 04:51PM UTC coverage: 45.43% (+5.7%) from 39.697%
13417967502

push

github

pwelter34
fix failing test

287 of 726 branches covered (39.53%)

Branch coverage included in aggregate %.

563 of 1145 relevant lines covered (49.17%)

73.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

14.59
/src/EntityChange/Reflection/ReflectionHelper.cs
1
#nullable disable
2

3
using System.Collections;
4
using System.ComponentModel;
5
using System.Linq.Expressions;
6
using System.Reflection;
7

8
namespace EntityChange.Reflection;
9

10
/// <summary>
11
/// Reflection helper methods
12
/// </summary>
13
public static class ReflectionHelper
14
{
15
    /// <summary>
16
    /// Extracts the property name from a property expression.
17
    /// </summary>
18
    /// <typeparam name="TValue">The of the property value.</typeparam>
19
    /// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param>
20
    /// <returns>The name of the property.</returns>
21
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
22
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
23
    ///     Not a <see cref="MemberExpression"/><br/>
24
    ///     The <see cref="MemberExpression"/> does not represent a property.<br/>
25
    ///     Or, the property is static.
26
    /// </exception>
27
    public static string ExtractPropertyName<TValue>(Expression<Func<TValue>> propertyExpression)
28
    {
29
        if (propertyExpression == null)
×
30
            throw new ArgumentNullException(nameof(propertyExpression));
×
31

32
        return ExtractPropertyName(propertyExpression.Body as MemberExpression);
×
33
    }
34

35
    /// <summary>
36
    /// Extracts the property name from a property expression.
37
    /// </summary>
38
    /// <typeparam name="TSource">The type of the source.</typeparam>
39
    /// <typeparam name="TValue">The type of the property value.</typeparam>
40
    /// <param name="propertyExpression">The property expression (e.g. p =&gt; p.PropertyName)</param>
41
    /// <returns>
42
    /// The name of the property.
43
    /// </returns>
44
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
45
    ///
46
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
47
    /// Not a <see cref="MemberExpression"/><br/>
48
    /// The <see cref="MemberExpression"/> does not represent a property.<br/>
49
    /// Or, the property is static.
50
    ///   </exception>
51
    public static string ExtractPropertyName<TSource, TValue>(Expression<Func<TSource, TValue>> propertyExpression)
52
    {
53
        if (propertyExpression == null)
×
54
            throw new ArgumentNullException(nameof(propertyExpression));
×
55

56
        return ExtractPropertyName(propertyExpression.Body as MemberExpression);
×
57
    }
58

59
    /// <summary>
60
    /// Extracts the property name from a property expression.
61
    /// </summary>
62
    /// <param name="memberExpression">The member expression</param>
63
    /// <returns>
64
    /// The name of the property.
65
    /// </returns>
66
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="memberExpression"/> is null.</exception>
67
    ///
68
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
69
    /// Not a <see cref="MemberExpression"/><br/>
70
    /// The <see cref="MemberExpression"/> does not represent a property.<br/>
71
    /// Or, the property is static.
72
    ///   </exception>
73
    public static string ExtractPropertyName(MemberExpression memberExpression)
74
    {
75
        if (memberExpression == null)
×
76
            throw new ArgumentNullException(nameof(memberExpression));
×
77

78
        return memberExpression.Member.Name;
×
79
    }
80

81

82
    /// <summary>
83
    /// Extracts the property name from a property expression.
84
    /// </summary>
85
    /// <typeparam name="TValue">The of the property value.</typeparam>
86
    /// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param>
87
    /// <returns>The name of the property.</returns>
88
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
89
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
90
    ///     Not a <see cref="MemberExpression"/><br/>
91
    ///     The <see cref="MemberExpression"/> does not represent a property.<br/>
92
    ///     Or, the property is static.
93
    /// </exception>
94
    public static string ExtractColumnName<TValue>(Expression<Func<TValue>> propertyExpression)
95
    {
96
        if (propertyExpression == null)
×
97
            throw new ArgumentNullException(nameof(propertyExpression));
×
98

99
        return ExtractColumnName(propertyExpression.Body as MemberExpression);
×
100
    }
101

102
    /// <summary>
103
    /// Extracts the property name from a property expression.
104
    /// </summary>
105
    /// <typeparam name="TSource">The type of the source.</typeparam>
106
    /// <typeparam name="TValue">The type of the property value.</typeparam>
107
    /// <param name="propertyExpression">The property expression (e.g. p =&gt; p.PropertyName)</param>
108
    /// <returns>
109
    /// The name of the property.
110
    /// </returns>
111
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
112
    ///
113
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
114
    /// Not a <see cref="MemberExpression"/><br/>
115
    /// The <see cref="MemberExpression"/> does not represent a property.<br/>
116
    /// Or, the property is static.
117
    ///   </exception>
118
    public static string ExtractColumnName<TSource, TValue>(Expression<Func<TSource, TValue>> propertyExpression)
119
    {
120
        if (propertyExpression == null)
×
121
            throw new ArgumentNullException(nameof(propertyExpression));
×
122

123
        return ExtractColumnName(propertyExpression.Body as MemberExpression);
×
124
    }
125

126
    /// <summary>
127
    /// Extracts the property name from a property expression.
128
    /// </summary>
129
    /// <param name="memberExpression">The member expression</param>
130
    /// <returns>
131
    /// The name of the property.
132
    /// </returns>
133
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="memberExpression"/> is null.</exception>
134
    ///
135
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
136
    /// Not a <see cref="MemberExpression"/><br/>
137
    /// The <see cref="MemberExpression"/> does not represent a property.<br/>
138
    /// Or, the property is static.
139
    ///   </exception>
140
    public static string ExtractColumnName(MemberExpression memberExpression)
141
    {
142
        var property = ExtractPropertyInfo(memberExpression);
×
143

144
#if NET40
145
        var display = Attribute.GetCustomAttribute(property, typeof(System.ComponentModel.DataAnnotations.DisplayAttribute)) as System.ComponentModel.DataAnnotations.DisplayAttribute;
146
        if (!string.IsNullOrEmpty(display?.Name))
147
            return display.Name;
148
#else
149
        var column = property.GetCustomAttribute<System.ComponentModel.DataAnnotations.Schema.ColumnAttribute>();
×
150
        if (!string.IsNullOrEmpty(column?.Name))
×
151
            return column.Name;
×
152

153
        var display = property.GetCustomAttribute<System.ComponentModel.DataAnnotations.DisplayAttribute>();
×
154
        if (!string.IsNullOrEmpty(display?.Name))
×
155
            return display.Name;
×
156
#endif
157

158
        return property.Name;
×
159
    }
160

161

162
    /// <summary>
163
    /// Extracts the <see cref="PropertyInfo"/> from the specified property expression.
164
    /// </summary>
165
    /// <typeparam name="TValue">The type of the value.</typeparam>
166
    /// <param name="propertyExpression">The property expression.</param>
167
    /// <returns></returns>
168
    public static PropertyInfo ExtractPropertyInfo<TValue>(Expression<Func<TValue>> propertyExpression)
169
    {
170
        if (propertyExpression == null)
×
171
            throw new ArgumentNullException(nameof(propertyExpression));
×
172

173
        return ExtractPropertyInfo(propertyExpression.Body as MemberExpression);
×
174
    }
175

176
    /// <summary>
177
    /// Extracts the <see cref="PropertyInfo"/> from the specified property expression.
178
    /// </summary>
179
    /// <typeparam name="TSource">The type of the source.</typeparam>
180
    /// <typeparam name="TValue">The type of the value.</typeparam>
181
    /// <param name="propertyExpression">The property expression.</param>
182
    /// <returns></returns>
183
    public static PropertyInfo ExtractPropertyInfo<TSource, TValue>(Expression<Func<TSource, TValue>> propertyExpression)
184
    {
185
        if (propertyExpression == null)
×
186
            throw new ArgumentNullException(nameof(propertyExpression));
×
187

188
        return ExtractPropertyInfo(propertyExpression.Body as MemberExpression);
×
189
    }
190

191
    /// <summary>
192
    /// Extracts the <see cref="PropertyInfo"/> from the specified member expression.
193
    /// </summary>
194
    /// <param name="memberExpression">The member expression.</param>
195
    /// <returns></returns>
196
    public static PropertyInfo ExtractPropertyInfo(MemberExpression memberExpression)
197
    {
198
        if (memberExpression == null)
×
199
            throw new ArgumentException("The expression is not a member access expression.", nameof(memberExpression));
×
200

201
        var property = memberExpression.Member as PropertyInfo;
×
202
        if (property == null)
×
203
            throw new ArgumentException("The member access expression does not access a property.", nameof(memberExpression));
×
204

205
        return property;
×
206
    }
207

208

209
    /// <summary>
210
    /// Gets the underlying type dealing with <see cref="Nullable"/>.
211
    /// </summary>
212
    /// <param name="type">The type.</param>
213
    /// <returns>Returns a type dealing with <see cref="Nullable"/>.</returns>
214
    public static Type GetUnderlyingType(this Type type)
215
    {
216
        if (type == null)
297!
217
            throw new ArgumentNullException(nameof(type));
×
218

219
        var t = type;
297✔
220
        var typeInfo = t.GetTypeInfo();
297✔
221

222
        bool isNullable = typeInfo.IsGenericType && (t.GetGenericTypeDefinition() == typeof(Nullable<>));
297✔
223
        if (isNullable)
297!
224
            return Nullable.GetUnderlyingType(t);
×
225

226
        return t;
297✔
227
    }
228

229

230
    /// <summary>
231
    /// Determines whether the specified <paramref name="type"/> is a collection.
232
    /// </summary>
233
    /// <param name="type">The type to check.</param>
234
    /// <returns>
235
    ///   <c>true</c> if the specified <paramref name="type"/> is a collection; otherwise, <c>false</c>.
236
    /// </returns>
237
    public static bool IsCollection(this Type type)
238
    {
239
        if (type == null)
191!
240
            throw new ArgumentNullException(nameof(type));
×
241

242
        return type
191✔
243
            .GetTypeInfo()
191✔
244
            .GetInterfaces()
191✔
245
            .Union(new[] { type })
191✔
246
            .Any(x => x == typeof(ICollection) || (x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)));
2,508!
247
    }
248

249
    /// <summary>
250
    /// Determines whether the specified <paramref name="type"/> is a collection.
251
    /// </summary>
252
    /// <param name="type">The type to check.</param>
253
    /// <param name="elementType">The Type of the generic element.</param>
254
    /// <returns>
255
    ///   <c>true</c> if the specified <paramref name="type"/> is a collection; otherwise, <c>false</c>.
256
    /// </returns>
257
    public static bool IsCollection(this Type type, out Type elementType)
258
    {
259
        if (type == null)
×
260
            throw new ArgumentNullException(nameof(type));
×
261

262
        elementType = type;
×
263
        var collectionType = type
×
264
            .GetTypeInfo()
×
265
            .GetInterfaces()
×
266
            .Union(new[] { type })
×
267
            .FirstOrDefault(t => t.GetTypeInfo().IsGenericType && (t.GetGenericTypeDefinition() == typeof(ICollection<>)));
×
268

269
        if (collectionType == null)
×
270
            return false;
×
271

272
        elementType = collectionType.GetTypeInfo().GetGenericArguments().Single();
×
273
        return true;
×
274
    }
275

276
    /// <summary>
277
    /// Determines whether the specified <paramref name="type"/> is a dictionary.
278
    /// </summary>
279
    /// <param name="type">The type to check.</param>
280
    /// <returns>
281
    ///   <c>true</c> if the specified <paramref name="type"/> is a dictionary; otherwise, <c>false</c>.
282
    /// </returns>
283
    public static bool IsDictionary(this Type type)
284
    {
285
        if (type == null)
×
286
            throw new ArgumentNullException(nameof(type));
×
287

288
        return type
×
289
            .GetTypeInfo()
×
290
            .GetInterfaces()
×
291
            .Union(new[] { type })
×
292
            .Any(x => x == typeof(IDictionary) || (x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)));
×
293
    }
294

295
    /// <summary>
296
    /// Determines whether the specified <paramref name="type"/> is a dictionary.
297
    /// </summary>
298
    /// <param name="type">The type to check.</param>
299
    /// <param name="keyType">Type of the generic key.</param>
300
    /// <param name="elementType">The Type of the generic element.</param>
301
    /// <returns>
302
    ///   <c>true</c> if the specified <paramref name="type"/> is a dictionary; otherwise, <c>false</c>.
303
    /// </returns>
304
    public static bool IsDictionary(this Type type, out Type keyType, out Type elementType)
305
    {
306
        if (type == null)
203!
307
            throw new ArgumentNullException(nameof(type));
×
308

309
        keyType = type;
203✔
310
        elementType = type;
203✔
311

312
        var collectionType = type
203✔
313
            .GetTypeInfo()
203✔
314
            .GetInterfaces()
203✔
315
            .Union(new[] { type })
203✔
316
            .FirstOrDefault(t => t.GetTypeInfo().IsGenericType && (t.GetGenericTypeDefinition() == typeof(IDictionary<,>)));
2,652✔
317

318
        if (collectionType == null)
203!
319
            return false;
203✔
320

321
        var arguments = collectionType.GetTypeInfo().GetGenericArguments();
×
322
        keyType = arguments.First();
×
323
        elementType = arguments.Skip(1).First();
×
324

325
        return true;
×
326
    }
327

328

329
    /// <summary>
330
    /// Attempts to coerce a value of one type into
331
    /// a value of a different type.
332
    /// </summary>
333
    /// <param name="desiredType">
334
    /// Type to which the value should be coerced.MO
335
    /// </param>
336
    /// <param name="valueType">
337
    /// Original type of the value.
338
    /// </param>
339
    /// <param name="value">
340
    /// The value to coerce.
341
    /// </param>
342
    /// <remarks>
343
    /// <para>
344
    /// If the desired type is a primitive type or Decimal,
345
    /// empty string and null values will result in a 0
346
    /// or equivalent.
347
    /// </para>
348
    /// <para>
349
    /// If the desired type is a <see cref="Nullable"/> type, empty string
350
    /// and null values will result in a null result.
351
    /// </para>
352
    /// <para>
353
    /// If the desired type is an <c>enum</c> the value's ToString()
354
    /// result is parsed to convert into the <c>enum</c> value.
355
    /// </para>
356
    /// </remarks>
357
    public static object CoerceValue(Type desiredType, Type valueType, object value)
358
    {
359
        if (desiredType == null)
×
360
            throw new ArgumentNullException(nameof(desiredType));
×
361

362
        if (valueType == null)
×
363
            throw new ArgumentNullException(nameof(valueType));
×
364

365
        // types match, just copy value
366
        if (desiredType == valueType)
×
367
            return value;
×
368

369
        bool isNullable = desiredType.GetTypeInfo().IsGenericType && (desiredType.GetGenericTypeDefinition() == typeof(Nullable<>));
×
370
        if (isNullable)
×
371
        {
372
            if (value == null)
×
373
                return null;
×
374
            if (typeof(string) == valueType && Convert.ToString(value) == string.Empty)
×
375
                return null;
×
376
        }
377

378
        desiredType = GetUnderlyingType(desiredType);
×
379

380
        if ((desiredType.GetTypeInfo().IsPrimitive || typeof(decimal) == desiredType)
×
381
            && typeof(string) == valueType
×
382
            && string.IsNullOrEmpty((string)value))
×
383
            return 0;
×
384

385
        if (value == null)
×
386
            return null;
×
387

388
        // types don't match, try to convert
389
        if (typeof(Guid) == desiredType)
×
390
            return new Guid(value.ToString());
×
391

392
        if (desiredType.GetTypeInfo().IsEnum && typeof(string) == valueType)
×
393
            return Enum.Parse(desiredType, value.ToString(), true);
×
394

395
        bool isBinary = desiredType.IsArray && typeof(byte[]) == desiredType;
×
396

397
        if (isBinary && typeof(string) == valueType)
×
398
        {
399
            byte[] bytes = Convert.FromBase64String((string)value);
×
400
            return bytes;
×
401
        }
402

403
        isBinary = valueType.IsArray && typeof(byte[]) == valueType;
×
404

405
        if (isBinary && typeof(string) == desiredType)
×
406
        {
407
            byte[] bytes = (byte[])value;
×
408
            return Convert.ToBase64String(bytes);
×
409
        }
410

411
        try
412
        {
413
            if (typeof(string) == desiredType)
×
414
                return value.ToString();
×
415

416
            return Convert.ChangeType(value, desiredType);
×
417
        }
418
        catch
×
419
        {
420
#if !SILVERLIGHT
421
            var converter = TypeDescriptor.GetConverter(desiredType);
×
422
            if (converter.CanConvertFrom(valueType))
×
423
                return converter.ConvertFrom(value);
×
424
#endif
425
            throw;
×
426
        }
427
    }
×
428

429

430
    /// <summary>
431
    /// Determines whether the specified <paramref name="method"/> overrides a base method.
432
    /// </summary>
433
    /// <param name="method">The method information.</param>
434
    /// <returns>
435
    ///   <c>true</c> if the specified <paramref name="method"/> overrides a base method; otherwise, <c>false</c>.
436
    /// </returns>
437
    /// <exception cref="System.ArgumentNullException">methodInfo</exception>
438
    public static bool IsOverriding(this MethodInfo method)
439
    {
440
        if (method == null)
×
441
            throw new ArgumentNullException(nameof(method));
×
442

443
        return method.DeclaringType != method.GetBaseDefinition().DeclaringType;
×
444
    }
445

446
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc