Multiple table join using lambda/linq c# with DTO

泄露秘密 提交于 2019-12-25 02:58:55

问题


This really has me stumped. I have four tables in the database, and unfortunately the person who designed this table didn't create referential constraints. So, there is no navigation properties available.

Four tables are:

CiscoPhoneReport
ApplicationSummary
CSQActivityReport
CallDistributionSummary

The idea is that for each PhoneReportID in CiscoPhoneReport, there is one ApplicationSummary, three CSQActivityReport, and three CallDistributionSummary.

I want the output as below in JSON format:

`[{
   "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"
  }]
}]`

For this, I created 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; }

    }
`

The CSQModel class has needed data from both CSQActivityReport and CallDistributionSummary tables.

I was able to create a linq statement with table joins as below.

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
         }
   };
`

The result I'm getting is a set of data with 9 rows, which makes sense - as in inner join in SQL. But this is not what I wanted.

How can I obtain the data as in the JSON format above? I couldn't figure it out at all.


回答1:


I think part of the reason you are seeing 9 records is because the syntax you are using is the one for left outer joins in Linq.

What might work is using subqueries to get the data you want in a format you want it.

For example

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()
          }

You were right that you need the CSQModels to be some sort of collection, be it a List or even a basic ICollection of type CSQModel. You can write another sub query for the CallDistributionSummary as needed.




回答2:


For every master record, you have 3 records each in 2 separate child tables. Any join you do will give you 9 records (even if you go straight to T-SQL) unless you add some more information.

One way to do this in SQL is to join record 1 from table A to record 1 from table B, you would need an indexer to do this. One option in T-SQL is to use the ROW_NUMBER() function on each of the child tables and use that value in the join. But, the ROW_NUMBER() hasn't been extended to LINQ.

If you can't stand getting getting duplicate records, and just doing a Distinct() call for each child result set. Then you could do this as two or 3 separate queries.

NOTE: You can bundle this into 3 result sets in a stored proc.. You can easily get EntityFramework to deserialize each result into your POCO's..

            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();
                }
            }



回答3:


You can serialize your object:

            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;


来源:https://stackoverflow.com/questions/33269836/multiple-table-join-using-lambda-linq-c-sharp-with-dto

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!