简体   繁体   English

表达式转换为IQueryable <t> 整数列表 <SelectListItem>

[英]Expression to convert 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
);

And, with the selectedValue parameter being "1", it should produce a result like this: 并且,在selectedValue参数为“ 1”的情况下,它应产生如下结果:

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

I am having trouble with the generic AllAsSelectListItems() method. 我在使用通用AllAsSelectListItems()方法时遇到麻烦。

You can see my attempt so far in the code below. 您可以在下面的代码中看到我到目前为止的尝试。 But it's not ideal. 但这不是理想的。

I have resorted to hard-coded strings to populate SelectListItem properties with T properties. 我已使用硬编码的字符串来用T属性填充SelectListItem属性。 I think an expression tree is the solution, but I'm struggling to code it correctly. 我认为表达式树是解决方案,但是我正在努力对其进行正确编码。

Also, assigning the ID property breaks it, because it's an int not a string . 另外,分配ID属性会破坏它,因为它是一个int而不是string

Finally, I am also struggling to compare the selectedValue parameter to the SelectListItem.Value property. 最后,我还在努力将selectedValue参数与SelectListItem.Value属性进行比较。


Person Class 人类

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

Controller 控制者

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);
    }
}

Repository 资料库

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) Binding the passed valueProperty and textProperty expressions to a common parameter. (A)将传递的valuePropertytextProperty表达式绑定到一个公共参数。 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. 由于假定它们代表属性/字段访问器,所以传递的表达式Body应该为MemberExpression类型,并且可以从MemberExpression.Member属性提取实际的成员信息。

(B) Generating the Selected assignment by using Expression.Equal (B)通过使用Expression.Equal生成Selected分配

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;
}

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 . 更新:不幸的是SelectListItem.Valuestring类型的,并且在大多数情况下source属性(通常是某种Id)不是string So let rename valueProperty to valueSelector and allow passing something like x => x.Id.ToString() . 因此,让我们将valueProperty重命名为valueSelector并允许传递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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM