【问题标题】:IQueryable extension method to generate a SelectListItem list用于生成 SelectListItem 列表的 IQueryable 扩展方法
【发布时间】:2015-05-04 11:51:54
【问题描述】:

我正在处理返回 IQueryable 对象的存储库。

所以,我创建了一个扩展方法来返回一个 SelectListItem 列表以用于我的视图:

public static List<SelectListItem> ToSelectList<T>(this IQueryable<T> query, Func<T, string> value, Func<T, string> text)
{
    return query.Select(x => new SelectListItem() { Text = text(x), Value = value(x) }).ToList();
}

我这样称呼:

var query = Repository<MyClass>.GetAll(); //Return MyClass IQueryable 
var test = query.ToSelectList(x => x.Property1, x => x.Property2);

效果很好(返回正确的列表),但是 SQL 生成的 select all property from MyClass 。我希望在 db 上只查询 Property1 和 Property2。

我错过了什么?

【问题讨论】:

  • 这是使用实体框架生成到 SQL 中的吗?从生成的 SQL 中选择的其他列很可能是由于传递给您的 IQueryable&lt;T&gt; 而不是此扩展方法中的任何内容。
  • 我正在使用 NHibernate,我不知道我理解你的意思
  • 您在调用此方法之前使用的 IQueryable 可能会生成选择其他列的 SQL。您的扩展方法可能没问题。
  • 1.存储库不应该返回 IQueryable,它打破了它的意义,因为它返回到数据库的链接而不是数据本身。 2. 我在同一场景中使用 nHibernate,但它只选择请求的属性。可能是对 DB 的调用发生在投影到 SelectListItems 之前。
  • 嗯,好的,您将 x 对象传递给方法,因此您正在请求整个对象 - ORM 会处理它并返回具有所有属性的对象。

标签: c# nhibernate extension-methods iqueryable


【解决方案1】:

在从数据库中检索结果x 后,将调用传入的委托,这就是返回所有字段的原因。您可以改为传入表达式,然后将它们组合成一个表达式:

public static List<SelectListItem> ToSelectList<T>(this IQueryable<T> query, Expression<Func<T, object>> value, Expression<Func<T, string>> text)
{
    var valueSrc = getProperty(value);
    var textSrc  = getProperty(text);    
    var x = Expression.Parameter(typeof(T),"x");
    var type = typeof(SelectListItem);
    var textProp = type.GetProperty("Text");
    var valueProp = type.GetProperty("Value");

    // x.valueSrc
    Expression valueExpr = Expression.Property(x,valueSrc);

    // (x.valueSrc).ToString()
    if (valueExpr.Type == typeof(int))
    {
         var toStringMethod = typeof(int).GetMethod("ToString",Type.EmptyTypes);
         valueExpr = Expression.Call(valueExpr,toStringMethod);
    }

    //x => new SelectListItem { Text = x.textSrc, Value = valueExpr }
    var lambda = Expression.Lambda<Func<T,SelectListItem>>(
                    Expression.MemberInit(
                        Expression.New(type),
                        Expression.Bind(textProp,Expression.Property(x,textSrc)),
                        Expression.Bind(valueProp,valueExpr)),
                    x);

    return query.Select(lambda).ToList();
}

static PropertyInfo getProperty(LambdaExpression exp)
{
    var body = exp.Body;

    //x => (object) x.Property
    var ue = body as UnaryExpression;
    if (ue != null)
        body = ue.Operand;

    return (PropertyInfo) ((MemberExpression) body).Member;        
}

【讨论】:

  • 您不能为此使用Expression.Convert()。您可以将Expression.Call() 用于NHibernate 支持的方法,或者您正在使用的任何查询提供程序,将int 更改为string
  • 如果属性类型为 int,我更新了我的答案以包括对 ToString() 的调用。
【解决方案2】:

您可能必须选择仅包含您需要的属性的新匿名对象,然后执行创建您的 SelectListItem 的选择

类似

return query.Select(p => new { value = value(p), text = text(p) ).Select(p => new SelectListItem(...) ;

【讨论】:

  • 知道怎么做吗?
  • 在方法内部可能很难做到,因为你有 2 个函数要处理,并且你需要它们都出现在一个 select 调用中。在调用您的方法之前,您可能只需要自己手动选择新对象。
  • 如果之前创建的 IQueryable 访问这些列,则此答案不会更改使用 EF 在生成的 SQL 中选择的列。
猜你喜欢
  • 2019-08-29
  • 1970-01-01
  • 2019-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多