为 return 字符串构建动态 linq Func
Building a dynamic linq Func to return a string
我正在尝试为 IQueryable 弄清楚如何通过动态选择对象作为字符串来构建 csv 文件。
例如:
我读过这篇关于动态选择 T 属性的文章...
LINQ : Dynamic select
这样我就可以做这样的事情了...
var data = new List<T> { items };
var fields = new string[] { "Field1", "Field2", "Field3" };
// build row strings
var rows = set.Select(BuildRowObjectExpression<T, ProjectionOfT>(fields))
.Select(i => Serialise<ProjectionOfT>(i));
string Serialise<T>(T i, string separator)
{
var properties = typeof(T).GetProperties();
var values = new List<string>();
foreach (var p in properties)
values.Add(p.GetValue(i).ToString());
return string.Join(separator, values);
}
Func<T, Tout> BuildRowObjectExpression<T, Tout>(string[] fields)
{
// input parameter "o"
var xParameter = Expression.Parameter(typeof(T), "o");
// new statement "new Data()"
var xNew = Expression.New(typeof(T));
// create initializers
var bindings = fields.Select(o => {
// property "Field1"
var mi = typeof(T).GetProperty(o);
// original value "o.Field1"
var xOriginal = Expression.Property(xParameter, mi);
// set value "Field1 = o.Field1"
return Expression.Bind(mi, xOriginal);
}
);
// initialization "new T { Field1 = o.Field1, Field2 = o.Field2 }"
var xInit = Expression.MemberInit(xNew, bindings);
// expression "o => new T { Field1 = o.Field1, Field2 = o.Field2 }"
var lambda = Expression.Lambda<Func<T, string>>(xInit, xParameter);
// compile to Func<T, string>
return lambda.Compile();
}
然而我想知道的是:
如何将其构建为表达式/函数,我可以将其与 IQueryable 一起使用来执行类似这样的操作
// this would build me a string array from the specified properties
// in a collection of T joining the values using the given separator
var results = data.Select(i => BuildString(fields, "|")).ToArray();
理想情况下,我想将其与实体集一起使用。
字符串 conversion/concatenation 不是数据库作业。你最好把这两个部分分开——数据库查询中的数据检索和内存查询中的数据转换。
例如,您可以使用以下自定义扩展方法:
public static class Extensions
{
public static IQueryable<T> Select<T>(this IQueryable source, string[] fields)
{
var parameter = Expression.Parameter(source.ElementType, "o");
var body = Expression.MemberInit(
Expression.New(typeof(T)),
fields.Select(field => Expression.Bind(
typeof(T).GetProperty(field),
Expression.PropertyOrField(parameter, field))
)
);
var selector = Expression.Lambda(body, parameter);
var expression = Expression.Call(
typeof(Queryable), "Select", new[] { parameter.Type, body.Type },
source.Expression, Expression.Quote(selector)
);
return source.Provider.CreateQuery<T>(expression);
}
public static IEnumerable<string> Serialize<T>(this IEnumerable<T> source, string separator)
{
var properties = typeof(T).GetProperties();
return source.Select(item => string.Join(separator, properties.Select(property => property.GetValue(item))));
}
}
像这样
var results = db.Data.Select<ProjectionOfT>(fields).Serialize("|");
如果你想避免 ProjectionOfT
class,没有简单的方法可以做到这一点,因为它需要动态运行时 class 生成,所以你最好求助于 System.Linq.Dynamic
包.
我正在尝试为 IQueryable 弄清楚如何通过动态选择对象作为字符串来构建 csv 文件。
例如:
我读过这篇关于动态选择 T 属性的文章...
LINQ : Dynamic select
这样我就可以做这样的事情了...
var data = new List<T> { items };
var fields = new string[] { "Field1", "Field2", "Field3" };
// build row strings
var rows = set.Select(BuildRowObjectExpression<T, ProjectionOfT>(fields))
.Select(i => Serialise<ProjectionOfT>(i));
string Serialise<T>(T i, string separator)
{
var properties = typeof(T).GetProperties();
var values = new List<string>();
foreach (var p in properties)
values.Add(p.GetValue(i).ToString());
return string.Join(separator, values);
}
Func<T, Tout> BuildRowObjectExpression<T, Tout>(string[] fields)
{
// input parameter "o"
var xParameter = Expression.Parameter(typeof(T), "o");
// new statement "new Data()"
var xNew = Expression.New(typeof(T));
// create initializers
var bindings = fields.Select(o => {
// property "Field1"
var mi = typeof(T).GetProperty(o);
// original value "o.Field1"
var xOriginal = Expression.Property(xParameter, mi);
// set value "Field1 = o.Field1"
return Expression.Bind(mi, xOriginal);
}
);
// initialization "new T { Field1 = o.Field1, Field2 = o.Field2 }"
var xInit = Expression.MemberInit(xNew, bindings);
// expression "o => new T { Field1 = o.Field1, Field2 = o.Field2 }"
var lambda = Expression.Lambda<Func<T, string>>(xInit, xParameter);
// compile to Func<T, string>
return lambda.Compile();
}
然而我想知道的是:
如何将其构建为表达式/函数,我可以将其与 IQueryable 一起使用来执行类似这样的操作
// this would build me a string array from the specified properties
// in a collection of T joining the values using the given separator
var results = data.Select(i => BuildString(fields, "|")).ToArray();
理想情况下,我想将其与实体集一起使用。
字符串 conversion/concatenation 不是数据库作业。你最好把这两个部分分开——数据库查询中的数据检索和内存查询中的数据转换。
例如,您可以使用以下自定义扩展方法:
public static class Extensions
{
public static IQueryable<T> Select<T>(this IQueryable source, string[] fields)
{
var parameter = Expression.Parameter(source.ElementType, "o");
var body = Expression.MemberInit(
Expression.New(typeof(T)),
fields.Select(field => Expression.Bind(
typeof(T).GetProperty(field),
Expression.PropertyOrField(parameter, field))
)
);
var selector = Expression.Lambda(body, parameter);
var expression = Expression.Call(
typeof(Queryable), "Select", new[] { parameter.Type, body.Type },
source.Expression, Expression.Quote(selector)
);
return source.Provider.CreateQuery<T>(expression);
}
public static IEnumerable<string> Serialize<T>(this IEnumerable<T> source, string separator)
{
var properties = typeof(T).GetProperties();
return source.Select(item => string.Join(separator, properties.Select(property => property.GetValue(item))));
}
}
像这样
var results = db.Data.Select<ProjectionOfT>(fields).Serialize("|");
如果你想避免 ProjectionOfT
class,没有简单的方法可以做到这一点,因为它需要动态运行时 class 生成,所以你最好求助于 System.Linq.Dynamic
包.