我正在寻找一种从iQueryable对象动态创建选择列表的方法。
具体示例,我想做类似以下的事情:
public void CreateSelectList(IQueryable(of EntityModel.Core.User entities), string[] columns)
{
foreach(var columnID in columns)
{
switch(columnID)
{
case "Type":
SelectList.add(e => e.UserType);
break;
case "Name":
SelectList.add(e => e.Name);
break;
etc....
}
}
var selectResult = (from u in entities select objSelectList);
}
所以所有属性都是已知的,但我事先并不知道要选择哪些属性。这将通过columns参数传递。
我知道我将遇到selectResult类型的问题,因为当选择列表是动态的时,编译器不知道匿名类型的属性是什么。
如果上述情况不可能:我需要的方案如下:
我正在尝试创建一个可以实现的类来显示分页/过滤的数据列表。这些数据可以是任何东西(取决于实现)。使用的linq是实体的linq。所以它们直接链接到sql数据。现在我只想选择我实际在列表中显示的实体的列。因此我希望select是动态的。我的实体可能有一百个属性,但如果列表中只显示了3个属性,我不想生成一个选择所有100列数据的查询,然后只使用其中的3个。如果有一种我没有想到的不同方法,我愿意接受想法
编辑:
对禁令的一些澄清:
- 查询需要与linq一起使用实体(参见问题主题)
- 一个实体可能包含100列,因此选择所有列然后只读取我需要的列不是一个选项
- 最终用户决定显示哪些列,因此要在运行时确定要选择的列
- 我需要创建一个SINGLE选择,有多个select语句意味着对数据库有多个查询,我不想要
答案 0 :(得分:16)
动态选择表达式到编译时已知类型可以使用Expression.MemberInit
方法轻松构建,MemberBinding
方法使用Expression.Bind
方法创建。
这是一个自定义扩展方法:
public static class QueryableExtensions
{
public static IQueryable<TResult> Select<TResult>(this IQueryable source, string[] columns)
{
var sourceType = source.ElementType;
var resultType = typeof(TResult);
var parameter = Expression.Parameter(sourceType, "e");
var bindings = columns.Select(column => Expression.Bind(
resultType.GetProperty(column), Expression.PropertyOrField(parameter, column)));
var body = Expression.MemberInit(Expression.New(resultType), bindings);
var selector = Expression.Lambda(body, parameter);
return source.Provider.CreateQuery<TResult>(
Expression.Call(typeof(Queryable), "Select", new Type[] { sourceType, resultType },
source.Expression, Expression.Quote(selector)));
}
}
唯一的问题是TResult
类型是什么。在 EF Core 中,您可以传递实体类型(例如您的示例中的EntityModel.Core.User
),它可以正常工作。在 EF 6 和更早版本中,您需要一个单独的非实体类型,否则您将获得NotSupportedException
- 无法在LINQ to Entities查询中构造实体或复杂类型< / em>的
更新:如果您想要删除字符串列,我建议您使用以下类替换扩展方法:
public class SelectList<TSource>
{
private List<MemberInfo> members = new List<MemberInfo>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
var member = ((MemberExpression)selector.Body).Member;
members.Add(member);
return this;
}
public IQueryable<TResult> Select<TResult>(IQueryable<TSource> source)
{
var sourceType = typeof(TSource);
var resultType = typeof(TResult);
var parameter = Expression.Parameter(sourceType, "e");
var bindings = members.Select(member => Expression.Bind(
resultType.GetProperty(member.Name), Expression.MakeMemberAccess(parameter, member)));
var body = Expression.MemberInit(Expression.New(resultType), bindings);
var selector = Expression.Lambda<Func<TSource, TResult>>(body, parameter);
return source.Select(selector);
}
}
样本用法:
var selectList = new SelectList<EntityModel.Core.User>();
selectList.Add(e => e.UserType);
selectList.Add(e => e.Name);
var selectResult = selectList.Select<UserDto>(entities);
答案 1 :(得分:1)
你想要的是可能的,但这并不简单。您可以使用System.Linq.Expressions命名空间中的方法和类动态构建EF查询。
有关如何动态构建Select表达式的一个很好的示例,请参阅this question。
答案 2 :(得分:1)
我相信这就是你所需要的:
// initialize alist of entities (User)
var entities = new List<User>();
entities.Add(new User { Name = "First", Type = "TypeA", SomeOtherField="abc" });
entities.Add(new User { Name = "Second", Type = "TypeB", SomeOtherField = "xyz" });
// set the wanted fields
string[] columns = { "Name", "Type" };
// create a set of properties of the User class by the set of wanted fields
var properties = typeof(User).GetProperties()
.Where(p => columns.Contains(p.Name))
.ToList();
// Get it with a single select (by use of the Dynamic object)
var selectResult = entities.Select(e =>
{
dynamic x = new ExpandoObject();
var temp = x as IDictionary<string, Object>;
foreach (var property in properties)
temp.Add(property.Name, property.GetValue(e));
return x;
});
// itterate the results
foreach (var result in selectResult)
{
Console.WriteLine(result.Name);
Console.WriteLine(result.Type);
}
此代码输出:
- 首先
- 第二
- TypeA
- 的TypeB
更新(根据评论)
$random_bit_stream = array_map(function() {
return mt_rand(0,1);
}, range(1,6));
or
$random_bit_stream = array_map(function() {
return rand(0,1);
}, range(1,6));
print_r($random_bit_stream);
此代码输出:
- 首先
- TypeA
- 第二
- 的TypeB