简体   繁体   中英

How to convert this lambda expression to linq

I can't read complicated lambda expression and only the super basic lambda expression only I know I am just starting to study lambda.

As my title above how to convert this lambda into linq?

var train = db.Certificates
            .Join(db.TrainingSchedules, a => a.CertificateId, b => b.CertificateId, (a, b) => new { a, b })
            .Where(x => x.a.Year.Value.Year == year && x.a.TrainingTypeId.Value == trainingTypeId && x.a.IsApproved.Value && x.b.EndDate >= DateTime.Now)
            .Select(z => z.a).Distinct().Where(q => !db.Registrations.Where(s => s.EmployeeId == empId).Select(t => t.Certificate).Any(u => u.CertificateId == q.CertificateId));

Can someone explain to me why it has a different variables?. Like x , q , z , b ?

As my title above how to convert this lambda into linq?

Have you every worked in Linq to know how does it looks like ?

If the above specified code is not Linq , then what is Linq , Lambda is an integral part of fluent representation of the Linq , since most of the APIs would need a Func delegate , that's where Lambda comes in.

Now regarding x , q , z , b , what do they represent ?

The Linq APIs are nothing but extension methods for IEnumerable<T> , like List<T> , Dictionary<TK,TV> , whether we write the fluent' or the Sql syntax these variables represent, each and every element in the collection, which is processed as part of logic provided by the Func Delegate , you can certainly use and should use more credible variable to represent the exact thing, similar to other parts of code where int x, float y, DateTime z` is a bad way to code

Regarding the statement posted above consider following changes:

  • Rename a,b as cert,ts , which refers to a Certificate and Training Schedule classes respectively
  • Instead of generating anonymous type new { a, b } , create a class like CerificateTrainingSchedule that has all the elements of the Certificate and Training Schedule class respectively, it would be easier to work as you move forward
  • Rename x as cts , to represent combined CerificateTrainingSchedule
  • If its easy to read then separate Where clause in multiple chains, like:

     .Where(cts => cts.a.Year.Value.Year == year) .Where(cts => cts.a.TrainingTypeId.Value == trainingTypeId) .Where(cts => cts.a.IsApproved.Value) .Where(cts => cts.b.EndDate >= DateTime.Now) 
  • Similarly the names of other variables can be modified to represent the true class and its objects, instead of random a,b,c,d . Also calls can be chained for clear understanding of the logic

Edit - // Modified Linq Query

// Joined / Merged version of Certificate and Training Schedule, add more fields as required, current one is based on certain assumptions of the fields / properties in the Certificate & TrainingSchedule classes respectively

public class CertificateTrainingSchedule
{
   public int Year {get; set;} // Certificate Class Property
   public int TrainingTypeId {get; set;} // Certificate Class Property
   public bool IsApproved {get; set;} // Certificate Class Property
   public DateTime EndDate {get; set;} // TrainingSchedule Class Property
}

var train = db.Certificates
            .Join(db.TrainingSchedules, cert => cert.CertificateId, ts => ts.CertificateId, (cert, ts) => new CertificateTrainingSchedule{ Year = cert.Year, TrainingTypeId = cert.TrainingTypeId, IsApproved = cert.IsApproved,EndDate = ts.EndDate})
            .Where(cts => cts.Year == year)
            .Where(cts => cts.TrainingTypeId == trainingTypeId)
            .Where(cts => cts.IsApproved)
            .Where(cts => cts.EndDate >= DateTime.Now)
            .Select(cts => new {cts.Year,cts.TrainingTypeId,cts.IsApproved})
            .Distinct() // Allowing anonymous type to avoid IEqualityComparer<Certificate>
            .Where(certMain => !db.Registrations.Where(s => s.EmployeeId == empId)
                                                .Select(cert => new Certificate{Year = cert.Year,TrainingTypeId = cert.TrainingTypeId,IsApproved = cert.IsApproved})
                                                .Any(cert => cert.CertificateId == certMain.CertificateId))

I assume that by your question, you mean you want a query expression that is equivalent to calling the LINQ methods explicitly. Without a good Minimal, Complete, and Verifiable code example , it's impossible to know for sure what a correct example would be. However, the following is I believe what you're looking for:

var train =
    from q in
         (from x in
              (from a in db.Certificates
               join b in db.TrainingSchedules on a.CertificateId equals b.CertificateId
               select new { a, b })
          where x.a.Year.Value.Year == year && x.a.TrainingTypeId == trainingTypeId &&
                x.a.IsApproved.Value && x.b.EndDate >= DateTime.Now
          select x.a).Distinct()
     where !(from s in db.Registrations where s.EmployeeId == empId select s.Certificate)
       .Any(u => u.CertificateId == q.CertificateId)
     select q;

Note that not all of the LINQ methods have a C# query expression language equivalent. In particular, there's no equivalent for Distinct() or Any() , so these are still written out explicitly.

Can someone explain to me why it has a different variables?. Like x , q , z , b?

Each lambda expression has the input on the left side of the => and the result expression on the right. The variables you're referring to are the inputs. These are commonly written using single letters when writing lambda expressions, because the a lambda expression is so short, the meaning can be clear without a longer variable name. For that matter, independent lambda expressions could even use the same variable name.

Note that in the query expression syntax, not all of the variables "made it". In particular, we lost z and t because those variables were superfluous.

In an expression this long, it's possible you might find longer variable names helpful. But it's a trade-off. The query expression language is meant to provide a compact way to represent queries on data sources. Longer variable names could make it harder to understand the query itself, even as it potentially makes it easier to understand the intent of each individual part of the expression. It's very much a matter of personal preference.

I can't read complicated lambda expression and only the super basic lambda expression only I know I am just starting to study lambda.

Try reading this:

var train = db.Certificates
    .Where(c => c.Year.Value.Year == year && 
                c.TrainingTypeId.Value == trainingTypeId &&
                c.IsApproved.Value &&
                c.TrainingSchedules.Any(ts => ts.EndDate >= DateTime.Now) &&
                !c.Registrations.Any(r => r.EmployeeId == empId));

If you can, then you are just fine.

Note that this is not an exact translation of the sample query, but is functionally equivalent (should produce the same result). The sample query is a good example of badly written query - variable naming, unnecessary multiplicative Join which requires then a Distinct operator (while GroupJoin would do the same w/o the need of Distinct ), inconsistent handling of two similar detail criteria ( Join for TrainingSchedules and Any for Registrations ), overcomplicated criteria for Registrations part etc.

Shortly, don't write such queries. Concentrate on the desired result from the query and use the most logical constructs to express it. Avoid manual joins when you have navigation properties. If you don't have navigation properties, then add them to the model - it's easy one time action which helps a lot when writing queries. For instance, in my translation I assume you have something like this:

class Certificate
{
    // Other properties ...
    public ICollection<TrainingSchedule> TrainingSchedules { get; set; }  
    public ICollection<Registration> Registrations { get; set; }  
}

class TrainingSchedule
{
    // Other properties ...
    public Certificate Certificate { get; set; }  
}

class Registration
{
    // Other properties ...
    public Certificate Certificate { get; set; }  
}

UPDATE: Here is the same using the query syntax:

var train = 
    from c in db.Certificates
    where c.Year.Value.Year == year && 
          c.TrainingTypeId.Value == trainingTypeId &&
          c.IsApproved.Value &&
          c.TrainingSchedules.Any(ts => ts.EndDate >= DateTime.Now) &&
          !c.Registrations.Any(r => r.EmployeeId == empId)
    select c;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM