Вызов конструктора класса в LINQ - PullRequest
0 голосов
/ 01 июля 2019

Я играл с несколькими техниками, но все еще не нашел то, чем я доволен.

Если я сделаю что-то вроде;

        IQueryable<TestClass> tests = new MyDbContext()
            .LdbRecords
            .Select(r => new TestClass() { Id = r.RecordId, Name = r.RecordName });
        Console.WriteLine(tests.Count());

EF выполняет относительно разумный запрос;

SELECT 
    [GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[ldb_record] AS [Extent1]
)  AS [GroupBy1]

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

Конструктор выглядит так:

        public TestClass(int id, string name)
        {
            Id = id;
            Name = name;
        }

Каждая техника, которую я нашел до сих пор, например;

        IEnumerable<TestClass> tests = new MyDbContext()
            .LdbRecords
            .AsEnumerable()
            .Select(r => new TestClass(r.RecordId, r.RecordName));
        Console.WriteLine(tests.Count());

оказывает пагубное влияние на генерацию SQL-запроса;

SELECT 
  [Extent1].[record_id] AS [record_id], 
  [Extent1].[record_name] AS [record_name], 
  [Extent1].[record_note] AS [record_note]
FROM [dbo].[ldb_record] AS [Extent1]

Есть ли метод, который я могу использовать (возможно, с использованием делегатов, выражений или функций?), Который позволит мне использовать мой конструктор TestClass без перечисления?

Я был бы рад иметь частный конструктор без параметров в TestClass, который функция / делегат и т. Д. Могли бы вызывать?

Спасибо

Ответы [ 3 ]

0 голосов
/ 01 июля 2019

Ваш результирующий SQL отличается, потому что результаты, которые они возвращают, отличаются.В первом примере все, что вы запрашиваете у БД (в конце), это счетчик, который дает вам Linq.Вы никогда не используете сами сущности, поэтому Linq никогда не запрашивает их.

Во втором примере вы запрашиваете сущности с помощью вызова AsEnumerable, инструктирующего Linq переключиться с контекста базы данных на контекст в памяти.Поэтому, когда вы запрашиваете счет, Linq должен извлечь записи из базы данных , прежде чем подсчитает их.

Если вы на самом деле использовали сущностей в первом примере, запрос к БД будет очень похожим (возможно, исключая неиспользуемый столбец, который должен быть незначительным).Если вам нужен только счетчик, вам не нужно использовать любой конструктор.

Простой тест, чтобы увидеть, какой запрос генерируется, если вы перечислите первый пример, измените свой вывод на:

Console.WriteLine(tests.AsEnumerable().Count());
0 голосов
/ 02 июля 2019

Похоже, то, что вы пытаетесь решить, действительно уже решено.

при условии:

 var tests = new MyDbContext()
        .LdbRecords         
        .Select(r => new LdbRecord());

Вы получите IQueryable из LdbRecord и вызовете .ToList ()список этих моделей базы данных со всеми его свойствами.

То, что вы должны делать, это использовать что-то вроде AutoMapper.Ваш следующий бит логики будет выглядеть примерно так:

 var results = Mapper.Map<TestClass>(tests.ToList());

Вы можете настроить свой AutoMapper так, чтобы он совпадал с именами свойств, но также настраивал свойства для игнорирования или совпадения от 1 до 1.

var mapper = config.CreateMapper<LdbRecord, TestClass>();
mapper.ForMember(dest => dest.TestId, opt => opt.MapFrom(src => src.Id));

Это гарантирует, что ваш код будет написан один раз (вам не нужно обходить кодовую базу, меняя параметры в конструкторе.

Кроме того, вы можете заставить AutoMapper генерировать исключение для несопоставленных свойств.Если доверие AutoMapper к реализации TestClass.Id и LdbRecords.Id недостаточно, то приложение не будет собираться / запускаться (модульное тестирование здесь также идеально).

0 голосов
/ 01 июля 2019

я придумал;

    IQueryable<TestClass> tests = new MyDbContext()
        .LdbRecords
        .Select(TestClass.ConvertPoco);
    Console.WriteLine(tests.Count());

    public class TestClass
    {
        public int Id { get; set; }
        public string Name { get; set; }

        private TestClass()
        {

        }

        public TestClass(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public static Expression<Func<LdbRecord, TestClass>> ConvertPoco
        {
            get
            {
                return p => new TestClass() { Id = p.RecordId, Name = p.RecordName };
            }
        }
    }

, который оценивает точно такой же запрос, как метод конструктора без параметров;

SELECT 
    [GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[ldb_record] AS [Extent1]
)  AS [GroupBy1]

Есть ли какие-либо достижения в этом вопросе?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...