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

loresoft / EntityChange / 10792597827

10 Sep 2024 12:38PM CUT coverage: 39.608%. Remained the same
10792597827

push

github

web-flow
Merge pull request #103 from loresoft/dependabot/nuget/MinVer-6.0.0

Bump MinVer from 5.0.0 to 6.0.0

237 of 731 branches covered (32.42%)

Branch coverage included in aggregate %.

571 of 1309 relevant lines covered (43.62%)

60.49 hits per line

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

14.59
/src/EntityChange/Reflection/ReflectionHelper.cs
1
using System.Collections;
2
using System.ComponentModel;
3
using System.Linq.Expressions;
4
using System.Reflection;
5

6
namespace EntityChange.Reflection;
7

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

30
        return ExtractPropertyName(propertyExpression.Body as MemberExpression);
×
31
    }
32

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

54
        return ExtractPropertyName(propertyExpression.Body as MemberExpression);
×
55
    }
56

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

76
        return memberExpression.Member.Name;
×
77
    }
78

79

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

97
        return ExtractColumnName(propertyExpression.Body as MemberExpression);
×
98
    }
99

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

121
        return ExtractColumnName(propertyExpression.Body as MemberExpression);
×
122
    }
123

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

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

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

156
        return property.Name;
×
157
    }
158

159

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

171
        return ExtractPropertyInfo(propertyExpression.Body as MemberExpression);
×
172
    }
173

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

186
        return ExtractPropertyInfo(propertyExpression.Body as MemberExpression);
×
187
    }
188

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

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

203
        return property;
×
204
    }
205

206

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

217
        var t = type;
297✔
218
        var typeInfo = t.GetTypeInfo();
297✔
219

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

224
        return t;
297✔
225
    }
226

227

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

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

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

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

267
        if (collectionType == null)
×
268
            return false;
×
269

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

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

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

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

307
        keyType = type;
202✔
308
        elementType = type;
202✔
309

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

316
        if (collectionType == null)
202!
317
            return false;
202✔
318

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

323
        return true;
×
324
    }
325

326

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

360
        if (valueType == null)
×
361
            throw new ArgumentNullException(nameof(valueType));
×
362

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

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

376
        desiredType = GetUnderlyingType(desiredType);
×
377

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

383
        if (value == null)
×
384
            return null;
×
385

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

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

393
        bool isBinary = desiredType.IsArray && typeof(byte[]) == desiredType;
×
394

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

401
        isBinary = valueType.IsArray && typeof(byte[]) == valueType;
×
402

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

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

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

427

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

441
        return method.DeclaringType != method.GetBaseDefinition().DeclaringType;
×
442
    }
443

444
}
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

© 2025 Coveralls, Inc