代码之家  ›  专栏  ›  技术社区  ›  Martin Hansen Lennox

用于转换IQueryable<t>int List<SelectListItem>的表达式

  •  0
  • Martin Hansen Lennox  · 技术社区  · 9 年前

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

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

    这将使我能够这样称呼它:

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

    而且,随着 selectedValue 参数为“1”时,应产生如下结果:

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

    我对通用型有问题 AllAsSelectListItems() 方法

    您可以在下面的代码中看到我的尝试。但这并不理想。

    我使用硬编码字符串来填充 SelectListItem 属性与 T 属性。我认为表达式树是解决方案,但我很难正确编码。

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

    最后,我也在努力比较 选定的值 参数 SelectListItem.Value 所有物


    人员类别

    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();
        }
    }
    
    1 回复  |  直到 9 年前
        1
  •  2
  •   Ivan Stoev    9 年前

    你快到了。要实现的事情很少:

    (A) 绑定传递的 valueProperty textProperty 表达式转换为公共参数。由于假设它们表示属性/字段访问器,因此传递的表达式 Body 应为类型 MemberExpression 并且可以从中提取实际成员信息 MemberExpression.Member 所有物

    (B) 生成 Selected 使用赋值 Expression.Equal

    把它们放在一起,看起来会是这样的

    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 ,并且大多数时候源属性(通常是某种Id)不是 一串 。所以让我们重命名 value属性 valueSelector 允许传递类似 x => x.Id.ToString() 虽然我们无法轻松地重新绑定传递的表达式,但我们可以轻松地使用它,而不是创建新的参数,只需重用该表达式的参数即可。

    修改后的方法如下

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