LINQPad:如何向转储的每一行添加 属性?
LINQPad: How do I add a property to each row being dumped?
在 LINQPad 中,我经常想转储一些 SQL table,但在每一行添加一个 属性。我通常默认为简单的:
Publishers
.Select(p => new { p, AppCount = p.Apps.Count() })
但这当然会导致输出混乱,因为每个项目都被格式化为垂直 table,并且有一个额外的列包含添加的 属性。我可以创建一个新对象,但这非常冗长,而且您会丢失 table 遍历链接:
Publishers
.Select(p => new { p.PublisherId, p.Name, p.Added, ..., AppCount = p.Apps.Count() })
我喜欢的是某种合并语法,它创建一个动态对象来转储添加的 属性:
Publishers
.Select(p => p.Merge(new { AppCount = p.Apps.Count() })
我尝试了几种不同的方法来实现这一点:使用 JsonConvert.SerializeObject(试图序列化所有外键引用和重复循环,使用 ExpandoObject(无法想出一个很好的干净的语法),以及像 https://github.com/kfinley/TypeMerger 这样的库,我也没有开始工作。
有没有人想出一个干净的方法来做到这一点? LINQPad 是否有一些我可以使用的内置 synax?谢谢!
更新:我上传了一个示例脚本来演示这个问题,让任何人尝试他们的解决方案:http://share.linqpad.net/kclxpl.linq
到 运行,您需要填充 Nutshell 数据库,您可以使用 LINQPad 中的“填充演示数据库”示例来完成此操作。
您可以添加一些扩展方法来完成这项工作。我不建议将这些添加到 My Extensions
,因为它们需要导入 System.Dynamic
,而对于大多数查询,您可能不需要。将它们放在另一个查询中,在需要时导入 System.Dynamic
和 #load
。
public static class ExpandoObjectExt {
// convert object to ExpandoObject, add a property and return as dynamic
public static dynamic With<T, TAdd>(this T obj, string propName, TAdd val) {
var eo = Util.ToExpando(obj);
var objDict = (IDictionary<string, object>)eo;
objDict[propName] = val;
return eo;
}
// convert IEnumerable<T> to IEnumerable<dynamic>, adding a property to each object
public static IEnumerable<dynamic> With<T, TAdd>(this IEnumerable<T> src, string propName, Func<T, TAdd> valFn) => src.Select(s => s.With(propName, valFn(s)));
}
现在你可以这样使用了:
Publishers
.With("AppCount", p => p.Apps.Count())
但是,转换为 ExpandoObject
将消除集合属性的任何超链接,您可以通过使用显式 .Dump(1)
仅指定一级输出来解决此问题。您还可以编写自定义 Util.ToExpando
(代码为 here),使用 Util.OnDemand
为集合属性创建超链接。
此外,如果您想添加多个 属性,您可以编写 With
的变体,它采用多对或测试 ExpandoObject
的版本(和 IEnumerable<ExpandoObject>
) 并且可以链接流畅的风格。
我对 NetMage 的扩展方法略有不同。试试这个:
public static object Extendo(this object @object, Action<dynamic> inject)
{
dynamic e = Util.ToExpando(@object);
inject(e);
return (object)e;
}
Action<dynamic>
允许这样写代码:
var people = new[]
{
new { name = "Fred", age = 41 },
new { name = "John", age = 67 },
new { name = "Dave", age = 23 },
};
people
.Select(p => p.Extendo(d => d.IsOld = p.age >= 55))
.Dump();
我可以用这个方法稍微扩展一下:
public static object Extendo<T>(this IEnumerable<T> source, Action<T, dynamic> inject) =>
source.Select(x => x.Extendo(d => inject(x, d)));
现在我可以这样写了:
people.Extendo((p, d) => d.IsOld = p.age >= 55).Dump();
我得到了相同的结果。
当然,这些方法有效地 return object
因此无法进行进一步的计算。因此,直奔 Dump
可能是值得的。试试这个:
public static void Dump<T>(this IEnumerable<T> source, Action<T, dynamic> inject) =>
source.Select(x => x.Extendo(d => inject(x, d))).Dump();
现在你只要这样写:
people.Dump((p, d) => d.IsOld = p.age >= 55);
这是一个很难解决的问题。部分问题在于,当将延迟加载的属性从其“实体”主页中取出并移至 ExpandoObject 时,允许 LINQPad 识别延迟加载属性的元数据会丢失,从而导致过多 round-tripping.
为了避免麻烦,我在 LINQPad 7.2.7 中添加了一个 Util.Merge
方法。这是它最简单的用法:
var o1 = new { A = 1, B = 2 };
var o2 = new { C = 3, D = 4 };
Util.Merge (o1, o2).Dump();
对于手头的问题:
Publishers
.Select(p => Util.Merge (p, new { AppCount = p.Apps.Count() }))
在 LINQPad 中,我经常想转储一些 SQL table,但在每一行添加一个 属性。我通常默认为简单的:
Publishers
.Select(p => new { p, AppCount = p.Apps.Count() })
但这当然会导致输出混乱,因为每个项目都被格式化为垂直 table,并且有一个额外的列包含添加的 属性。我可以创建一个新对象,但这非常冗长,而且您会丢失 table 遍历链接:
Publishers
.Select(p => new { p.PublisherId, p.Name, p.Added, ..., AppCount = p.Apps.Count() })
我喜欢的是某种合并语法,它创建一个动态对象来转储添加的 属性:
Publishers
.Select(p => p.Merge(new { AppCount = p.Apps.Count() })
我尝试了几种不同的方法来实现这一点:使用 JsonConvert.SerializeObject(试图序列化所有外键引用和重复循环,使用 ExpandoObject(无法想出一个很好的干净的语法),以及像 https://github.com/kfinley/TypeMerger 这样的库,我也没有开始工作。
有没有人想出一个干净的方法来做到这一点? LINQPad 是否有一些我可以使用的内置 synax?谢谢!
更新:我上传了一个示例脚本来演示这个问题,让任何人尝试他们的解决方案:http://share.linqpad.net/kclxpl.linq
到 运行,您需要填充 Nutshell 数据库,您可以使用 LINQPad 中的“填充演示数据库”示例来完成此操作。
您可以添加一些扩展方法来完成这项工作。我不建议将这些添加到 My Extensions
,因为它们需要导入 System.Dynamic
,而对于大多数查询,您可能不需要。将它们放在另一个查询中,在需要时导入 System.Dynamic
和 #load
。
public static class ExpandoObjectExt {
// convert object to ExpandoObject, add a property and return as dynamic
public static dynamic With<T, TAdd>(this T obj, string propName, TAdd val) {
var eo = Util.ToExpando(obj);
var objDict = (IDictionary<string, object>)eo;
objDict[propName] = val;
return eo;
}
// convert IEnumerable<T> to IEnumerable<dynamic>, adding a property to each object
public static IEnumerable<dynamic> With<T, TAdd>(this IEnumerable<T> src, string propName, Func<T, TAdd> valFn) => src.Select(s => s.With(propName, valFn(s)));
}
现在你可以这样使用了:
Publishers
.With("AppCount", p => p.Apps.Count())
但是,转换为 ExpandoObject
将消除集合属性的任何超链接,您可以通过使用显式 .Dump(1)
仅指定一级输出来解决此问题。您还可以编写自定义 Util.ToExpando
(代码为 here),使用 Util.OnDemand
为集合属性创建超链接。
此外,如果您想添加多个 属性,您可以编写 With
的变体,它采用多对或测试 ExpandoObject
的版本(和 IEnumerable<ExpandoObject>
) 并且可以链接流畅的风格。
我对 NetMage 的扩展方法略有不同。试试这个:
public static object Extendo(this object @object, Action<dynamic> inject)
{
dynamic e = Util.ToExpando(@object);
inject(e);
return (object)e;
}
Action<dynamic>
允许这样写代码:
var people = new[]
{
new { name = "Fred", age = 41 },
new { name = "John", age = 67 },
new { name = "Dave", age = 23 },
};
people
.Select(p => p.Extendo(d => d.IsOld = p.age >= 55))
.Dump();
我可以用这个方法稍微扩展一下:
public static object Extendo<T>(this IEnumerable<T> source, Action<T, dynamic> inject) =>
source.Select(x => x.Extendo(d => inject(x, d)));
现在我可以这样写了:
people.Extendo((p, d) => d.IsOld = p.age >= 55).Dump();
我得到了相同的结果。
当然,这些方法有效地 return object
因此无法进行进一步的计算。因此,直奔 Dump
可能是值得的。试试这个:
public static void Dump<T>(this IEnumerable<T> source, Action<T, dynamic> inject) =>
source.Select(x => x.Extendo(d => inject(x, d))).Dump();
现在你只要这样写:
people.Dump((p, d) => d.IsOld = p.age >= 55);
这是一个很难解决的问题。部分问题在于,当将延迟加载的属性从其“实体”主页中取出并移至 ExpandoObject 时,允许 LINQPad 识别延迟加载属性的元数据会丢失,从而导致过多 round-tripping.
为了避免麻烦,我在 LINQPad 7.2.7 中添加了一个 Util.Merge
方法。这是它最简单的用法:
var o1 = new { A = 1, B = 2 };
var o2 = new { C = 3, D = 4 };
Util.Merge (o1, o2).Dump();
对于手头的问题:
Publishers
.Select(p => Util.Merge (p, new { AppCount = p.Apps.Count() }))