Last active
December 30, 2015 22:09
-
-
Save tugberkugurlu/7892276 to your computer and use it in GitHub Desktop.
Type description (object graph) discoveribility through reflection.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PersonRequestModel | |
{ | |
public string FirstName { get; set; } | |
public string LastName { get; set; } | |
public int Age { get; set; } | |
} | |
public class PersonDto | |
{ | |
public int Id { get; set; } | |
public string FirstName { get; set; } | |
public string LastName { get; set; } | |
public int Age { get; set; } | |
public Nullable<int> Score { get; set; } | |
public IEnumerable<Point> Points { get; set; } | |
public IEnumerable<string> Tags { get; set; } | |
public AddressDto Address { get; set; } | |
public DateTimeOffset CreatedOn { get; set; } | |
} | |
public class Point | |
{ | |
public double Value { get; set; } | |
public string RatedBy { get; set; } | |
} | |
public class AddressDto | |
{ | |
public int Id { get; set; } | |
public string Address { get; set; } | |
public LocationDto Location { get; set; } | |
} | |
public class LocationDto | |
{ | |
public double Latitude { get; set; } | |
public double Longitude { get; set; } | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
IDictionary<string, string> descs = FlattenAsDictionary(typeof(PersonDto)); | |
foreach (KeyValuePair<string, string> desc in descs) | |
{ | |
Console.WriteLine(desc); | |
} | |
/* | |
* | |
Result: | |
[Address.Location.Latitude, Latitude - System.Double] | |
[Address.Location.Longitude, Longitude - System.Double] | |
[Address.Id, Id - System.Int32] | |
[Address.Address, Address - System.String] | |
[Points[0].Value, Value - System.Double] | |
[Points[0].RatedBy, RatedBy - System.String] | |
[Id, Id - System.Int32] | |
[FirstName, FirstName - System.String] | |
[LastName, LastName - System.String] | |
[Age, Age - System.Int32] | |
[Score, Score - System.Int32] | |
[Tags, Tags - System.String] | |
[CreatedOn, CreatedOn - System.DateTimeOffset] | |
* | |
*/ | |
} | |
static IDictionary<string, string> FlattenAsDictionary(Type type, string prefix = null) | |
{ | |
IEnumerable<PropertyInfo> properties = type.GetProperties(); | |
Dictionary<string, string> tempStorage = new Dictionary<string, string>(); | |
foreach (PropertyInfo property in properties) | |
{ | |
string propertyName = (prefix == null) | |
? property.Name | |
: string.Concat(prefix, ".", property.Name); | |
Type propertyType = TypeHelper.IsNullableValueType(property.PropertyType) | |
? Nullable.GetUnderlyingType(property.PropertyType) | |
: property.PropertyType; | |
if (TypeHelper.IsSimpleType(propertyType)) | |
{ | |
string desc = property.Name + " - " + propertyType.FullName; | |
tempStorage.Add(propertyName, desc); | |
} | |
else if(TypeHelper.IsCollection(propertyType)) | |
{ | |
Type enumerableType = TypeHelper.GetEnumerableType(propertyType); | |
if (TypeHelper.IsSimpleType(enumerableType)) | |
{ | |
string desc = property.Name + " - " + enumerableType.FullName; | |
tempStorage.Add(propertyName, desc); | |
} | |
else | |
{ | |
propertyName = propertyName + "[0]"; | |
IDictionary<string, string> innerDescs = FlattenAsDictionary(enumerableType, propertyName); | |
tempStorage = innerDescs.Concat(tempStorage).ToDictionary(x => x.Key, x => x.Value); | |
} | |
} | |
else | |
{ | |
IDictionary<string, string> innerDescs = FlattenAsDictionary(propertyType, propertyName); | |
tempStorage = innerDescs.Concat(tempStorage).ToDictionary(x => x.Key, x => x.Value); | |
} | |
} | |
return tempStorage; | |
} | |
} | |
public static class TypeHelper | |
{ | |
internal static bool IsNullableValueType(Type type) | |
{ | |
return Nullable.GetUnderlyingType(type) != null; | |
} | |
internal static bool IsSimpleType(Type type) | |
{ | |
return type.IsPrimitive || | |
type.Equals(typeof(string)) || | |
type.Equals(typeof(DateTime)) || | |
type.Equals(typeof(Decimal)) || | |
type.Equals(typeof(Guid)) || | |
type.Equals(typeof(DateTimeOffset)) || | |
type.Equals(typeof(TimeSpan)); | |
} | |
internal static bool IsSimpleUnderlyingType(Type type) | |
{ | |
Type underlyingType = Nullable.GetUnderlyingType(type); | |
if (underlyingType != null) | |
{ | |
type = underlyingType; | |
} | |
return TypeHelper.IsSimpleType(type); | |
} | |
internal static bool IsCollection(Type type) | |
{ | |
Type genericParam = GetEnumerableType(type); | |
return IsSimpleType(type) ? false : genericParam != null; | |
} | |
/// <remarks> | |
/// ref: http://stackoverflow.com/questions/1846671/determine-if-collection-is-of-type-ienumerablet | |
/// </remarks> | |
internal static Type GetEnumerableType(Type type) | |
{ | |
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) | |
{ | |
return type.GetGenericArguments()[0]; | |
} | |
foreach (Type intType in type.GetInterfaces()) | |
{ | |
if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) | |
{ | |
return intType.GetGenericArguments()[0]; | |
} | |
} | |
return null; | |
} | |
/// <summary> | |
/// </summary> | |
/// <see href="http://stackoverflow.com/questions/11430654/is-it-possible-to-use-type-gettype-with-a-dynamically-loaded-assembly" /> | |
/// <param name="assemblyQualifiedName"></param> | |
/// <returns></returns> | |
/// <exception cref="System.TypeLoadException"> | |
/// throwOnError is true and the type is not found. -or-throwOnError is true | |
/// and typeName contains invalid characters, such as an embedded tab.-or-throwOnError | |
/// is true and typeName is an empty string.-or-throwOnError is true and typeName | |
/// represents an array type with an invalid size. -or-typeName represents an | |
/// array of System.TypedReference. | |
/// </exception> | |
internal static Type ParseAssemblyQualifiedName(string assemblyQualifiedName) | |
{ | |
// Throws exception is type was not found | |
Type type = Type.GetType(assemblyQualifiedName, | |
(name) => | |
{ | |
// Returns the assembly of the type by enumerating loaded assemblies | |
// in the app domain | |
return AppDomain.CurrentDomain.GetAssemblies().Where(z => z.FullName == name.FullName).FirstOrDefault(); | |
}, | |
null, | |
true); | |
return type; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment