LEFT OUTER JOIN in LINQ

后端 未结 22 2512
臣服心动
臣服心动 2020-11-21 04:49

How to perform left outer join in C# LINQ to objects without using join-on-equals-into clauses? Is there any way to do that with where clause? Corr

相关标签:
22条回答
  • 2020-11-21 05:13

    This is the general form (as already provided in other answers)

    var c =
        from a in alpha
        join b in beta on b.field1 equals a.field1 into b_temp
        from b_value in b_temp.DefaultIfEmpty()
        select new { Alpha = a, Beta = b_value };
    

    However here's an explanation that I hope will clarify what this actually means!

    join b in beta on b.field1 equals a.field1 into b_temp
    

    essentially creates a separate result set b_temp that effectively includes null 'rows' for entries on the right hand side (entries in 'b').

    Then the next line:

    from b_value in b_temp.DefaultIfEmpty()
    

    ..iterates over that result set, setting the default null value for the 'row' on the right hand side, and setting the result of the right hand side row join to the value of 'b_value' (i.e. the value that's on the right hand side,if there's a matching record, or 'null' if there isn't).

    Now, if the right hand side is the result of a separate LINQ query, it will consist of anonymous types, which can only either be 'something' or 'null'. If it's an enumerable however (e.g. a List - where MyObjectB is a class with 2 fields), then it's possible to be specific about what default 'null' values are used for its properties:

    var c =
        from a in alpha
        join b in beta on b.field1 equals a.field1 into b_temp
        from b_value in b_temp.DefaultIfEmpty( new MyObjectB { Field1 = String.Empty, Field2 = (DateTime?) null })
        select new { Alpha = a, Beta_field1 = b_value.Field1, Beta_field2 = b_value.Field2 };
    

    This ensures that 'b' itself isn't null (but its properties can be null, using the default null values that you've specified), and this allows you to check properties of b_value without getting a null reference exception for b_value. Note that for a nullable DateTime, a type of (DateTime?) i.e. 'nullable DateTime' must be specified as the 'Type' of the null in the specification for the 'DefaultIfEmpty' (this will also apply to types that are not 'natively' nullable e.g double, float).

    You can perform multiple left outer joins by simply chaining the above syntax.

    0 讨论(0)
  • 2020-11-21 05:14
    class Program
    {
        List<Employee> listOfEmp = new List<Employee>();
        List<Department> listOfDepart = new List<Department>();
    
        public Program()
        {
            listOfDepart = new List<Department>(){
                new Department { Id = 1, DeptName = "DEV" },
                new Department { Id = 2, DeptName = "QA" },
                new Department { Id = 3, DeptName = "BUILD" },
                new Department { Id = 4, DeptName = "SIT" }
            };
    
    
            listOfEmp = new List<Employee>(){
                new Employee { Empid = 1, Name = "Manikandan",DepartmentId=1 },
                new Employee { Empid = 2, Name = "Manoj" ,DepartmentId=1},
                new Employee { Empid = 3, Name = "Yokesh" ,DepartmentId=0},
                new Employee { Empid = 3, Name = "Purusotham",DepartmentId=0}
            };
    
        }
        static void Main(string[] args)
        {
            Program ob = new Program();
            ob.LeftJoin();
            Console.ReadLine();
        }
    
        private void LeftJoin()
        {
            listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach
                (z =>
                {
                    Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt);
                });
        }
    }
    
    class Employee
    {
        public int Empid { get; set; }
        public string Name { get; set; }
        public int DepartmentId { get; set; }
    }
    
    class Department
    {
        public int Id { get; set; }
        public string DeptName { get; set; }
    }
    

    OUTPUT

    0 讨论(0)
  • 2020-11-21 05:15

    I would like to add that if you get the MoreLinq extension there is now support for both homogenous and heterogeneous left joins now

    http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm

    example:

    //Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them
    
    return DataContext.ClientCompany
        .LeftJoin(DataContext.Employees,                         //Table being joined
            company => company.ClientCompanyID,                  //First key
            employee => employee.ClientCompanyID,                //Second Key
            company => new {company, employee = (Employee)null}, //Result selector when there isn't a match
            (company, employee) => new { company, employee });   //Result selector when there is a match
    

    EDIT:

    In retrospect this may work, but it converts the IQueryable to an IEnumerable as morelinq does not convert the query to SQL.

    You can instead use a GroupJoin as described here: https://stackoverflow.com/a/24273804/4251433

    This will ensure that it stays as an IQueryable in case you need to do further logical operations on it later.

    0 讨论(0)
  • 2020-11-21 05:16

    Overview: In this code snippet, I demonstrate how to group by ID where Table1 and Table2 have a one to many relationship. I group on Id, Field1, and Field2. The subquery is helpful, if a third Table lookup is required and it would have required a left join relationship. I show a left join grouping and a subquery linq. The results are equivalent.

    class MyView
    {
    public integer Id {get,set};
        public String Field1  {get;set;}
    public String Field2 {get;set;}
        public String SubQueryName {get;set;}                           
    }
    
    IList<MyView> list = await (from ci in _dbContext.Table1
                                                   join cii in _dbContext.Table2
                                                       on ci.Id equals cii.Id
    
                                                   where ci.Field1 == criterion
                                                   group new
                                                   {
                                                       ci.Id
                                                   } by new { ci.Id, cii.Field1, ci.Field2}
    
                                               into pg
                                                   select new MyView
                                                   {
                                                       Id = pg.Key.Id,
                                                       Field1 = pg.Key.Field1,
                                                       Field2 = pg.Key.Field2,
                                                       SubQueryName=
                                                       (from chv in _dbContext.Table3 where chv.Id==pg.Key.Id select chv.Field1).FirstOrDefault()
                                                   }).ToListAsync<MyView>();
    
    
     Compared to using a Left Join and Group new
    
    IList<MyView> list = await (from ci in _dbContext.Table1
                                                   join cii in _dbContext.Table2
                                                       on ci.Id equals cii.Id
    
                           join chv in _dbContext.Table3
                                                      on cii.Id equals chv.Id into lf_chv
                                                    from chv in lf_chv.DefaultIfEmpty()
    
                                                   where ci.Field1 == criterion
                                                   group new
                                                   {
                                                       ci.Id
                                                   } by new { ci.Id, cii.Field1, ci.Field2, chv.FieldValue}
    
                                               into pg
                                                   select new MyView
                                                   {
                                                       Id = pg.Key.Id,
                                                       Field1 = pg.Key.Field1,
                                                       Field2 = pg.Key.Field2,
                                                       SubQueryName=pg.Key.FieldValue
                                                   }).ToListAsync<MyView>();
    
    0 讨论(0)
  • 2020-11-21 05:17

    This is a SQL syntax compare to LINQ syntax for inner and left outer joins. Left Outer Join:

    http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html

    "The following example does a group join between product and category. This is essentially the left join. The into expression returns data even if the category table is empty. To access the properties of the category table, we must now select from the enumerable result by adding the from cl in catList.DefaultIfEmpty() statement.

    0 讨论(0)
  • 2020-11-21 05:18

    As per my answer to a similar question, here:

    Linq to SQL left outer join using Lambda syntax and joining on 2 columns (composite join key)

    Get the code here, or clone my github repo, and play!

    Query:

            var petOwners =
                from person in People
                join pet in Pets
                on new
                {
                    person.Id,
                    person.Age,
                }
                equals new
                {
                    pet.Id,
                    Age = pet.Age * 2, // owner is twice age of pet
                }
                into pets
                from pet in pets.DefaultIfEmpty()
                select new PetOwner
                {
                    Person = person,
                    Pet = pet,
                };
    

    Lambda:

            var petOwners = People.GroupJoin(
                Pets,
                person => new { person.Id, person.Age },
                pet => new { pet.Id, Age = pet.Age * 2 },
                (person, pet) => new
                {
                    Person = person,
                    Pets = pet,
                }).SelectMany(
                pet => pet.Pets.DefaultIfEmpty(),
                (people, pet) => new
                {
                    people.Person,
                    Pet = pet,
                });
    
    0 讨论(0)
提交回复
热议问题