-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Default BuildObject implementation using reflection #24
Comments
I've implemented this as an extension method. There's a bit of ninja backflipping to get the data out of the builder when running outside of the builder but that's easily fixed when implementing this for realsies. public static class TestDataBuilderExtensions
{
public static TObject BuildObjectByConstructor<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> builder)
where TObject : class
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
{
var longestConstructor = typeof (TObject)
.GetConstructors()
.OrderByDescending(x => x.GetParameters().Length)
.FirstOrDefault();
if(longestConstructor == null) throw new ObjectCreationException();
// TODO Validation around Parameters.All(x => typeof(TObject).HasProperty(...))
var parameterValues = longestConstructor
.GetParameters()
.Select(x => new { PropertyName = x.Name.ToUpperCamelCase(), PropertyType = x.ParameterType })
.Select(x => GetValueStoredInBuilder(builder, x.PropertyType, x.PropertyName));
return (TObject) longestConstructor.Invoke(parameterValues.ToArray());
}
public static string ToUpperCamelCase(this string param)
{
if(string.IsNullOrWhiteSpace(param)) throw new ArgumentNullException("param");
var builder = new StringBuilder(param);
builder[0] = char.ToUpperInvariant(builder[0]);
return builder.ToString();
}
public static bool HasProperty(this Type type, string propertyName, Type propertyType)
{
var propertyInfo = type.GetProperty(propertyName);
if (propertyInfo == null) return false;
return propertyInfo.PropertyType == propertyType;
}
private static object GetValueStoredInBuilder<TObject, TBuilder>(TestDataBuilder<TObject, TBuilder> builder, Type propertyType,
string propertyName) where TObject : class where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
{
// Make a Func<TObj, TPropertyType>
var expressionDelegateType = typeof (Func<,>).MakeGenericType(typeof (TObject), propertyType);
// Make an expression parameter of type TObj
var tObjParameterType = Expression.Parameter(typeof (TObject));
var valueStoredInBuilder = typeof (TBuilder)
.GetMethod("Get")
.MakeGenericMethod(propertyType)
.Invoke(builder, new object[]
{
Expression.Lambda(
expressionDelegateType,
Expression.Property(tObjParameterType, propertyName),
tObjParameterType)
});
return valueStoredInBuilder;
}
} There are obvious shortcomings to this approach:
Perhaps this feature could be opt-in by making it a protected method rather than a default? |
Hi Ryan :) Nice one! My thinking was if we couldn't find a matching value via Case sensitivity might actually be an issue because we store the values in a dictionary, we might need to make a chance to make it case insensitive by lowercasing the key when it gets popped in and when it gets checked. |
To avoid having to implement FYI I'm not using this personally, I just had a similar implementation to populate a DTO via setters and figured I should create a basic version while it was still in my head. :) |
I ended up using my reflective method to call I'm not convinced this is a good solution, so perhaps this was a decent exercise in proving it is "too much effort" for the amount of value. |
Automatically build an object by reflecting for a constructor with the most params and getting each by looking for a property of the same name (kinda like AutoFixture, but this would use the ability to customise the object(s).
This would be the default behaviour for BuildObject - you can then override it as soon as you get a more complex example.
This just makes it even more easy to quickly create a builder.
Might be more complex than it's worth though? It doesn't take long to create the BuildObject method...
The text was updated successfully, but these errors were encountered: