将lambda表达式中的逻辑拆分为多个LINQ表达式

时间:2016-11-24 12:20:05

标签: c# linq lambda linq-to-entities

下面的代码示例说明了我的问题。 我如何将lambda的一部分放在别处?

如何从'GetGurus()'方法调用'Foo'? 我希望LINQ将其翻译为1个语句。

public enum GuruLevel
 {
    NotSet,
    Goku,
    SuperSayan
}

 private IEnumerable<PersonInfo> GetGurus()
 {
    using (var context = new CRMContext())
    {
        var persons = context.Person
            .Where(p => p.Experience > 10)
            .OrderBy(p => p.DateOfBirth)
            .Select(p => new PersonInfo())
            {
                StackOverFlowName = p.StackOverFlowName,
                Experience = p.Experience,
                GuruStatus = Foo //(p.Experience > 9000) ? GuruLevel.SuperSayan : GuruLevel.Goku
            }
        return persons.ToList();
    } 
 }

public static System.Linq.Expressions.Expression<Func<Person, GuruLevel>> Foo
{
    get
    {
        return bar => (bar.Experience > 9000) ? GuruLevel.SuperSayan : GuruLevel.Goku;
    }
}

3 个答案:

答案 0 :(得分:1)

这个逻辑是PersonInfo类的责任。此代码通常放在构造函数或工厂类中,并且完全符合单一责任原则。

class PersonInfo
{
    public string StackOverFlowName { get; set; }
    public int Experience { get; set; }
    public GuruLevel GuruStatus { get; set; }

    public void PersonInfo(Person p)
    {
      StackOverFlowName = p.StackOverFlowName;
      Experience = p.Experience;
      GuruStatus = p.Experience > 9000 ? GuruLevel.SuperSayan : GuruLevel.Goku;
    }
}

然后将您的服务方法更改为:

private IEnumerable<PersonInfo> GetGurus()
{
   using (var context = new CRMContext())
   {
       var persons = context.Person
           .Where(p => p.Experience > 10)
           .OrderBy(p => p.DateOfBirth)
           .ToList()
           .Select(p => new PersonInfo(p))
       return persons.ToList();
   } 
}

或将责任转移到工厂:

public class PersonInfoFactory
{
    public PersonInfo Create(Person person)
    {
        return new PersonInfo
        {
            StackOverFlowName = p.StackOverFlowName,
            Experience = p.Experience,
            GuruStatus = ExperienceBasedStatus(p.Experience)
        };
    }

    private GuruLevel ExperienceBasedStatus(int experience) => experience > 9000 ? GuruLevel.SuperSayan : GuruLevel.Goku;
}

答案 1 :(得分:1)

这不是你问题的直接答案,但它与你问的原因有关。

我假设您正在使用某种DB,因此您无法使用常规方法,因为它们不会被DB的上下文所知。

例如,您实际上可以执行使用数据执行某些操作的私有方法,但您无法使用自定义类型或类。在你的例子中,'GuruLevel'将不被DB识别。

我建议不要尝试在DB端进行转换。而是以DB格式检索所需的所有数据,并在应用程序端进行转换。

using (var context = new CRMContext())
{
    var persons = context.Person
        .Where(p => p.Experience > 10)
        .OrderBy(p => p.DateOfBirth)
        .Select(p => new {p.StackOverFlowName, p.Experience }) //or whole object, or other fields if needed
        .ToList() //executes query, all following code will be in a context of application not DB
        .Select(p => new PersonInfo())
        {
            StackOverFlowName = p.StackOverFlowName,
            Experience = p.Experience,
            GuruStatus = GetGuruStatus(p.Experience)
        }
    return persons.ToList();
}

private GuruLevel GetGuruLevel(int exp)
{
    return exp > 9000 ? GuruLevel.SuperSayan : GuruLevel.Goku
}

答案 2 :(得分:0)

改变这一点;

public static Func<Person, GuruLevel> Foo
{
    get
    {
        return bar => (bar.Experience > 9000) ? GuruLevel.SuperSayan : GuruLevel.Goku;
    }
}

然后;

private IEnumerable<PersonInfo> GetGurus()
{
    using (var context = new CRMContext())
    {
        var persons = context.Person
            .Where(p => p.Experience > 10)
            .OrderBy(p => p.DateOfBirth)
            .Select(p => new PersonInfo())
            {
                StackOverFlowName = p.StackOverFlowName,
                Experience = p.Experience,
                GuruStatus = Foo(p)
            }
        return persons.ToList();
    } 
}

只是一张纸条;这是一种奇怪的方式。你可以让Foo成为一个正常的功能,它会很好;

public GuruLevel Foo(Person bar)
{
    return bar.Experience > 9000 ? GuruLevel.SuperSayan : GuruLevel.Goku;
}
...
GuruStatus = Foo(p)