Как создать метод расширения динамического соединения LINQ

Была выпущена библиотека динамических методов расширения LINQ с Visual Studio 2008. Я хотел бы расширить его с помощью метода соединения. Приведенный ниже код завершается ошибкой с исключением совпадения параметров во время выполнения. В чем проблема?

public static IQueryable Join(this IQueryable outer, IEnumerable inner,
                              string outerSelector, string innerSelector, string resultsSelector,
                              params object[] values)
{
    if (inner == null)
        throw new ArgumentNullException("inner");
    if (outerSelector == null)
        throw new ArgumentNullException("outerSelector");
    if (innerSelector == null)
        throw new ArgumentNullException("innerSelector");
    if (resultsSelector == null)
        throw new ArgumentNullException("resultsSelctor");

    LambdaExpression outerSelectorLambda =
        DynamicExpression.ParseLambda(outer.ElementType, null,
                                      outerSelector, values);
    LambdaExpression innerSelectorLambda =
        DynamicExpression.ParseLambda(inner.AsQueryable().ElementType,
                                      null, innerSelector, values);

    ParameterExpression[] parameters = new ParameterExpression[] {
        Expression.Parameter(outer.ElementType, "outer"),
        Expression.Parameter(inner.AsQueryable().ElementType,
        "inner")
    };
    LambdaExpression resultsSelectorLambda =
        DynamicExpression.ParseLambda(parameters, null,
                                      resultsSelector, values);

    return outer.Provider.CreateQuery(
        Expression.Call(
            typeof(Queryable), "Join", new Type[] {
                outer.ElementType,
                inner.AsQueryable().ElementType,
                outerSelectorLambda.Body.Type,
                innerSelectorLambda.Body.Type,
                resultsSelectorLambda.Body.Type
            },
            outer.Expression, inner.AsQueryable().Expression,
            Expression.Quote(outerSelectorLambda),
            Expression.Quote(innerSelectorLambda),
            Expression.Quote(resultsSelectorLambda))
        );
}

person Royd Brayshay    schedule 23.12.2008    source источник
comment
не могли бы вы вставить точное сообщение об ошибке?   -  person Perpetualcoder    schedule 23.12.2008
comment
Можете ли вы опубликовать пример использования вашего join. Я хотел бы использовать его, но я такой новичок в LINQ. Спасибо.   -  person joe    schedule 02.02.2011
comment
Эй, ты знал, что это попало в книгу? Я нашел ссылку на него в Modern Data Access with Entity Framework Core 2018.   -  person johnny    schedule 07.01.2019


Ответы (3)


Я сам сейчас исправил. Это была ошибка школьника, передавшая слишком много параметров вызову CreateQuery(...). Вставьте следующий код в файл Dynamic.cs в классе DynamicQueryable для динамического метода расширения Join. Исходный код примера проекта DynamicQuery можно найти по адресу http://code.msdn.microsoft.com/csharpsamples< /а>.
Наслаждайтесь.

    public static IQueryable Join(this IQueryable outer, IEnumerable inner, string outerSelector, string innerSelector, string resultsSelector, params object[] values)
    {
        if (inner == null) throw new ArgumentNullException("inner");
        if (outerSelector == null) throw new ArgumentNullException("outerSelector");
        if (innerSelector == null) throw new ArgumentNullException("innerSelector");
        if (resultsSelector == null) throw new ArgumentNullException("resultsSelctor");

        LambdaExpression outerSelectorLambda = DynamicExpression.ParseLambda(outer.ElementType, null, outerSelector, values);
        LambdaExpression innerSelectorLambda = DynamicExpression.ParseLambda(inner.AsQueryable().ElementType, null, innerSelector, values);

        ParameterExpression[] parameters = new ParameterExpression[] {
            Expression.Parameter(outer.ElementType, "outer"), Expression.Parameter(inner.AsQueryable().ElementType, "inner") };
        LambdaExpression resultsSelectorLambda = DynamicExpression.ParseLambda(parameters, null, resultsSelector, values);

        return outer.Provider.CreateQuery(
            Expression.Call(
                typeof(Queryable), "Join",
                new Type[] {outer.ElementType, inner.AsQueryable().ElementType, outerSelectorLambda.Body.Type, resultsSelectorLambda.Body.Type  },
                outer.Expression, inner.AsQueryable().Expression, Expression.Quote(outerSelectorLambda), Expression.Quote(innerSelectorLambda), Expression.Quote(resultsSelectorLambda)));
    }


    //The generic overload.
    public static IQueryable<T> Join<T>(this IQueryable<T> outer, IEnumerable<T> inner, string outerSelector, string innerSelector, string resultsSelector, params object[] values)
    {
        return (IQueryable<T>)Join((IQueryable)outer, (IEnumerable)inner, outerSelector, innerSelector, resultsSelector, values);
    }
person Royd Brayshay    schedule 05.01.2009
comment
Простое использование этого кода выглядит следующим образом: var result = myDbContext.Person .Join(myDbContext.Roles,"new(Id as firstKey,SomeOtherId as secondKey)","new(PersonId as firstKey,AlternativeId as secondKey)", "new (inner as r, outer as p)"); обратите внимание, что если он объединен в нескольких столбцах, вы должны использовать его как [somekey], если они не имеют одинаковых имен, иначе произойдет ошибка. Также в селекторе результатов вы используете ключевые слова «внешний» и «внутренний». - person user2945722; 13.06.2016
comment
Как реализовать, если я хочу сравнить строку без учета регистра в Join? - person Shekhar Dalvi; 20.03.2019
comment
Как я могу сделать то же самое, если мое количество таблиц также является динамическим? - person Ameerudheen.K; 11.03.2020

Вы можете установить пакет nuget System.Linq.Dynamic.Core — https://github.com/StefH/System.Linq.Dynamic.Core

В нем реализован метод соединения вместе с различными другими вспомогательными методами.

Используя эту библиотеку, вы можете выполнить простое соединение следующим образом.

myContext.TableA.Join(myContext.TableB,'Id','TableAId','outer',null)

в селекторе результатов outer и inner являются ключевыми словами для доступа к результату соединения.

Использование ключа с несколькими свойствами и/или выбор результата с несколькими свойствами можно выполнить следующим образом.

myContext.TableA.Join(myContext.TableB,'new (Id as key1,Code as key2)','new (TableAId as key1,AnotherCol as key2)','new(outer.Id,inner.Desc)',null)

person user2945722    schedule 13.06.2016

Вот пример кода, показывающий объединение нескольких столбцов. Используя datatable и datarows, вам нужно всегда обращаться к полям через индексатор.

  DataTable t1 = new DataTable();
  t1.Columns.Add("FundId", typeof(int));
  t1.Columns.Add("Date", typeof(DateTime));
  t1.Columns.Add("CodeA", typeof(string));
  t1.Rows.Add(1, new DateTime(2010, 01, 01), "A1");
  t1.Rows.Add(2, new DateTime(2010, 01, 01), "A2");
  t1.Rows.Add(3, new DateTime(2010, 01, 01), "A3");

  DataTable t2 = new DataTable();
  t2.Columns.Add("FundId", typeof(int));
  t2.Columns.Add("Date", typeof(DateTime));
  t2.Columns.Add("CodeB", typeof(string));
  t2.Rows.Add(1, new DateTime(2010, 01, 01), "B1");
  t2.Rows.Add(2, new DateTime(2010, 01, 01), "B2");
  t2.Rows.Add(3, new DateTime(2010, 01, 01), "B3");

  IQueryable outerTable = t1.AsEnumerable().AsQueryable();
  IEnumerable innerTable = t2.AsEnumerable();

  var query = outerTable.Join
    (
      innerTable, 
      "new(get_Item(0) as FundId, get_Item(1) as Date)",
      "new(get_Item(0) as FundId, get_Item(1) as Date)",
      "new(outer.get_Item(0) as FundId, outer.get_Item(2) as CodeA, inner.get_Item(2) as CodeB)"
    );
person TREx    schedule 23.11.2010
comment
Это не отвечает на вопрос. Здесь вы используете метод Join, предоставленный Linq, вопрос был о создании этого метода, потому что Linq еще не предоставил его. - person psycho; 06.12.2012