检测LambdaExpression是否用于对象的属性

时间:2013-10-28 15:07:00

标签: c# lambda

我正在尝试通过传递需要补水的子对象的表达式来在存储库中实现一种急切的加载功能。

    Candidate Get(int candidateId, params Expression<Func<Candidate, object>>[] includes);

所以我的服务可以用这样的方式调用:

        candidateRepository.Get(1, p => p.Notes, p => p.Profile, p => p.Application);

Notes,Profile和Application都是Candidate的所有属性。像这样:

    public class Candidate
    {
        public string Name { get; set; }

        public IList<CandidateNote> Notes { get; set; }

        public Profile Profile { get; set; }

        public Application Application { get; set; }
    }

因此,在存储库中我需要确定是否将属性传递到表达式params以实际尝试保湿它。但我没有看到实现这一目标的优雅方式。

    public Candidate Get(int candidateId, params Func<Candidate, object>[] includes)
    {
        ...

        if (candidate.Notes property was specified?)
        {
           candidate.Notes = this.candidateNoteRepository.List(candidateId);
        }

        return candidate;
    }

看起来我可以通过(includes[0].Body as MemberExpression).Member.Name从表达式中获取属性名称,但似乎应该有一种更简单的方法。另外,我不喜欢字符串比较。我真的不想这样做,除非我必须:

        if (includes[0].Body as MemberExpression).Member.Name == "Notes")
        {

它似乎应该是非常简单的东西,如

        if (includes[0].Is(candidate.Notes) or includes[0](candidate.Notes).Match())
        {

它出现了,我非常希望将包含作为一个表达式数组,因为虽然我今天正在使用ADO.NET,但我们计划最终采用实体框架的方式,这些表达式将会使用Include方法非常好地工作。

        return this.Filter(m => m.Candidates.CandiateId == candidateId)
            .Include(includes[0])

2 个答案:

答案 0 :(得分:1)

你可能不想这样做。它将强制您在编译时指定所有属性值,这将既乏味又容易出错,并且不易扩展。

相反,你最好只从表达式中获取MemberInfo对象,然后使用它来获取该成员的值并设置该成员的给定对象的值。

从帮助器开始,按照您显示的模式从表达式中获取MemberInfo对象:

public static MemberInfo GetMemberInfo(LambdaExpression expression)
{
    var body = expression.Body as MemberExpression;
    if (body == null)
        return null;
    else
        return body.Member;
}

然后,您可以获取需要获取的成员对象的集合:

var members = includes.Select(selector => GetMemberInfo(selector))
    .Where(member => member != null)
    //this validates the member is from the type itself; 
    //remove if such validation isn't desired
    .Intersect(typeof(Candidate).GetMembers());

然后通过查询或实际获取数据所需的其他任何内容获取所有成员的值。

然后创建新对象并填充每个成员的值:

var newItem = new Candidate();

foreach(var member in members)
{
    //Note that GetValueForMember shouldn't be doing a query.
    //it should be getting getting the value from the results
    member.SetValue(newItem, GetValueForMember(member));
}

答案 1 :(得分:0)

正如你所说:字符串比较不是一个好方法,因为它不易扩展。您可以更仔细地检查成员,以确定它是否是有效的包含。
一种选择是查看属性的返回类型。如果它是从IEnumerable派生的,那么它可能是一个有效的包含。
如果您无法在属性本身中找到基于决策的必要事实,则可以使用自定义属性在类中标记包含属性。然后,您可以检查可能包含的CustomAttributes属性,以确定它是否是有效的包含。