多个 table 使用 lambda/linq c# 和 DTO 加入
Multiple table join using lambda/linq c# with DTO
这真是把我难住了。我在数据库中有四个 table,不幸的是设计这个 table 的人没有创建引用约束。因此,没有可用的导航属性。
四个table是:
CiscoPhoneReport
ApplicationSummary
CSQActivityReport
CallDistributionSummary
想法是,对于 CiscoPhoneReport 中的每个 PhoneReportID
,有一个 ApplicationSummary
、三个 CSQActivityReport
和三个 CallDistributionSummary
。
我想要 JSON 格式的输出:
`[{
"appSummary":{
"startDate":"2015-09-01T00:00:00",
"endDate":"2015-09-30T00:00:00",
"applicationName":"RationalDrugTherapy",
"callsPresented":14504,
"callsAbandoned":1992,
"callsHandled":12512
},
"csqModel":[
{
"startDate":null,
"csqid":"3",
"callsPresented":6271,
"avgQueueTime":"00:00:21",
"callsHandled":0,
"avgAnswerSpeed":"00:00:00",
"avgHandleTime":"00:02:08",
"callsHandledGreaterThan3t":5742,
"callsAbandoned":99,
"avgAbandonTime":"00:02:20",
"maxQueueTime":"00:25:26",
"maxHandleTime":"00:19:33",
"maxAbandonTime":"00:17:50"
},{
"startDate":null,
"csqid":"3",
"callsPresented":6271,
"avgQueueTime":"00:00:21",
"callsHandled":0,
"avgAnswerSpeed":"00:00:00",
"avgHandleTime":"00:02:08",
"callsHandledGreaterThan3t":1728,
"callsAbandoned":99,
"avgAbandonTime":"00:02:20",
"maxQueueTime":"00:25:26",
"maxHandleTime":"00:19:33",
"maxAbandonTime":"00:17:50"
}, {
"startDate":null,
"csqid":"3",
"callsPresented":6271,
"avgQueueTime":"00:00:21",
"callsHandled":0,
"avgAnswerSpeed":"00:00:00",
"avgHandleTime":"00:02:08",
"callsHandledGreaterThan3t":3363,
"callsAbandoned":99,
"avgAbandonTime":"00:02:20",
"maxQueueTime":"00:25:26",
"maxHandleTime":"00:19:33",
"maxAbandonTime":"00:17:50"
}]
}]`
为此,我创建了 DTO:
`public class AppSummary
{
public string PhoneReportID { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string ApplicationName { get; set; }
public int CallsPresented { get; set; }
public int CallsAbandoned { get; set; }
public int CallsHandled { get; set; }
}
`
`public class CSQModel
{
public string StartDate { get; set; }
public string CSQID { get; set; }
public int CallsPresented { get; set; }
public TimeSpan AvgQueueTime { get; set; }
public int CallsHandled { get; set; }
public TimeSpan AvgAnswerSpeed { get; set; }
public TimeSpan AvgHandleTime { get; set; }
public int CallsHandledGreaterThan3t { get; set; }
public int CallsAbandoned { get; set; }
public TimeSpan AvgAbandonTime { get; set; }
public TimeSpan MaxQueueTime { get; set; }
public TimeSpan MaxHandleTime { get; set; }
public TimeSpan MaxAbandonTime { get; set; }
}
`
`public class PhoneReport
{
public AppSummary AppSummary { get; set; }
//Initially, I had it like this
public CSQModel CSQModel { get; set; }
//I renamed the property as LIST to see if I could use it and add data to the list in linq, but I couldn't use the list within select expression in linq.
//public List<CSQModel> CSQModel { get; set; }
}
`
CSQModel class 需要来自 CSQActivityReport
和 CallDistributionSummary
table 的数据。
我能够创建一个带有 table 连接的 linq 语句,如下所示。
var res = from cpr in db.CiscoPhoneReport
join app in db.ApplicationSummary on cpr.PhoneReportID equals app.PhoneReportID into g1
from appGroup in g1.DefaultIfEmpty()
join csq in db.CSQActivityReport on cpr.PhoneReportID equals csq.PhoneReportID into g2
from csqGroup in g2.DefaultIfEmpty()
join call in db.CallDistributionSummary on cpr.PhoneReportID equals call.PhoneReportID into g3
from callGroup in g3.DefaultIfEmpty()
where cpr.PhoneReportID == phoneReportID
select new PhoneReport
{
AppSummary = new AppSummary
{
StartDate = cpr.StartDate,
EndDate = cpr.EndDate,
ApplicationName = appGroup.ApplicationName,
CallsPresented = appGroup.CallsPresented,
CallsAbandoned = appGroup.CallsAbandoned,
CallsHandled = appGroup.CallsHandled
},
CSQModel = new CSQModel
{
CSQID = csqGroup.CSQID.ToString(),
CallsPresented = csqGroup.CallsPresented,
AvgQueueTime = csqGroup.AvgQueueTime,
AvgHandleTime = csqGroup.AvgHandleTime,
CallsHandledGreaterThan3t = callGroup.CallsHandledGreaterThan3t,
CallsAbandoned = csqGroup.CallsAbandoned,
AvgAbandonTime = csqGroup.AvgAbandonTime,
MaxQueueTime = csqGroup.MaxQueueTime,
MaxHandleTime = csqGroup.MaxHandleTime,
MaxAbandonTime = csqGroup.MaxAbandonTime
}
};
`
我得到的结果是一组包含 9 行的数据,这是有道理的 - 就像 SQL 中的内部联接一样。但这不是我想要的。
如何获取上述JSON格式的数据?我根本想不通。
对于每条主记录,您在 2 个单独的子 table 中各有 3 条记录。除非您添加更多信息,否则您所做的任何加入都会给您 9 条记录(即使您直接转到 T-SQL)。
在 SQL 中执行此操作的一种方法是将 table A 中的记录 1 连接到 table B 中的记录 1,您需要一个索引器来执行此操作。 T-SQL 中的一个选项是在每个子 table 上使用 ROW_NUMBER()
函数并在连接中使用该值。但是,ROW_NUMBER()
还没有扩展到 LINQ。
如果您无法忍受获取重复记录,只需对每个子结果集执行 Distinct()
调用。然后您可以将其作为两个或 3 个单独的查询来执行。
注意:您可以将其捆绑到存储过程中的 3 个结果集中。您可以轻松地 EntityFramework 将每个结果反序列化到您的 POCO 中..
var objCtx = ((IObjectContextAdapter)ctx).ObjectContext;
using (SqlCommand cmd = ctx.Database.Connection.CreateCommand() as SqlCommand)
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "<your proc here>";
var param = cmd.CreateParameter();
param.ParameterName = "@param1";
param.Value = someValue;
cmd.Parameters.Add(param);
await cmd.Connection.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
var results = objCtx.Translate<type1Here>(reader).ToList();
reader.NextResult();
var results2 = objCtx.Translate<type2Here>(reader).ToList();
reader.NextResult();
var results3 = objCtx.Translate<type3Here>(reader).ToList();
reader.NextResult();
}
}
您可以序列化您的对象:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(GenericObject.GetType());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, GenericObject);
string json = Encoding.UTF8.GetString(ms.ToArray());
ms.Close();
return json;
我认为您看到 9 条记录的部分原因是您使用的语法是 left outer joins in Linq.
可能有用的是使用子查询以您想要的格式获取您想要的数据。
例如
var res = from cpr in db.CiscoPhoneReport
join app in db.ApplicationSummary on cpr.PhoneReportID equals app.PhoneReportID
where cpr.PhoneReportID == phoneReportID
select new PhoneReport
{
AppSummary = new AppSummary
{
// Mappings
},
CSQModel = (from model in db.CSQActivityReport
where model.PhoneReportId == phoneReportID
select new CSQModel
{
// Mappings
}).ToList()
}
你是对的,你需要 CSQModels
成为某种集合,无论是 List
还是 CSQModel
类型的基本 ICollection
。您可以根据需要为 CallDistributionSummary
编写另一个子查询。
这真是把我难住了。我在数据库中有四个 table,不幸的是设计这个 table 的人没有创建引用约束。因此,没有可用的导航属性。
四个table是:
CiscoPhoneReport
ApplicationSummary
CSQActivityReport
CallDistributionSummary
想法是,对于 CiscoPhoneReport 中的每个 PhoneReportID
,有一个 ApplicationSummary
、三个 CSQActivityReport
和三个 CallDistributionSummary
。
我想要 JSON 格式的输出:
`[{
"appSummary":{
"startDate":"2015-09-01T00:00:00",
"endDate":"2015-09-30T00:00:00",
"applicationName":"RationalDrugTherapy",
"callsPresented":14504,
"callsAbandoned":1992,
"callsHandled":12512
},
"csqModel":[
{
"startDate":null,
"csqid":"3",
"callsPresented":6271,
"avgQueueTime":"00:00:21",
"callsHandled":0,
"avgAnswerSpeed":"00:00:00",
"avgHandleTime":"00:02:08",
"callsHandledGreaterThan3t":5742,
"callsAbandoned":99,
"avgAbandonTime":"00:02:20",
"maxQueueTime":"00:25:26",
"maxHandleTime":"00:19:33",
"maxAbandonTime":"00:17:50"
},{
"startDate":null,
"csqid":"3",
"callsPresented":6271,
"avgQueueTime":"00:00:21",
"callsHandled":0,
"avgAnswerSpeed":"00:00:00",
"avgHandleTime":"00:02:08",
"callsHandledGreaterThan3t":1728,
"callsAbandoned":99,
"avgAbandonTime":"00:02:20",
"maxQueueTime":"00:25:26",
"maxHandleTime":"00:19:33",
"maxAbandonTime":"00:17:50"
}, {
"startDate":null,
"csqid":"3",
"callsPresented":6271,
"avgQueueTime":"00:00:21",
"callsHandled":0,
"avgAnswerSpeed":"00:00:00",
"avgHandleTime":"00:02:08",
"callsHandledGreaterThan3t":3363,
"callsAbandoned":99,
"avgAbandonTime":"00:02:20",
"maxQueueTime":"00:25:26",
"maxHandleTime":"00:19:33",
"maxAbandonTime":"00:17:50"
}]
}]`
为此,我创建了 DTO:
`public class AppSummary
{
public string PhoneReportID { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string ApplicationName { get; set; }
public int CallsPresented { get; set; }
public int CallsAbandoned { get; set; }
public int CallsHandled { get; set; }
}
`
`public class CSQModel
{
public string StartDate { get; set; }
public string CSQID { get; set; }
public int CallsPresented { get; set; }
public TimeSpan AvgQueueTime { get; set; }
public int CallsHandled { get; set; }
public TimeSpan AvgAnswerSpeed { get; set; }
public TimeSpan AvgHandleTime { get; set; }
public int CallsHandledGreaterThan3t { get; set; }
public int CallsAbandoned { get; set; }
public TimeSpan AvgAbandonTime { get; set; }
public TimeSpan MaxQueueTime { get; set; }
public TimeSpan MaxHandleTime { get; set; }
public TimeSpan MaxAbandonTime { get; set; }
}
`
`public class PhoneReport
{
public AppSummary AppSummary { get; set; }
//Initially, I had it like this
public CSQModel CSQModel { get; set; }
//I renamed the property as LIST to see if I could use it and add data to the list in linq, but I couldn't use the list within select expression in linq.
//public List<CSQModel> CSQModel { get; set; }
}
`
CSQModel class 需要来自 CSQActivityReport
和 CallDistributionSummary
table 的数据。
我能够创建一个带有 table 连接的 linq 语句,如下所示。
var res = from cpr in db.CiscoPhoneReport
join app in db.ApplicationSummary on cpr.PhoneReportID equals app.PhoneReportID into g1
from appGroup in g1.DefaultIfEmpty()
join csq in db.CSQActivityReport on cpr.PhoneReportID equals csq.PhoneReportID into g2
from csqGroup in g2.DefaultIfEmpty()
join call in db.CallDistributionSummary on cpr.PhoneReportID equals call.PhoneReportID into g3
from callGroup in g3.DefaultIfEmpty()
where cpr.PhoneReportID == phoneReportID
select new PhoneReport
{
AppSummary = new AppSummary
{
StartDate = cpr.StartDate,
EndDate = cpr.EndDate,
ApplicationName = appGroup.ApplicationName,
CallsPresented = appGroup.CallsPresented,
CallsAbandoned = appGroup.CallsAbandoned,
CallsHandled = appGroup.CallsHandled
},
CSQModel = new CSQModel
{
CSQID = csqGroup.CSQID.ToString(),
CallsPresented = csqGroup.CallsPresented,
AvgQueueTime = csqGroup.AvgQueueTime,
AvgHandleTime = csqGroup.AvgHandleTime,
CallsHandledGreaterThan3t = callGroup.CallsHandledGreaterThan3t,
CallsAbandoned = csqGroup.CallsAbandoned,
AvgAbandonTime = csqGroup.AvgAbandonTime,
MaxQueueTime = csqGroup.MaxQueueTime,
MaxHandleTime = csqGroup.MaxHandleTime,
MaxAbandonTime = csqGroup.MaxAbandonTime
}
};
`
我得到的结果是一组包含 9 行的数据,这是有道理的 - 就像 SQL 中的内部联接一样。但这不是我想要的。
如何获取上述JSON格式的数据?我根本想不通。
对于每条主记录,您在 2 个单独的子 table 中各有 3 条记录。除非您添加更多信息,否则您所做的任何加入都会给您 9 条记录(即使您直接转到 T-SQL)。
在 SQL 中执行此操作的一种方法是将 table A 中的记录 1 连接到 table B 中的记录 1,您需要一个索引器来执行此操作。 T-SQL 中的一个选项是在每个子 table 上使用 ROW_NUMBER()
函数并在连接中使用该值。但是,ROW_NUMBER()
还没有扩展到 LINQ。
如果您无法忍受获取重复记录,只需对每个子结果集执行 Distinct()
调用。然后您可以将其作为两个或 3 个单独的查询来执行。
注意:您可以将其捆绑到存储过程中的 3 个结果集中。您可以轻松地 EntityFramework 将每个结果反序列化到您的 POCO 中..
var objCtx = ((IObjectContextAdapter)ctx).ObjectContext;
using (SqlCommand cmd = ctx.Database.Connection.CreateCommand() as SqlCommand)
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "<your proc here>";
var param = cmd.CreateParameter();
param.ParameterName = "@param1";
param.Value = someValue;
cmd.Parameters.Add(param);
await cmd.Connection.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
var results = objCtx.Translate<type1Here>(reader).ToList();
reader.NextResult();
var results2 = objCtx.Translate<type2Here>(reader).ToList();
reader.NextResult();
var results3 = objCtx.Translate<type3Here>(reader).ToList();
reader.NextResult();
}
}
您可以序列化您的对象:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(GenericObject.GetType());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, GenericObject);
string json = Encoding.UTF8.GetString(ms.ToArray());
ms.Close();
return json;
我认为您看到 9 条记录的部分原因是您使用的语法是 left outer joins in Linq.
可能有用的是使用子查询以您想要的格式获取您想要的数据。
例如
var res = from cpr in db.CiscoPhoneReport
join app in db.ApplicationSummary on cpr.PhoneReportID equals app.PhoneReportID
where cpr.PhoneReportID == phoneReportID
select new PhoneReport
{
AppSummary = new AppSummary
{
// Mappings
},
CSQModel = (from model in db.CSQActivityReport
where model.PhoneReportId == phoneReportID
select new CSQModel
{
// Mappings
}).ToList()
}
你是对的,你需要 CSQModels
成为某种集合,无论是 List
还是 CSQModel
类型的基本 ICollection
。您可以根据需要为 CallDistributionSummary
编写另一个子查询。