C# Function that accepts string parameters: How to refactor to use something strongly typed?
I have this C# code that works just fine, which grabs any two fields and puts them in a list for a Drop Down List:
var myDDL = GetDDLValues<Product>( myContact, "contactid", "companyname");
I would like it to take the two string parameters in something other than strings. This would be really nice to do:
GetDDLValues<Product>( myContact, p => p.contactid开发者_如何学运维, p => p.companyname)
This is the code it calls (reflection by sweko Thanks!):
private object GetProperty(object obj, string propertyName)
{
PropertyInfo pi = obj.GetType().GetProperty(propertyName);
object value = pi.GetValue(obj, null);
return value;
}
public IList<DDLValues> GetDDLValues<T>(IList<T> objectListToMap,
string textProperty, string valueProperty)
{
if( objectListToMap != null && objectListToMap.Count > 0)
{
Mapper.CreateMap< T, DDLValues>()
.ForMember( dest => dest.text,
opt => opt.MapFrom(src => textProperty))
.ForMember( dest => dest.value,
opt => opt.MapFrom(src => valueProperty));
return Mapper.Map<IList<T>, IList<DDLValues>>(objectListToMap);
}
else
{
return null;
}
}
To build a dynamic query from string:
public class Product
{
public long ID { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
}
static void Main(string[] args)
{
List<Product> products = (from i in Enumerable.Range(1, 10)
select new Product { ID = i, Name = "product " + i, Date = DateTime.Now.AddDays(-i) }).ToList(); //the test case
const string SortBy = "Date"; // to test you can change to "ID"/"Name"
Type sortType = typeof(Product).GetProperty(SortBy).PropertyType; // DateTime
ParameterExpression sortParamExp = Expression.Parameter(typeof(Product), "p"); // {p}
Expression sortBodyExp = Expression.PropertyOrField(sortParamExp, SortBy); // {p.DateTime}
LambdaExpression sortExp = Expression.Lambda(sortBodyExp, sortParamExp); // {p=>p.DateTime}
var OrderByMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("OrderBy") && m.GetParameters().Count() == 2).FirstOrDefault().MakeGenericMethod(typeof(Product), sortType);
var result = OrderByMethod.Invoke(products, new object[] { products, sortExp.Compile() });
}
tvanfosson is right that a Select()
is simplest; if you want to use AutoMapper, you want something like this..
public IList<DDLValues> GetDDLValues<T>(IList<T> objectListToMap,
Func<T, string> textSelector, Func<T, string> valueSelector)
{
if( objectListToMap == null || objectListToMap.Count == 0)
return null;
Mapper.CreateMap< T, DDLValues>()
.ForMember( dest => dest.text,
opt => opt.MapFrom(textSelector))
.ForMember( dest => dest.value,
opt => opt.MapFrom(valueSelector));
return Mapper.Map<IList<T>, IList<DDLValues>>(objectListToMap);
}
If DDLValues
has a constructor that takes the text and value properties it should be relatively simple.
IList<DDLValues> GetDDLValues<T>( IList<T> source, Func<T,DDLValues> selector )
where T : class
{
return source.Select( selector )
.ToList();
}
Called as
var ddlList = GetDDLValues( contacts, p => new DDLValues( c.Name, p.ContactID.ToString() ) );
This presumes that DDLValues has a constructor like:
public DDLValues( string text, string value ) { ... }
If you're pulling from the database in your source, then you may also need to materialize the query using ToList() before you apply the selector to ensure it doesn't try to convert the selector into a SQL expression.
精彩评论