I forget where I got the OrderBy/OutToFile code from, the ObjectDumper is from the MS sample. The pretty formatter mods are mine.
//typical usage
ObjectDumper.WritePretty(stuff.OrderBy(sort));
//write objectdumper output to file (remove the using statement to write to console)
using (new OutToFile("stuffpretty.txt"))
{
//example: sort and some modification of the output based on what date is in the column
string sort="t"; //[-]p[ropertyName] [DESC]
var fcb = new ObjectDumper.Callbacks();
fcb.fColumn = (string h) => { return h == "Time" ? "" : h; };
fcb.fwDate = (DateTime dt) => { return dt > DateTime.Today.AddDays(-1) ? "<< " + dt.ToShortDateString() : " " + dt.ToShortDateString(); };
// pretty format stuff object and sort by a column specd in sort string (from user input)
ObjectDumper.WritePretty(stuff.OrderBy(sort), fcb);
}
Class (remember to add namespace to it, then import that namespace so you get the OrderBy query extension):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Linq.Expressions;
public static class QueryExtensions
{
private static Dictionary<TKey, TValue> DictionaryFromKeys<TKey, TValue>(IEnumerable<TKey> keysource, TValue defvalue)
{
var myDict = new Dictionary<TKey, TValue>();
foreach (TKey c in keysource) { if (!myDict.ContainsKey(c)) myDict.Add(c, defvalue); }
return myDict;
}
//[-]p[ropertyName] [DESC]
//public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string propertyName)
public static IQueryable<T> OrderBy<T>(this IEnumerable<T> iesource, string propertyName) //returns iesource back if property was invalid!!! (no fail)
{
if (iesource == null) throw new ArgumentNullException("source");
var source = iesource.AsQueryable<T>(); //++
// DataSource control passes the sort parameter with a direction if the direction is descending
int descIndex = propertyName.IndexOf(" DESC");
if (descIndex >= 0) propertyName = propertyName.Substring(0, descIndex).Trim();
if (String.IsNullOrEmpty(propertyName)) return source;
if (propertyName[0] == '-') { propertyName = propertyName.Substring(1); descIndex=0; } //*** [-]
if (propertyName == "-" || propertyName == "+") return source; // existing ordering not known? / not supported
if (String.IsNullOrEmpty(propertyName)) return source;
var flds = typeof(T).GetFields(); var prps = typeof(T).GetProperties();
MemberInfo foundprop = prps.FirstOrDefault(x => x.Name.ToLowerInvariant().StartsWith(propertyName.ToLowerInvariant()));
MemberInfo foundfld = flds.FirstOrDefault(x => x.Name.ToLowerInvariant().StartsWith(propertyName.ToLowerInvariant()));
if (foundprop == null && foundfld == null) return source; //foundprop = propertyName; // *** RETURN unmodified if property was invalid!!! (no fail)
ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);
MemberExpression property=null;
if (foundprop != null) property = Expression.Property(parameter, foundprop.Name);
else if (foundfld != null) property = Expression.Field(parameter, foundfld.Name);
LambdaExpression lambda = Expression.Lambda(property, parameter);
string methodName = (descIndex < 0) ? "OrderBy" : "OrderByDescending";
Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
new Type[] { source.ElementType, property.Type },
source.Expression, Expression.Quote(lambda));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
//NameValueCollection allows key string to have multiple string values. This extension method allows NVC usage in LINQ
public static IEnumerable<KeyValuePair<string, string>> ToPairs(this System.Collections.Specialized.NameValueCollection collection)
{
if (collection == null) throw new ArgumentNullException("collection");
return collection.Cast<string>().Select(key => new KeyValuePair<string, string>(key, collection[key]));
}
}
// If you want CSV output there's http://www.codeproject.com/KB/linq/LINQtoCSV.aspx
// transferring data to Excel http://support.microsoft.com/kb/306023
/// <summary>
///using (new OutToFile("dump.txt")) // overwrites
///{
/// ObjectDumper.WritePretty(...);
/// ObjectDumper.WritePretty(...);
///}
/// </summary>
public class OutToFile : IDisposable
{
private StreamWriter fileOutput;
private TextWriter oldOutput;
/// <summary>
/// Redirect the console output to specified file
/// </summary>
public OutToFile(string outFileName) : this(new FileStream(outFileName, FileMode.Create)) { }
/// <summary>
/// Redirect the console output to specified Stream
/// </summary>
public OutToFile(Stream writableStream)
{
oldOutput = Console.Out;
fileOutput = new StreamWriter(writableStream);
fileOutput.AutoFlush = true;
Console.SetOut(fileOutput);
}
/// <summary>
/// Redirect the console output to Stream.Null
/// </summary>
public OutToFile() : this(Stream.Null) { }
// Dispose() is called automatically when the object
// goes out of scope
public void Dispose()
{
Console.SetOut(oldOutput); // Restore the console output
fileOutput.Close(); // Done with the file
}
}
public class ObjectDumper
{
public static bool OdOutputNullableNulls = false; // write xxx? fieldName=null;'s in the output
public static void Write(object o) { Write(o, 0, true); }
public static void Write(object o, bool autoTabSize) { Write(o, 0, autoTabSize); }
public static void Write(object o, int depth, bool autoTabSize)
{
ObjectDumper dumper = new ObjectDumper(depth, autoTabSize);
dumper.WriteObject(null, o);
}
public static List<string> WritePretty(object o) { return WritePretty(o, 0, null); }
public static List<string> WritePretty(object o, Callbacks OutputFormatter) { return WritePretty(o, 0, OutputFormatter); }
public static List<string> WritePretty(object o, int depth, Callbacks OutputFormatter)
{
ObjectDumper dumper = new ObjectDumper(depth, true);
if (OutputFormatter!=null) dumper.formatter = OutputFormatter;
if (o is IEnumerable)
{
dumper.writer = new StreamWriter(System.IO.Stream.Null);
var cache = new List<object>();
dumper.cachePass = true;
foreach (var co in (IEnumerable)o) cache.Add(co);
dumper.WriteObject(null, cache);
dumper.writer.Close();
dumper.cacheCount = cache.Count;
dumper.cachePass = false;
dumper.writer = Console.Out;
dumper.WriteObject(null, cache);
}
else dumper.WriteObject(null, o);
return dumper.fields;
}
bool cachePass = false;
int cacheCount = 0;
public class Callbacks
{
public Func<string, string, string> fValue = (a, b) => { return b; };
public Func<string, string> fColumn = (a) => { return a; };
public Func<string, string> fwString = (a) => { return a; };
public Func<DateTime, string> fwDate = (a) => { return a.ToShortDateString(); };
public Func<string, object, object> fWriteValue = (a, b) => { return b; };
public Action<int> fPrintNext = (a) => { };
}
// field value ret:formattedvalue
Callbacks formatter = new Callbacks();
internal TextWriter writer;
int pos;
int level;
int depth;
private ObjectDumper(int depth, bool autoTabSize)
{
this.autoTabSize = autoTabSize;
this.writer = Console.Out;
this.depth = depth;
}
private void Write(string s)
{
if (s != null)
{
writer.Write(s);
pos += s.Length;
}
}
private void WriteIndent()
{
for (int i = 0; i < level; i++) writer.Write(" ");
}
private void WriteLine()
{
writer.WriteLine();
pos = 0;
curval = 0; //***
}
private void WriteTab()
{ //*** added ability to grow the tab size
if (!autoTabSize) { Write(" "); while (pos % 8 != 0) Write(" "); }
else
{
Write(" "); while (pos % 4 != 0) Write(" ");
if (!vallengths.ContainsKey(curval)) vallengths.Add(curval, 0);
if (vallengths[curval] < pos) vallengths[curval] = pos;
else
{
int ofs = vallengths[curval] - pos;
for (int i = 0; i < ofs; i++) Write(" ");
}
}
curval++;
}
//***
Dictionary<int, int> vallengths = new Dictionary<int, int>(20);
List<string> fields = new List<string>(20);
int curval = 0;
bool autoTabSize;
private void WriteObject(string prefix, object o)
{
if (o == null || o is ValueType || o is string)
{
WriteIndent();
Write(prefix);
WriteValue(o);
WriteLine();
}
else if (o is IEnumerable)
{
foreach (object element in (IEnumerable)o)
{
if (element is IEnumerable && !(element is string))
{
WriteIndent();
Write(prefix);
Write("...");
WriteLine();
if (level < depth)
{
level++;
WriteObject(prefix, element);
level--;
}
}
else
{
WriteObject(prefix, element);
}
}
}
else
{
MemberInfo[] members = o.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance); // BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy
WriteIndent();
Write(prefix);
bool propWritten = false;
foreach (MemberInfo m in members)
{
//if (m.Name.Contains("NNN")) continue;
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
if (propWritten)
{
WriteTab();
}
else
{
propWritten = true;
}
if (!fields.Contains(m.Name)) fields.Add(m.Name); ///***
var colstr = formatter.fColumn(m.Name);
Type t = f != null ? f.FieldType : p.PropertyType;
object vlue=null;
if (t.IsValueType || t == typeof(string))
vlue = f != null ? f.GetValue(o) : p.GetValue(o, null);
bool writeIfNull = true; // !(!ODwriteNULLs && vlue == null); //++
if (vlue == null && Nullable.GetUnderlyingType(t) != null) writeIfNull = OdOutputNullableNulls;
if (writeIfNull && !string.IsNullOrEmpty(colstr))
{
Write(colstr);
Write("=");
}
if (writeIfNull && vlue != null)
{
WriteValue(formatter.fWriteValue(m.Name, vlue));
}
else
{
if (typeof(IEnumerable).IsAssignableFrom(t))
{
Write("...");
}
else
{
object x = f != null ? f.GetValue(o) : p.GetValue(o, null);
if (x == null || x.ToString() == x.GetType().ToString()) { if (writeIfNull) Write("{ }"); }//+ x==null ||
else Write("{" + formatter.fValue(m.Name, x.ToString()) + "}"); // *** display the object.ToString() if overriden
}
}
}
}
if (propWritten) { WriteLine(); }
if (level < depth)
{
foreach (MemberInfo m in members)
{
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
Type t = f != null ? f.FieldType : p.PropertyType;
if (!(t.IsValueType || t == typeof(string)))
{
object value = f != null ? f.GetValue(o) : p.GetValue(o, null);
if (value != null)
{
level++;
WriteObject(formatter.fColumn(m.Name) + ": ", formatter.fWriteValue(m.Name,value));
level--;
}
}
}
}
}
}
if (!cachePass)
{
if (prnti < cacheCount)
{
formatter.fPrintNext(prnti);
prnti++;
}
}
}
int prnti = 0;
private void WriteValue(object o)
{
if (o == null)
{
Write("null");
}
else if (o is DateTime)
{
Write(formatter.fwDate((DateTime)o));
//Write(((DateTime)o).ToShortDateString());
}
else if (o is ValueType || o is string)
{
Write(formatter.fwString(o.ToString()));
}
else if (o is IEnumerable)
{
Write("...");
}
else
{
Write("{ }");
}
}
// NOT TESTED
static IEnumerable<string> GetFields(object o, int level, int depth)
{
List<string> fields = new List<string>();
if (o == null || o is ValueType || o is string)
{
}
else if (o is IEnumerable)
{
foreach (object element in (IEnumerable)o)
{
if (element is IEnumerable && !(element is string))
{
if (level < depth)
{
level++;
GetFields(element, level, depth);
level--;
}
}
else
{
GetFields(element, level, depth);
}
}
}
else
{
MemberInfo[] members = o.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
foreach (MemberInfo m in members)
{
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
if (!fields.Contains(m.Name)) fields.Add(m.Name); ///***
}
}
}
return fields;
}
}