c# - 用 Linq 连接 4 个数据表
问题描述
我有 4 个要加入的数据表,但不知道如何有效地加入。
我得到了前两个表来加入,创建了第三个对象,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 执行此操作的“正确”方式。
解决方案
您应该更多地关注关注点分离:将数据(DataTables)的内部存储方法与数据处理分开(使用 LINQ 语句组合数据表中的数据)。
在您的情况下,请考虑为以下内容创建扩展函数DataTable
:将 a 转换DataTable
为IEnumerable<Customer>
andIEnumerable<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
and ToAppointments
,所有其他功能将保持不变。
推荐阅读
- mysql - 将全名分为名字和姓氏MySQL
- angular - Angular Material Select - 预选多个值
- firebase - FireStore 规则 - 创建加入多个集合的规则
- jquery - 保存值输入动态表单jquery
- c++ - 什么决定了 C++ 变量的初始值
- php - 如何使用 laravel 将图像存储在数据库中?
- python - 在 pyscipopt 中添加非约束
- python - Python Selenium Scraper 被检测到
- autocomplete - CodeSandbox 编辑器已停止自动完成相应的 JSX 结束标记?
- php - 使用 Laravel 从 TMDb 获取电影名称