转换IQueryable< t>的表达式int List< SelectListItem> [英] Expression to convert IQueryable<t> int List<SelectListItem>

查看:64
本文介绍了转换IQueryable< t>的表达式int List< SelectListItem>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个这样的存储库方法:

I would like to create a repository method like this:

public List<SelectListItem> AllAsSelectListItems(
    Expression<Func<T, string>> valueProperty, 
    Expression<Func<T, string>> textProperty, 
    string selectedValue = "")
{
    // what goes here? I am having serious trouble with this bit!
}

这将使我可以这样称呼它:

That will enable me to call it like this:

List<SelectListItem> selectListItems = PersonRepository.AllAsSelectListItems(
    m => m.ID,
    m => m.Name,
    selectedIDAsString
);

并且selectedValue参数为"1"时,它应产生如下结果:

And, with the selectedValue parameter being "1", it should produce a result like this:

List<SelectListItem>(){
    {Value: "1", Text: "Ted", Selected: true},
    {Value: "2", Text: "Sam", Selected: false},
    {Value: "3", Text: "Tracy", Selected: false}
};

我在使用通用AllAsSelectListItems()方法时遇到麻烦.

I am having trouble with the generic AllAsSelectListItems() method.

到目前为止,您可以在下面的代码中看到我的尝试.但这不是理想的.

You can see my attempt so far in the code below. But it's not ideal.

我求助于硬编码的字符串,以使用T属性填充SelectListItem属性.我认为表达式树是解决方案,但是我正在努力正确地对其进行编码.

I have resorted to hard-coded strings to populate SelectListItem properties with T properties. I think an expression tree is the solution, but I'm struggling to code it correctly.

此外,分配ID属性会破坏它,因为它是int而不是string.

Also, assigning the ID property breaks it, because it's an int not a string.

最后,我还努力将selectedValue参数与SelectListItem.Value属性进行比较.

Finally, I am also struggling to compare the selectedValue parameter to the SelectListItem.Value property.

人员班

public class Person
{
    public int ID {get;set;}
    public string Name {get;set;}
}

控制器

public class PersonController : Controller 
{
    public IPersonRepository Repository {get;set;}

    public PersonController(IPersonRepository repository) 
    {
        Repository = repository;
    }

    public ActionResult SelectPerson(int selectedID)
    {
        string selectedIDAsString = selectedID.ToString();
        var selectListItems = Repository.AllAsSelectListItems(
            m => m.ID,
            m => m.Name,
            selectedIDAsString
        );
        return View(selectListItems);
    }
}

存储库

public class PersonRepository : Repository
{
     // various specialised methods 
}

public class Repository<T> : IRepository<T> where T : DBEntity
{
    private ApplicationDbContext db = null;
    private DbSet<T> table = null;

    public RepositoryBase()
    {
        this.db = new ApplicationDbContext();
        table = db.Set<T>();
    }
    public RepositoryBase(ApplicationDbContext db)
    {
        this.db = db;
        table = db.Set<T>();
    }

    protected virtual IQueryable<T> AllAsQueryable(
        params Expression<Func<T, object>>[] includeExpressions)
    {
        return includeExpressions.Aggregate<Expression<Func<T, object>>, IQueryable<T>>
            (table, (current, expression) => current.Include(expression));
    }

    public List<SelectListItem> AllAsSelectListItems(
        Expression<Func<T, string>> valueProperty, 
        Expression<Func<T, string>> textProperty, 
        string selectedValue = "")
    {
        // temp hard coded values until we learn how to use the expression parameters properly
        string valuePropertyHardCoded = "Name";
        string textPropertyHardCoded = "Name";
        Type currentType = typeof(T);
        var itemParam = Expression.Parameter(currentType, "x");
        var valueMember = Expression.PropertyOrField(itemParam, valuePropertyHardCoded);
        var textMember = Expression.PropertyOrField(itemParam, textPropertyHardCoded);

        var selector = Expression.MemberInit(Expression.New(typeof(SelectListItem)),
            Expression.Bind(typeof(SelectListItem).GetMember("Value").Single(), valueMember),
            Expression.Bind(typeof(SelectListItem).GetMember("Text").Single(), textMember)
        );
        var lambda = Expression.Lambda<Func<T, SelectListItem>>(
            selector, itemParam);

        return AllAsQueryable().Select(lambda.Compile()).ToList();
    }
}

推荐答案

您快到了.有几件事要实现:

You are almost there. There are few things to be realized:

(A)将传递的valuePropertytextProperty表达式绑定到一个公共参数.由于假定它们代表属性/字段访问器,因此传递的表达式Body应该为MemberExpression类型,并且可以从MemberExpression.Member属性中提取实际的成员信息.

(A) Binding the passed valueProperty and textProperty expressions to a common parameter. Since the assumption is that they represent a property/field accessor, the passed expression Body should be of type MemberExpression and the actual member info can be extracted from MemberExpression.Member property.

(B)使用Expression.Equal

将它们放在一起,看起来像这样

Putting it all together, it will look something like this

public List<SelectListItem> AllAsSelectListItems(
        Expression<Func<T, string>> valueProperty,
        Expression<Func<T, string>> textProperty,
        string selectedValue = "")
{
    if (valueProperty == null) throw new ArgumentNullException("valueProperty");
    if (textProperty == null) throw new ArgumentNullException("textProperty");
    if (!(valueProperty.Body is MemberExpression)) throw new ArgumentException("Must be a field or property.", "valueProperty");
    if (!(textProperty.Body is MemberExpression)) throw new ArgumentException("Must be a field or property.", "textProperty");
    var item = Expression.Parameter(typeof(T), "x");
    var valueMember = Expression.MakeMemberAccess(item, ((MemberExpression)valueProperty.Body).Member);
    var textMember = Expression.MakeMemberAccess(item, ((MemberExpression)textProperty.Body).Member);
    var targetType = typeof(SelectListItem);
    var bindings = new List<MemberBinding>
    {
        Expression.Bind(targetType.GetProperty("Value"), valueMember),
        Expression.Bind(targetType.GetProperty("Text"), textMember)
    };
    if (!string.IsNullOrEmpty(selectedValue))
        bindings.Add(Expression.Bind(targetType.GetProperty("Selected"), Expression.Equal(valueMember, Expression.Constant(selectedValue))));
    var selector = Expression.Lambda<Func<T, SelectListItem>>(
        Expression.MemberInit(Expression.New(targetType), bindings), item);
    var query = AllAsQueryable().Select(selector);
    var result = query.ToList();
    return result;
}

更新:不幸的是,SelectListItem.Value的类型为string,并且在大多数情况下,source属性(通常是某种ID)不是string.因此,将valueProperty重命名为valueSelector并允许传递类似x => x.Id.ToString()的名称.虽然我们不能轻易地重新绑定所传递的表达式,但是我们可以轻松地不加更改地使用它,而无需创建新参数,而只需重用该表达式的参数即可.

Update: Unfortunately SelectListItem.Value is of type string, and most of the time the source property (usually some sort of Id) is not a string. So let rename valueProperty to valueSelector and allow passing something like x => x.Id.ToString(). While we cannot easily rebind the passed expression, but we can easily use it unchanged and instead of creating a new parameter, just reuse the parameter of that expression.

修改后的方法现在应该是这样

The modified method now would be something like this

public List<SelectListItem> AllAsSelectListItems(
        Expression<Func<T, string>> valueSelector,
        Expression<Func<T, string>> textProperty,
        string selectedValue = "")
{
    if (valueSelector == null) throw new ArgumentNullException("valueSelector");
    if (textProperty == null) throw new ArgumentNullException("textProperty");
    if (!(textProperty.Body is MemberExpression)) throw new ArgumentException("Must be a field or property.", "textProperty");
    var item = valueSelector.Parameters[0];
    var itemValue = valueSelector.Body;
    var itemText = Expression.MakeMemberAccess(item, ((MemberExpression)textProperty.Body).Member);
    var targetType = typeof(SelectListItem);
    var bindings = new List<MemberBinding>
    {
        Expression.Bind(targetType.GetProperty("Value"), itemValue),
        Expression.Bind(targetType.GetProperty("Text"), itemText)
    };
    if (!string.IsNullOrEmpty(selectedValue))
        bindings.Add(Expression.Bind(targetType.GetProperty("Selected"), Expression.Equal(itemValue, Expression.Constant(selectedValue))));
    var selector = Expression.Lambda<Func<T, SelectListItem>>(Expression.MemberInit(Expression.New(targetType), bindings), item);
    var query = AllAsQueryable().Select(selector);
    var result = query.ToList();
    return result;
}

这篇关于转换IQueryable&lt; t&gt;的表达式int List&lt; SelectListItem&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆