How to generically create an Expression for a property? - c#

ExpressMapper has an Ignore method that looks like this:
public IMemberConfiguration<T, TN> Ignore<TMember>(Expression<Func<TN, TMember>> dest)
{
if (dest == null)
{
throw new ArgumentNullException("dst");
}
if (!(dest.Body is MemberExpression))
{
throw new Exception("MemberExpression should return one of the properties of destination class");
}
foreach (var typeMapper in _typeMappers)
{
typeMapper.Ignore(dest);
}
return this;
}
I would like to add my own IgnoreAll method that iterates over all properties on a Type and calls the Ignore method for each property. This is what I have so far:
public static IMemberConfiguration<TSource, TDestination> IgnoreAll<TSource, TDestination>(
this IMemberConfiguration<TSource, TDestination> config)
{
var props = typeof (TDestination).GetProperties();
foreach (var prop in props)
{
var propertyInfo = typeof(TDestination).GetProperty(prop.Name);
var entityParam = Expression.Parameter(typeof(TDestination), "e");
Expression columnExpr = Expression.Property(entityParam, prop);
if (propertyInfo.PropertyType != typeof(object))
columnExpr = Expression.Convert(columnExpr, typeof(object));
var expression = Expression.Lambda<Func<TDestination, object>>(columnExpr, entityParam);
config.Ignore(expression);
}
return config;
}
When this code is run, I get an error:
MemberExpression should return one of the properties of destination
class
As you can see from the source of the Ignore method above, my generated expression is failing the following conditional:
if (!(dest.Body is MemberExpression))
{
throw new Exception("MemberExpression should return one of the properties of destination class");
}
So my question is:
What do I need to change in my extension method to make the correct Expression that the Ignore method is expecting?
Edit: by the way, the full source for the MemberConfiguration class is here: https://github.com/fluentsprings/ExpressMapper/blob/master/ExpressMapper%20NET40/MemberConfiguration.cs

Okay I figured this out, and I gotta tell ya, it was a doozy.
public static IMemberConfiguration<TSource, TDestination> IgnoreAll<TSource, TDestination>(
this IMemberConfiguration<TSource, TDestination> config)
{
// First we'll get the collection of properties to iterate over.
var props = typeof (TDestination).GetProperties();
foreach (var prop in props)
{
// Get the property information.
var propertyInfo = typeof(TDestination).GetProperty(prop.Name);
// Create an expression that points to the property.
var entityParameter = new ParameterExpression[]
{
Expression.Parameter(typeof(TDestination), "e")
};
var propertyExpression = Expression.Property(entityParameter[0], prop);
// Create a Func<,> using the TDestination and the property's type
// for the Type parameters.
var funcType = typeof(Func<,>).MakeGenericType(typeof(TDestination), propertyInfo.PropertyType);
// We need to create an Expression using Expression.Lambda<>, but we
// don't know the types involved so we have to do this using reflection.
var lambdaMethod = typeof (Expression)
.GetMethods()
.Single(m => m.IsGenericMethod &&
m.GetParameters()[0].ParameterType == typeof(Expression) &&
m.GetParameters()[1].ParameterType == typeof(ParameterExpression[]));
var lambdaMethodConstructed = lambdaMethod.MakeGenericMethod(funcType);
var expression = lambdaMethodConstructed.Invoke(
null,
new object[] { propertyExpression, entityParameter });
// Now we need to construct the Ignore method using the property's Type.
var ignoreMethod = config.GetType().GetMethod("Ignore");
var constructed = ignoreMethod.MakeGenericMethod(propertyInfo.PropertyType);
// Finally, we call the constructed Ignore method, using
// our expression as the argument.
constructed.Invoke(config, new object[] { expression });
}
return config;
}

Related

Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined error

Check this fiddle for the error: https://dotnetfiddle.net/tlz4Qg
I have two classes like this:
public class ParentType{
private ParentType(){}
public int Id { get; protected set; }
public SubType Sub { get; protected set; }
}
public class SubType{
private SubType(){}
public int Id { get; protected set; }
}
I am going to transform a multilevel anonymous expression to a multilevel non-anonymous expression. To achieve this I have an expression like the below-mentioned one:
x => new
{
x.Id,
Sub = new
{
x.Sub.Id
}
}
To achieve that goal, I have transformed it to an expression like this:
x => new ParentType()
{
Id = x.Id,
Sub = new SubType()
{
Id = x.Sub.Id
},
}
But when I call Compile() method, I get the following error:
Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined
Here is my visitor class:
public class ReturnTypeVisitor<TIn, TOut> : ExpressionVisitor
{
private readonly Type funcToReplace;
private ParameterExpression currentParameter;
private ParameterExpression defaultParameter;
private Type currentType;
public ReturnTypeVisitor() => funcToReplace = typeof(Func<,>).MakeGenericType(typeof(TIn), typeof(object));
protected override Expression VisitNew(NewExpression node)
{
if (!node.Type.IsAnonymousType())
return base.VisitNew(node);
if (currentType == null)
currentType = typeof(TOut);
var ctor = currentType.GetPrivateConstructor();
if (ctor == null)
return base.VisitNew(node);
NewExpression expr = Expression.New(ctor);
IEnumerable<MemberBinding> bindings = node.Members.Select(x =>
{
var mi = currentType.GetProperty(x.Name);
//if the type is anonymous then I need to transform its body
if (((PropertyInfo)x).PropertyType.IsAnonymousType())
{
//This section is became unnecessary complex!
//
var property = (PropertyInfo)x;
var parentType = currentType;
var parentParameter = currentParameter;
currentType = currentType.GetProperty(property.Name).PropertyType;
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
//I pass the inner anonymous expression to VisitNew and make the non-anonymous expression from it
var xOriginal = VisitNew(node.Arguments.FirstOrDefault(a => a.Type == property.PropertyType) as NewExpression);
currentType = parentType;
currentParameter = parentParameter;
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
else//if type is not anonymous then simple find the property and make the memberbinding
{
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
});
return Expression.MemberInit(expr, bindings);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) != funcToReplace)
return base.VisitLambda(node);
defaultParameter = node.Parameters.First();
currentParameter = defaultParameter;
var body = Visit(node.Body);
return Expression.Lambda<Func<TIn, TOut>>(body, currentParameter);
}
}
And use it like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
var visitor = new ReturnTypeVisitor<Tin, Tout>();
var result = (Expression<Func<Tin, Tout>>)visitor.Visit(source);
return result;// result.Compile() throw the aforementioned error
}
Here is the extension methods used inside my Visitor class:
public static ConstructorInfo GetPrivateConstructor(this Type type) =>
type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
// this hack taken from https://stackoverflow.com/a/2483054/4685428
// and https://stackoverflow.com/a/1650895/4685428
public static bool IsAnonymousType(this Type type)
{
var markedWithAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any();
var typeName = type.Name;
return markedWithAttribute
&& (typeName.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& typeName.Contains("AnonymousType");
}
Update
Here is the .Net Fiddle link for the problem: https://dotnetfiddle.net/tlz4Qg
Update
I have removed the extra codes that seems to be out of the problem scope.
The cause of the problem in question is the line
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
inside VisitNew method.
With your sample, it creates a new parameter called "x.Sub", so if we mark the parameters with {}, the actual result is
Sub = new SubType()
{
Id = {x.Sub}.Id
},
rather than expected
Sub = new SubType()
{
Id = {x}.Sub.Id
},
In general you should not create new ParameterExpressions except when remapping lambda expressions. And all newly created parameters should be passed to Expression.Lambda call, otherwise they will be considered "not defined".
Also please note that the visitor code has some assumptions which doesn't hold in general. For instance
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
won't work inside nested new, because there you need access to a member of the x parameter like x.Sub.Id rather than x.Id. Which is basically the corersonding expression from NewExpression.Arguments.
Processing nested lambda expressions or collection type members and LINQ methods with expression visitors requires much more state control. While converting simple nested anonymous new expression like in the sample does not even need a ExpressionVisitor, because it could easily be achieved with simple recursive method like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
return Expression.Lambda<Func<Tin, Tout>>(
Transform(source.Body, typeof(Tout)),
source.Parameters);
}
static Expression Transform(Expression source, Type type)
{
if (source.Type != type && source is NewExpression newExpr && newExpr.Members.Count > 0)
{
return Expression.MemberInit(Expression.New(type), newExpr.Members
.Select(m => type.GetProperty(m.Name))
.Zip(newExpr.Arguments, (m, e) => Expression.Bind(m, Transform(e, m.PropertyType))));
}
return source;
}

Conditional method calls in an expression tree

I'm trying to add an additional method call to my expression tree, but I'm slightly confused how to implement it. Here is what I'm currently working with:
private static Action<object, object> CreateSetter(SetterInfo info)
{
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null)
return (s, v) => { };
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
//This is the method call I'm trying to add
if (info.Name[0] == 'G' && info.Type.Name == TaxDataConstant.ParcelFeat)
{
var convertParcelFeatCall = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
}
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, Expression.Constant(propertyInfo.PropertyType));
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
var lambda = Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
return lambda.Compile();
}
What I want to happen is:
1) If the name of the type in my SetterInfo object is ParcelFeat and the Properties name begins with 'G' I want to call ConvertParcelFeat on valueParameter and then call ChangeType on the return.
2) If the name of the type is anything other than ParcelFeat call Changetype as normal with out the additional steps
What I'm confused is how to build the conditional. I'm assuming the way I'm doing it in the above code is wrong and I need to use something like Expression.IfThen() to to build the conditional. I'm also unsure how I can chain the method calls like I want.
You do not need in Expression.IfThen because for each specific SetterInfo you combine exactly one specific lambda instance.
Just plug in convertParcelFeatCall in proper place of your ExpressionTree and all should work just fine.
So your code might look like:
class Program
{
static void Main(string[] args)
{
var program = new Program();
var weightLambda = program.DoInternal("Weight").ToString()
== "(Param_0, Param_1) => (Convert(Param_0).Weight = Convert(ChangeType(Param_1, System.Object)))";
var goodiesLambda = program.DoInternal("Goodies").ToString()
== "(Param_0, Param_1) => (Convert(Param_0).Goodies = Convert(ChangeType(Param_1, ConvertParcelFeat(Param_1, \"Goodies\"))))";
Console.WriteLine("WeightLambda is Ok: {0}\nGoodiesLambda is Ok: {1}", weightLambda, goodiesLambda);
}
public Action<Object, Object> Do(string name)
{
return DoInternal(name).Compile();
}
public Expression<Action<object, object>> DoInternal(string name)
{
var info = new {Name = name, Type = typeof(Program)};
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
//This is the method call I'm trying to add
Expression toBeTypeChanged;
if (info.Name[0] == 'G' && info.Type.Name == "Program")
{
toBeTypeChanged = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
}
else
{
toBeTypeChanged = Expression.Constant(propertyInfo.PropertyType);
}
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, toBeTypeChanged);
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
return Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
}
public object Weight { get; set; }
public object Goodies { get; set; }
public static object ChangeType(object valueParameter, object constant)
{
return null;
}
public static object ConvertParcelFeat(object valueParameter, object constant)
{
return null;
}
public MethodInfo ConvertParcelFeatMethod
{
get { return typeof(Program).GetMethod("ConvertParcelFeat"); }
}
public MethodInfo ChangeTypeMethod
{
get { return typeof(Program).GetMethod("ChangeType"); }
}
}

Reading Properties of an Object with Expression Trees

I want to create a Lambda Expression for every Property of an Object that reads the value dynamically.
What I have so far:
var properties = typeof (TType).GetProperties().Where(p => p.CanRead);
foreach (var propertyInfo in properties)
{
var getterMethodInfo = propertyInfo.GetGetMethod();
var entity = Expression.Parameter(typeof (TType));
var getterCall = Expression.Call(entity, getterMethodInfo);
var lambda = Expression.Lambda(getterCall, entity);
var expression = (Expression<Func<TType, "TypeOfProperty">>) lambda;
var functionThatGetsValue = expression.Compile();
}
The code Works well when i call functionThatGetsValue as long as "TypeOfProperty" is hardcoded. I know that I can't pass the "TypeOfPoperty" dynamically. What can I do to achive my goal?
Assuming that you're happy with a Func<TType, object> delegate (as per the comments above), you can use Expression.Convert to achieve that:
var properties = typeof(TType).GetProperties().Where(p => p.CanRead);
foreach (var propertyInfo in properties)
{
MethodInfo getterMethodInfo = propertyInfo.GetGetMethod();
ParameterExpression entity = Expression.Parameter(typeof(TType));
MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo);
UnaryExpression castToObject = Expression.Convert(getterCall, typeof(object));
LambdaExpression lambda = Expression.Lambda(castToObject, entity);
var functionThatGetsValue = (Func<TType, object>)lambda.Compile();
}
After hours of googling found the answer here. I've added the snippets from the blog post as it might help others having the same troubles:
public static class PropertyInfoExtensions
{
public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
return (Func<T, object>)Expression.Lambda(convert, instance).Compile();
}
public static Action<T, object> GetValueSetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var argument = Expression.Parameter(typeof(object), "a");
var setterCall = Expression.Call(
instance,
propertyInfo.GetSetMethod(),
Expression.Convert(argument, propertyInfo.PropertyType));
return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile();
}
}
I've modified gsharp's post above to actually set the value directly and make it a bit easier to use. It's not ideal as there is the introduction of the DynamicCast function which requires you to know your type up front. My goal was to try to keep us strongly typed and not return object and avoid dynamic keyword. Also, keep "magic" to a minimum.
public static T DynamicCast<T>(this object value)
{
return (T) value;
}
public static object GetPropertyValue<T>(this PropertyInfo propertyInfo, T objectInstance)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, propertyInfo.PropertyType);
var lambda = Expression.Lambda(convert, instance).Compile();
var result = lambda.DynamicInvoke(objectInstance);
return result;
}
public static void SetPropertyValue<T, TP>(this PropertyInfo propertyInfo, T objectInstance, TP value)
where T : class
where TP : class
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var argument = Expression.Parameter(propertyInfo.PropertyType, "a");
var setterCall = Expression.Call(
instance,
propertyInfo.GetSetMethod(),
Expression.Convert(argument, propertyInfo.PropertyType));
var lambda = Expression.Lambda(setterCall, instance, argument).Compile();
lambda.DynamicInvoke(objectInstance, value);
}
Examples:
public void Get_Value_Of_Property()
{
var testObject = new ReflectedType
{
AReferenceType_No_Attributes = new object(),
Int32WithRange1_10 = 5,
String_Requires = "Test String"
};
var result = testObject.GetType().GetProperty("String_Requires").GetPropertyValue(testObject).DynamicCast<string>();
result.Should().Be(testObject.String_Requires);
}
public void Set_Value_Of_Property()
{
var testObject = new ReflectedType
{
AReferenceType_No_Attributes = new object(),
Int32WithRange1_10 = 5,
String_Requires = "Test String"
};
testObject.GetType().GetProperty("String_Requires").SetPropertyValue(testObject, "MAGIC");
testObject.String_Requires.Should().Be("MAGIC");
}
You could write a helper method which uses MakeGenericMethod or an expression tree to make a lambda do the typed call to call DynamicCast based on the PropertyInfo object and avoid having to know it up front. But that is less elegant.

How can I use reflection to invoke DbModelBuilder.Entity<T>.Ignore(x => x.Property)?

I'm dynamically executing fluent mapping based on attributes on my poco classes and I have the .Property case working fine, but blows up trying to run the .Ignore() method:
private void AddEntities(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
foreach (var entityType in EntityBaseTypes)
{
dynamic entityConfiguration = entityMethod.MakeGenericMethod(entityType).Invoke(modelBuilder, new object[] { });
foreach (PropertyInfo propertyInfo in entityType.GetProperties().Where(x => x.CanWrite))
{
foreach (var attribute in propertyInfo.GetCustomAttributes())
{
LambdaExpression propertyLambda = PropertyGetLambda(entityType, propertyInfo.Name, typeof(string));
string attributeName = attribute.GetType().Name;
if (attributeName == "NotMappedAttribute")
{
MethodInfo ignoreMethod = (MethodInfo)entityConfiguration.GetType().GetMethod("Ignore");
//BLOWS UP HERE: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true
ignoreMethod.Invoke(entityConfiguration, new[] { propertyLambda });
}
else if (attributeName == "ColumnAttribute")
{
dynamic column = attribute;
var propertyMethod = entityConfiguration.GetType().GetMethod("Property", new Type[] { propertyLambda.GetType() });
var entityProperty = (PrimitivePropertyConfiguration)propertyMethod.Invoke(entityConfiguration, new[] { propertyLambda });
//WORKS FINE:
entityProperty.HasColumnName(column.Name);
}
}
}
}
}
I think the type needed for the .Ignore() method parameters is different. This is the method I'm attempting to call: .Ignore()
public void Ignore<TProperty>(
Expression<Func<TStructuralType, TProperty>> propertyExpression
)
I think you need to get the MemberInfo for the Ignore method using:
MethodInfo ignoreMethod = typeof(StructuralTypeConfiguration<>)
.MakeGenericType(entityType)
.GetMethod("Ignore")
.MakeGenericMethod(propertyInfo.PropertyType);
Ignore is a method with a generic parameter corresponding to the property type of the Expression, so you need to supply the property type before you try to invoke it.

Listing object properties like the Visual Studio Immediate window

I store a few classes in session. I want to be able to see the values of my class properties in trace viewer. By default I only the Type name MyNamespace.MyClass. I was wondering if I overwrite the .ToString() method and use reflection to loop over all the properties and construct a string like that ... it would do the trick but just wanted to see if there is anything already out there (specially since Immediate Window has this capability) which does the same ... i.e. list the class property values in trace instead of just the Name of the class.
You can try something like that:
static void Dump(object o, TextWriter output)
{
if (o == null)
{
output.WriteLine("null");
return;
}
var properties =
from prop in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
where prop.CanRead
&& !prop.GetIndexParameters().Any() // exclude indexed properties to keep things simple
select new
{
prop.Name,
Value = prop.GetValue(o, null)
};
output.WriteLine(o.ToString());
foreach (var prop in properties)
{
output.WriteLine(
"\t{0}: {1}",
prop.Name,
(prop.Value ?? "null").ToString());
}
}
Of course, it's not very efficient because of the reflection... A better solution would be to dynamically generate and cache a dumper method for each specific type.
EDIT: here's an improved solution, that uses Linq expressions to generate a specialized dumper method for each type. Slightly more complex ;)
static class Dumper
{
private readonly static Dictionary<Type, Action<object, TextWriter>> _dumpActions
= new Dictionary<Type, Action<object, TextWriter>>();
private static Action<object, TextWriter> CreateDumper(Type type)
{
MethodInfo writeLine1Obj = typeof(TextWriter).GetMethod("WriteLine", new[] { typeof(object) });
MethodInfo writeLine1String2Obj = typeof(TextWriter).GetMethod("WriteLine", new[] { typeof(string), typeof(object), typeof(object) });
ParameterExpression objParam = Expression.Parameter(typeof(object), "o");
ParameterExpression outputParam = Expression.Parameter(typeof(TextWriter), "output");
ParameterExpression objVariable = Expression.Variable(type, "o2");
LabelTarget returnTarget = Expression.Label();
List<Expression> bodyExpressions = new List<Expression>();
bodyExpressions.Add(
// o2 = (<type>)o
Expression.Assign(objVariable, Expression.Convert(objParam, type)));
bodyExpressions.Add(
// output.WriteLine(o)
Expression.Call(outputParam, writeLine1Obj, objParam));
var properties =
from prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
where prop.CanRead
&& !prop.GetIndexParameters().Any() // exclude indexed properties to keep things simple
select prop;
foreach (var prop in properties)
{
bool isNullable =
!prop.PropertyType.IsValueType ||
prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
// (object)o2.<property> (cast to object to be passed to WriteLine)
Expression propValue =
Expression.Convert(
Expression.Property(objVariable, prop),
typeof(object));
if (isNullable)
{
// (<propertyValue> ?? "null")
propValue =
Expression.Coalesce(
propValue,
Expression.Constant("null", typeof(object)));
}
bodyExpressions.Add(
// output.WriteLine("\t{0}: {1}", "<propertyName>", <propertyValue>)
Expression.Call(
outputParam,
writeLine1String2Obj,
Expression.Constant("\t{0}: {1}", typeof(string)),
Expression.Constant(prop.Name, typeof(string)),
propValue));
}
bodyExpressions.Add(Expression.Label(returnTarget));
Expression<Action<object, TextWriter>> dumperExpr =
Expression.Lambda<Action<object, TextWriter>>(
Expression.Block(new[] { objVariable }, bodyExpressions),
objParam,
outputParam);
return dumperExpr.Compile();
}
public static void Dump(object o, TextWriter output)
{
if (o == null)
{
output.WriteLine("null");
}
Type type = o.GetType();
Action<object, TextWriter> dumpAction;
if (!_dumpActions.TryGetValue(type, out dumpAction))
{
dumpAction = CreateDumper(type);
_dumpActions[type] = dumpAction;
}
dumpAction(o, output);
}
}
Usage:
Dumper.Dump(myObject, Console.Out);
This is the code I use. I find it incredibly useful and is almost instant. The code is using the Newtonsoft JSON converter.
[System.Obsolete("ObjectDump should not be included in production code.")]
public static void Dump(this object value)
{
try
{
System.Diagnostics.Trace.WriteLine(JsonConvert.SerializeObject(value, Formatting.Indented));
}
catch (Exception exception)
{
System.Diagnostics.Trace.WriteLine("Object could not be formatted. Does it include any interfaces? Exception Message: " + exception.Message);
}
}
Add this to a common library, reference it and add it to the using clause. Can be used in the immediate window by typing YourObject.Dump() (the exact same as you'd do in linqpad).
Classes including interfaces have to be handled differently as this is simply a JSON converter. A workaround for classes that include interface implementations that I use is to remove the default empty constructor and implement a constructor using the specific instances of the interfaces.
I find JSON a very easy format to read and consider this small method invaluable for debugging.

Categories

Resources