将List<string>转换为EntityFramework列/字段列表

3
使用EntityFramework,我可以使用以下语法获取某种类型的实体列表:
List<Customer> customers = ((IQueryable<Customer>)myEntities.Customers
     .Where(c => c.Surname == strSurname)
     .OrderBy(c => c.Surname)).ToList<Customer>();

我可以像这样做,最终只得到我感兴趣的数据:

(我可以通过此方法获取我所需的数据)

var customerSummaries = from s in customers
     select new
     {
        s.Surname, s.FirstName, s.Address.Postcode
     };

我收到一份基于用户选择的字符串列表,其中包含所请求的汇总数据的字段(必要时还包括表)。例如,对于上述的“customerSummary”,提供的字符串列表将是:“Surname”,“FirstName”,“Address.Postcode”。
我的问题是:如何将这些字符串转换为提取指定字段所需的语法?
如果无法完成此操作,那么有什么更好的类型(而不是字符串)可以用于列的列表,以便我可以提取正确的信息?
我想我需要知道EF实体/表的成员/列/字段的类型,如果有意义的话。
编辑:
我尝试了建议的答案 - 使用动态LINQ - 使用以下语法。
string parmList = "Surname, Firstname, Address.Postcode";
var customers = myEntities.Customers.Select(parmList)
  .OrderBy("Address.Postcode");

但是这会导致以下结果:EntitySqlException was unhandled. 'Surname' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly. 所以,一个后续问题。我是否正确使用了Select?我只看到过使用WhereOrderBy子句的示例,但我认为我根据这些做得很对。
如果我的Select语法不是问题,有人能看出问题在哪里吗?
编辑2:它是颠倒的。这个可以:
string parmList = "Surname, Firstname, Address.Postcode";
var customers = myEntities.Customers
  .OrderBy("Address.Postcode")
  .Select(parmList);

有没有其他的方法可以使用?例如存储过程?如果只是一次性的事情,虽然不是LINQ,但实现起来非常简单。 - lahsrah
我考虑过使用直接的SQL语句,如果被打败了,我会尝试一下,但现在还不会。 :-) - mcalex
2个回答

2

我建议使用@Cuong已经发布的“动态linq”。

但是对于简单的Select投影,并作为表达式中手工练习…

使用LinqRuntimeTypeBuilder
(请参见那里的评论,“why”是必要的)

然后添加这个...

public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<string> fieldNames)
{
    Dictionary<string, PropertyInfo[]> sourceProperties = new Dictionary<string, PropertyInfo[]>();
    foreach (var propertyPath in fieldNames)
    {
        var props = propertyPath.Split('.');
        var name = props.Last();
        PropertyInfo[] infos;
        if (sourceProperties.TryGetValue(name, out infos))
            name = string.Join("", props);
        sourceProperties[name] = source.ElementType.GetDeepProperty(props);
    }

    Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.ToDictionary(x => x.Key, x => x.Value.Last().PropertyType));

    ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t");
    IEnumerable<MemberBinding> bindings = dynamicType.GetFields()
        .Select(p => Expression.Bind(p, sourceItem.MakePropertyExpression(sourceProperties[p.Name]))).OfType<MemberBinding>();

    Expression selector = Expression.Lambda(Expression.MemberInit(
        Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

    MethodCallExpression selectExpression = Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType }, Expression.Constant(source), selector);
    return Expression.Lambda(selectExpression).Compile().DynamicInvoke() as IQueryable;
}

public static PropertyInfo[] GetDeepProperty(this Type type, params string[] props)
{
    List<PropertyInfo> list = new List<PropertyInfo>();
    foreach (var propertyName in props)
    {
        var info = type.GetProperty(propertyName);
        type = info.PropertyType;
        list.Add(info);
    }
    return list.ToArray();
}

public static Expression MakePropertyExpression(this ParameterExpression sourceItem, PropertyInfo[] properties)
{
    Expression property = sourceItem;
    foreach (var propertyInfo in properties)
        property = Expression.Property(property, propertyInfo);
    return property;
}

使用方法例如:

public static IEnumerable<object> 
    SelectAsEnumerable(this IQueryable entitySet, params string[] propertyPath)
{
    return entitySet.SelectDynamic(propertyPath) as IEnumerable<object>;
}
var list = db.YourEntity.SelectAsEnumerable("Name", "ID", "TestProperty.ID").ToList();
注意:
您可以使用类似的方法来扩展OrderBy等功能,但这并不意味着涵盖了所有角度(或任何真实的查询)。

深度属性表达式 参考 我的这篇文章



2
你可以使用动态linq,在这里查看。它也可以在nuget上获取。

谢谢提供链接,您能看一下我的编辑吗? - mcalex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接