Объединение LINQ-запроса с предикатом

#c# #.net #linq #linq-to-sql

#c# #.net #linq #linq-to-sql

Вопрос:

У меня возникли некоторые проблемы с запросом LINQ в C #.

У меня в базе данных одни и те же таблицы с одинаковой структурой. Итак, сегодня я беспокоился о своем запросе LINQ.

Подробнее, я хочу объединить некоторые таблицы, используя предикаты.

У меня есть функция, которая имеет два параметра. Первым параметром является некоторый контекст (например, это может быть ProductContext, CarContext, CatContext и т. Д.).

Второй параметр List<something> — это тот, который я соединю с моим первым параметром — контекстом.

Мне не нужен набор методов.

Я добавил образец:

     public Element[] GetByIds( MyPredicateContext, Guid[] ids)
    {
        return 
            from id in ids
            join element in MyPredicateContext on id equals element.Id
            select
                new Element
                {
                    Id = element.Id,
                    Description = element.JobDescription,
                };
    }
  

Комментарии:

1. Почему вам нужен контекст в качестве параметра, а не таблица? Или вы хотите оба? И я думаю, что все ваши элементы не имеют JobDescription свойства, не так ли?

Ответ №1:

Если запрос правильный, одна из основных проблем, которую я вижу, заключается в том, что возвращаемый тип — это массив элементов, тогда как вы пытаетесь вернуть IEnumerable . Возможно, выполнение.toArray() в результирующем наборе может решить проблему.

Комментарии:

1. Не имеет значения для этого вопроса

2. Извиняюсь, возможно, я не понял проблему здесь, не могли бы вы подробнее объяснить точную ошибку, которую вы получаете.

3. Извините, если вы имеете в виду предикат .net, они не подходят для использования здесь. Филип дал хорошее решение, и это может быть хорошим началом. Я бы определил общий интерфейс между таблицами Car, Product и Cat и передал этот интерфейс вместо передачи MyPredicateContext, как предложил Филип. Пожалуйста, обратите внимание, что ваши 3 таблицы должны наследоваться от этого общего интерфейса. Таким образом, вы можете передать ссылку на одну из этих таблиц методу, предоставленному Filip.

Ответ №2:

Почему бы и нет

 return MyPredicateContext.Where(element=>ids.Contains(element.Id))
          .Select(e=>new Element()
             {
                 Id = element.Id, 
                 Description = element.JobDescription
             }).ToArray();
  

Комментарии:

1. Обратите внимание, там было соединение

Ответ №3:

Прежде всего, вы не можете создать новый IQueryable из массива, это приведет к извлечению всего в памяти и фильтрации там. Вы работаете с выражениями, а не с кодом c #, когда вы выполняете LINQ с SQL, это будет работать только с материалом в памяти (IEnumerable). Ваш запрос будет работать в SQL, если вы сделаете это так

 from element in MyPredicateContext
where ids.Contains(element.Id)
select  new Element
                {
                    Id = element.Id,
                    Description = element.JobDescription,
                }
  

Учитывая, что тип IQueryable где T — интерфейс или класс.
Метод end будет выглядеть примерно так

 public interface IElement
        {
            Guid Id { get; }
            string JobDescription { get; }
        }

        public Element[] GetByIds<T>(IQueryable<T> myPredicateContext, Guid[] ids) where T:IElement
        {
            return (from element in myPredicateContext
                    where ids.Contains(element.Id)
                    select new Element
                                    {
                                        Id = element.Id,
                                        Description = element.JobDescription,
                                    }).ToArray();

        }
  

Есть способы сделать это без дженериков, но они немного более продвинутые и их будет сложно поддерживать.

Вот метод, который будет работать со всеми типами T, и правильный IQueryable приведет к хорошему sql, как я уже указывал, он немного более продвинутый, и вам нужно будет посмотреть, как работает выражение.

 public static Element[] GetById<T, Tkey>(IQueryable<T> items,Tkey[] ids)
        {
            var type = typeof(T);
            ParameterExpression param = Expression.Parameter(type);
            var list = Expression.Constant(ids);
            //The names of the properties you need to get if all models have them and are named the same and are the same type this will work
            var idProp = Expression.Property(param, "Id");
            var descriptionProp = Expression.Property(param, "JobDescription");

            var contains = typeof(Enumerable).GetMethods().First(m => m.Name == "Contains" amp;amp; m.GetParameters().Count() == 2).MakeGenericMethod(typeof(Tkey));
            var where = Expression.Lambda<Func<T, bool>>(Expression.Call(contains, list, idProp), param);

            return (items.
                Where(where).
                Select(Expression.Lambda<Func<T, Element>>(
                    Expression.MemberInit(
                    Expression.New(typeof(Element)),
                        Expression.Bind(typeof(Element).GetProperty("Id"), idProp),
                        Expression.Bind(typeof(Element).GetProperty("Description"), descriptionProp)), 
                        param))).ToArray();
        }
  

Вызов GetById(items, new Guid[] { Guid.NewGuid() })

Комментарии:

1. Обратите внимание, там было соединение

2. @Andrew Contains В этом случае делает то же самое, что и ваше соединение.

3. @Филип Кордас. Хорошо, извините, это моя ошибка, но как класс из базы данных может быть унаследован от IElement?

4. Linq sql — сначала параметры региона DECLARE @p0 NVarChar(1000) = '1' DECLARE @p1 NVarChar(1000) = '2' -- EndRegion SELECT [t0].[CODE], [t0].[DESCRIPTION], [t0].[DISPLAYINDEX], [t0].[FILTER] FROM [BCPACTIVITYPLANRISK] AS [t0] WHERE [t0].[CODE] IN (@p0, @p1) GO Second SELECT [t0].[CODE], [t0].[DESCRIPTION], [t0].[DISPLAYINDEX], [t0].[FILTER] FROM [BCPACTIVITYPLANRISK] AS [t0]

5. Ваша модель данных должна иметь свойство Id и свойство JobDescription если ваш класс базы данных имеет два свойства, вы можете просто использовать его как T