2014-11-06 60 views
0

该物业目前已经困扰我好几天了....创建表达式树分配到列表

如果我有我自己的对象的列表SearchResults和SearchResult所包含对象的多个列表,所有这些都匹配(布尔)属性,我怎样才能重新表达式树来实现以下目标:

//searchResults is a List<SearchResults> 

searchResults[i].Comments = searchResults[i].Comments.Select(p1 => 
{ 
    p1.Match = ListOfStringVariable.All(p2 => 
    { 
     string value = (string)typeof(CommentData).GetProperty(propertyName).GetValue(p1); 
     return value.Contains(p2); 
    }); 
    return p1; 
}).OrderByDescending(x => x.Match); 

.... 

public class SearchResults 
{ 
    public IEnumerable<CommentData> Comments { get; set; } 
    public IEnumerable<AdvisorData> Advisors { get; set; } 
} 

public class CommentData 
{ 
    public string CommentText { get; set; } 
    public bool Match { get; set; } 
} 

public class AdvisorData 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public bool Match { get; set; } 
} 

,因为我不知道在编译时需要被赋予的属性所需要的表达式树,无论是它是评论,顾问等(因为这是一个更大问题的简化)。上面的例子仅用于评论,那么如何在没有条件块的情况下使用相同的代码来分配给顾问?

非常感谢

更新:

到目前为止,使用反射,我们有以下的StriplingWarrior

var searchResult = searchResults[i]; 
foreach (var srProperty in searchResultsProperties) 
{ 
    var collectionType = srProperty.PropertyType; 
    if(!collectionType.IsGenericType || collectionType.GetGenericTypeDefinition() != typeof(IEnumerable<>)) 
    { 
     throw new InvalidOperationException("All SearchResults properties should be IEnumerable<Something>"); 
    } 
    var itemType = collectionType.GetGenericArguments()[0]; 
    var itemProperties = itemType.GetProperties().Where(p => p.Name != "Match"); 
    var items = ((IEnumerable<IHaveMatchProperty>) srProperty.GetValue(searchResult)) 
     // Materialize the enumerable, in case it's backed by something that 
     // would re-create objects each time it's iterated over. 
     .ToList(); 
    foreach (var item in items) 
    { 
     var propertyValues = itemProperties.Select(p => (string)p.GetValue(item)); 
     item.Match = propertyValues.Any(v => searchTerms.Any(v.Contains)); 
    } 
    var orderedItems = items.OrderBy(i => i.Match); 
    srProperty.SetValue(srProperty, orderedItems); 
} 

然而orderedItemsSystem.Linq.OrderedEnumerable<IHaveMatchProperty,bool>型的,需要转换为IEnumerable<AdvisorData>。下面将引发错误:

'System.Linq.Enumerable.CastIterator(System.Collections.IEnumerable)' 是 '方法',但使用像 '类型'

var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(new[] {propertyType}); 
var result = castMethod.Invoke(null, new[] { orderedItems }); 

其中propertyType是类型AdvisorData

+0

1.你真的不应该在'Select()'lambda中设置属性。 2.你肯定需要表达树吗?如果你使用的是LINQ到对象,你应该能够通过一些基本的反思来获得。 3.您可以考虑使用另一个类,例如'SearchResults ',它具有'Match'属性,而不是将匹配属性放在结果上。如果你是肯定的,你需要CommentData等有一个Match属性,如果你让它们在它上面实现一个与该属性的接口,你可能会让它更容易。 – StriplingWarrior 2014-11-06 19:17:46

+0

@StriplingWarrior选择中的分配以便我不必完全重新创建新列表。我如何使用反射来创建上述?它似乎超出了仅仅反思的范围。另外,我也提到了MatchData接口和Match属性,但现在已经删除了它,因为它简化了我的开发过程并且很容易重新引入。非常感谢 – tic 2014-11-06 19:36:57

+0

我知道你为什么选择将作业放在那里。这仍然是不好的做法。如果您要将注释视为集合并对其进行修改,请使用'for'循环。无论如何,上面的代码已经使用反射来获取CommentData上的属性值。迭代所有SearchResults的值也不应该太大。你需要哪些帮助? (尽量保持您的问题尽可能具体。) – StriplingWarrior 2014-11-06 20:13:53

回答

1

首先,让你的类型实现这个接口,这样你就不必做这么多的反思:

public interface IHaveMatchProperty 
{ 
    bool Match { get; set; } 
} 

然后写代码,做像这样的东西。 (我做了很多假设,因为你的问题并不是非常清楚你想要的行为是什么。)

var searchResult = searchResults[i]; 
foreach (var srProperty in searchResultsProperties) 
{ 
    var collectionType = srProperty.PropertyType; 
    if(!collectionType.IsGenericType || collectionType.GetGenericTypeDefinition() != typeof(IEnumerable<>)) 
    { 
     throw new InvalidOperationException("All SearchResults properties should be IEnumerable<Something>"); 
    } 
    var itemType = collectionType.GetGenericArguments()[0]; 
    var itemProperties = itemType.GetProperties().Where(p => p.Name != "Match"); 
    var items = ((IEnumerable<IHaveMatchProperty>) srProperty.GetValue(searchResult)) 
     // Materialize the enumerable, in case it's backed by something that 
     // would re-create objects each time it's iterated over. 
     .ToList(); 
    foreach (var item in items) 
    { 
     var propertyValues = itemProperties.Select(p => (string)p.GetValue(item)); 
     item.Match = propertyValues.Any(v => searchTerms.Any(v.Contains)); 
    } 
    var orderedItems = items.OrderBy(i => i.Match); 
    srProperty.SetValue(srProperty, orderedItems); 
} 
+0

感谢您的帮助orderedItems是OrderedEnumerable 但需要IEnumerable (作为示例)什么是最好的投射方式? – tic 2014-11-07 18:43:52

+0

@tic:您可能需要使用反射创建一个调用'Enumerable.Cast <>()'。 – StriplingWarrior 2014-11-07 21:23:17

+0

我的演员不能工作,我已经把它放在更新中。 – tic 2014-11-11 17:48:17