转换 IQueryable<t> int List<SelectListItem> 的表达式
Expression to convert IQueryable<t> int List<SelectListItem>
我想像这样创建一个存储库方法:
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
。
最后,我也在努力比较selectedValue
参数和SelectListItem.Value
属性。
人Class
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();
}
}
你快到了。实现的东西很少:
(A) 将传递的 valueProperty
和 textProperty
表达式绑定到一个公共参数。由于假设它们表示 property/field 访问器,因此传递的表达式 Body
应该是 MemberExpression
类型,并且可以从 MemberExpression.Member
属性 中提取实际成员信息].
(B) 使用 Expression.Equal
生成 Selected
赋值
把它们放在一起,看起来像这样
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 ) 不是 string
。因此,让 valueProperty
重命名为 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;
}
我想像这样创建一个存储库方法:
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
。
最后,我也在努力比较selectedValue
参数和SelectListItem.Value
属性。
人Class
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();
}
}
你快到了。实现的东西很少:
(A) 将传递的 valueProperty
和 textProperty
表达式绑定到一个公共参数。由于假设它们表示 property/field 访问器,因此传递的表达式 Body
应该是 MemberExpression
类型,并且可以从 MemberExpression.Member
属性 中提取实际成员信息].
(B) 使用 Expression.Equal
Selected
赋值
把它们放在一起,看起来像这样
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 ) 不是 string
。因此,让 valueProperty
重命名为 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;
}