Присоединение 4 DataTables с Linq - PullRequest
0 голосов
/ 26 января 2019

У меня есть 4 DataTable, к которым я пытаюсь присоединиться, но не могу понять, как эффективно это сделать.

Я получил первые две таблицы для объединения, создав третий объект apptDetails, которыйявляется IEnumerable DataRows.У меня возникли проблемы с возвращением его в DataTable, поэтому я могу сделать больше соединений на нем, хотя.Я получаю сообщение об ошибке apptDetails.CopyToDataTable() из: 'IEnumerable' does not contain a definition for 'CopyToDataTable' and no accessible extension method 'CopyToDataTable' accepting a first argument of type 'IEnumerable' could be found (are you missing a using directive or an assembly reference?)

DataTable customer = ETL.ParseTable("customer");
DataTable appointments = ETL.ParseTable("appointments");
IEnumerable apptDetails = from t1 in customer.AsEnumerable()
    join t2 in appointments.AsEnumerable() on Convert.ToInt32(t1["customerId"]) equals Convert.ToInt32(t2["customerId"])
    into tableGroup
     select new
       {
        customerId = t1["customerId"],
        TotalAppointments = tableGroup.Count(),
        appointment_missed = Convert.ToInt32(t1["MissedAppt"]),
        appointment_show_rate = (
                                    tableGroup.Count()>0 ? 
                                        Math.Round((1 - ((double)Convert.ToInt32(t1["MissedAppt"]) / (double)tableGroup.Count())),2)
                                        : 0
                                )

        };
DataTable dt = apptDetails.CopyToDataTable();

Изначально я использовал var apptDetails, но похоже, что мне нужно больше типов, поэтому я попробовал что-то вроде следующего:

 IEnumerable<DataRow> apptDetails
 IEnumerable<EnumerableRowCollection> apptDetails
 as well as:
 DataTable dt = apptDetails.CopyToDataTable<DataRow>();
 DataTable dt = apptDetails.CopyToDataTable<EnumerableRowCollection>();

Мне нужно объединить таблицы клиентов и встреч, а затем добавить новые столбцы в единый плоский стол.Что мне не хватает в том, как я это делаю, или есть лучший способ сделать это?

Производительность является фактором, поскольку мы говорим о 20 000 клиентов и 80 000 встреч, плюс после этого будет еще 2-3 стола, чтобы присоединиться, поэтому я хотел бы изучить "Правильный" способ сделать это с помощью Linq дляэто наиболее полно.

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Вам следует больше поработать над разделением проблем : отделите метод внутреннего хранения ваших данных (DataTables) от обработки данных (объедините данные в ваших таблицах данных с помощью операторов LINQ).

В вашем случае рассмотрите возможность создания функций расширения для DataTable: функций, преобразующих DataTable в IEnumerable<Customer> и IEnumerable<Appointment>, и функций, преобразующих IEnumerable<Customer> / IEnumerable back into a DataTable`.

Если вы сделаете это, вам будет легче распознавать шаблоны и повторно использовать код. Кроме того, если вы измените свое хранилище данных, например, с DataTable на CSV-файл, или базу данных, или что-то еще, все, что вам нужно сделать, это написать функцию, которая сделает его IEnumerable / IQueryable, и ваши LINQ-запросы все еще будут работать.

См. Демистифицированные методы расширения

static class DataTableExtensions
{
     public static IEnumerable<Customer> ToCustomers(this DataTable table)
     {
          ... // TODO: implement
     }
     public static DataTable ToDataTable(this IEnumerable<Customer> customers)
     {
          ... // TODO implement
     }

     // similar functions for Appointments and AppointmentDetails:
     public static IEnumerable<Appointment> ToAppointments(this DataTable table) {...}
     public static DataTable ToDataTable(this IEnumerable<Appointment> appointments) {...}
     public static IEnumerable<AppointmentDetails> ToAppointmentDetails(this DataTable table) {...}
     public static DataTable ToDataTable(this IEnumerable<AppointmentDetail> appointmentDetails) {...}

Вы знаете DataTables лучше, чем я, поэтому я оставлю вам код. Для получения справки см. Преобразовать DataTable в IEnumerable и Преобразовать IEnumerable в DataTable

Нам нужно написать функцию для вашего запроса LINQ. Вы можете сохранить его в виде набора операторов LINQ, однако он будет выглядеть аккуратнее, лучше читаемым, лучше тестируемым, более пригодным для повторного использования, если вы напишите для этого функцию (в конце концов: вы уже знаете, как писать функции расширения:

public static IEnumerable<AppointmentDetail> ToAppointmentDetails(
    this IEnumerable<Customer> customers,
    IEnumerable<Appointment> appointments)
{
    return customers.GroupJoin(appointments,     // GroupJoin customer and appointments
        customer => customer.CustomerId,         // from every customer take the customerId,
        appointment => appointment.CustomerId,   // from every appointment take the CustomerId,
        // from every Customer with all his matching Appointments make one new AppointmentDetail 
        (customer, appointments => new AppointmentDetail 
        {
            CustomerId = customer.CustomerId,
            TotalAppointments = appointments.Count(),
            MissedAppointments = appointments
                 .Where(appointment => appointment.IsMissed)
                 .ToList(),
            ...
        });
}

Теперь соберите все вместе:

Использование:

DataTable customerTable = ...
DataTable appointmentTable = ...
IEnumerable<Customer> customers = customerTable.ToCustomers();
IEnumerable<Appointment> appointments = appoitnmentTable.ToAppointments();

IEnumerable<AppointmentDetail> appointmentDetails = customers.ToAppointmentDetails(appointments);

DataTable appointmentDetailTables = appointmentDetails.ToDataTable(appointmentDetails);

Разве это не выглядит лучше?

Обратите внимание, что перечисление будет выполнять только последнее утверждение. Все более ранние операторы только создают IEnumerable, перечисление не выполняется. Это очень похоже на объединение операторов LINQ. Фактически, если вы действительно хотите и можете убедить своего руководителя проекта в том, что код будет лучше читаемым, тестируемым, обслуживаемым (в чем я сомневаюсь), вы можете переписать его одним оператором, аналогично объединению операторов LINQ. Не думайте, что это улучшит скорость обработки:

DataTable appointmentDetailTable = customerTable.ToCustomers()
    .ToAppointmentDetails(appointmentTable.ToAppointments())
    .ToDataTable();

Поскольку вы разделили свои проблемы, этот код гораздо более пригоден для повторного использования. Небольшие изменения не сильно повлияют на ваш код. Если вы решите, что ваши Клиенты и Назначения должны выбираться из базы данных, а не из DataTable, все, что вам нужно сделать, это переписать ваши ToCustomers и ToAppointments, все остальные функции останется без изменений.

0 голосов
/ 26 января 2019

Не уверен, почему это работает, когда другие методы этого не сделали, но с помощью этого:

DataTable apptDetails = (from t1 in customer.AsEnumerable()
join t2 in appointments.AsEnumerable() on Convert.ToInt32(t1["customerId"]) equals Convert.ToInt32(t2["customerId"])
into tableGroup
 select new
   {
    customerId = t1["customerId"],
    TotalAppointments = tableGroup.Count(),
    appointment_missed = Convert.ToInt32(t1["MissedAppt"]),
    appointment_show_rate = (
                                tableGroup.Count()>0 ? 
                                    Math.Round((1 - ((double)Convert.ToInt32(t1["MissedAppt"]) / (double)tableGroup.Count())),2)
                                    : 0
                            )
    }).CopyToDataTable();

работает после реализации информации с этой страницы: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/implement-copytodatatable-where-type-not-a-datarow

Я все еще не мог сделать:

DataTable dt = apptDetails.CopyToDataTable();

но работает по-другому.

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