views:

754

answers:

2

i have the following linq query that create a left join between two tables:

            var joinResultRows = from leftTable in dataSet.Tables[leftTableName].AsEnumerable()
                             join
                                  rightTable in dataSet.Tables[rightTableName].AsEnumerable()
                                    on leftTable.Field<string>(leftComparedColumnName) equals rightTable.Field<string>(rightComparedColumnName)
                                        into leftJoinedResult
                             select new { leftTable, leftJoinedResult };

i want to get the rows that answer this: the String value in the left column contains the string value in the right column.

i tried this :

            var joinResultRows = from leftTable in dataSet.Tables[leftTableName].AsEnumerable()
                             join
                                  rightTable in dataSet.Tables[rightTableName].AsEnumerable()
                                    on leftTable.Field<string>(leftComparedColumnName).Contains(rightTable.Field<string>(rightComparedColumnName)) equals true
                                        into leftJoinedResult
                             select new { leftTable, leftJoinedResult };

but it doesn't work cause rightTable isn't recognized in the left side of the join.

How do i create a join that results the String.Contains, do i do the contains in the 'where' clause or in the 'On' clause?

+4  A: 

Have you tried a SelectMany?

var result =
 from left in dataSet.Tables[leftTableName].AsEnumerable()
 from right in dataSet.Tables[rightTableName].AsEnumerable()
 where left.Field<string>(leftComparedColumnName).Contains(right.Field<string>(rightComparedColumnName))
 select new { left, right };

Edit:

The following should have the desired effect:

class ContainsEqualityComparer: IEqualityComparer<string>
{
    public bool Equals(string right, string left) { return left.Contains(right); }
    public int GetHashCode(string obj) { return 0; }
}

var result =
    dataSet.Tables[leftTableName].AsEnumerable().GroupJoin(
        dataSet.Tables[rightTableName].AsEnumerable(),
        left => left,
        right => right,
        (left, leftJoinedResult) => new { left = left, leftJoinedResult = leftJoinedResult },
        new ContainsEqualityComparer());

The key comparison is run through the custom IEqualityComparer. Two rows will only be joined when GetHashCode() of left and right are the same, and Equals returns true.

Hope it helps.

Yannick M.
+1 for SelectMany - LINQ only supports "equi-joins" that pair elements in the sequences by key equality.
dahlbyk
selectMany is something i didn't think of , thanks. it does find the matches but doesn't solve my problem,because i need a LEFT JOIN that creates a new joined table or atleast an anonymous type that has two tables - the left one and the join one ( like in my example above). after executing my query above, i built the result table manually using both leftTable, leftJoinedResult. can i make SelectMany left join ?
Rodniko
I believe there is no real way to achieve what you are trying with LinQ and anonymous types. However there are a number of workarounds, I will try to work out for you in the above post.
Yannick M.
I have looked into it a bit further, and I have reproduced the desired effect with a GroupJoin overloading the IEqualityComparer.
Yannick M.
+1  A: 

i solved it by creating SelectMany (join) that the left table holds all records and the right holds nul if there is no match,

var joinResultRows = from leftDataRow in dataSet.Tables[leftTableName].AsEnumerable()
                             from rightDataRow in dataSet.Tables[rightTableName].AsEnumerable()
                             .Where(rightRow => 
                                 {
                                     // Dont include "" string in the Contains, because "" is always contained
                                     // in any string.                                         
                                     if ( String.IsNullOrEmpty(rightRow.Field<string>(rightComparedColumnName)))
                                         return false;

                                     return leftDataRow.Field<string>(leftComparedColumnName).Contains(rightRow.Field<string>(rightComparedColumnName));

                                 }).DefaultIfEmpty() // Makes the right table nulls row or the match row
                              select new { leftDataRow, rightDataRow };

Thant you for the tip :)

Rodniko