转换IQueryable< t>的表达式int List< 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
);
并且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)将传递的valueProperty
和textProperty
表达式绑定到一个公共参数.由于假定它们代表属性/字段访问器,因此传递的表达式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< t>的表达式int List< SelectListItem>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!