How to Join two tables showing all records Where Table A is Not In Table B - sql

I have a email marketing web application. I want to show which email contacts in (Table B) are not showing up in EmailContacts_Campaign (Table A). In addition, I want to filter table A by the CampaignId field. When I run the below code I get 0 records, yet I know there are a couple of thousand records there. Can anyone tell me where I am messing up?
SELECT * FROM TableA
LEFT JOIN TableB
ON TableA.EmailContactId = TableB.EmailContactId
WHERE TableA.CampaignId = 1
AND TableB.EmailContactId IS NULL
ORDER BY TableB.EmailContactId DESC
I want to show all email contacts in the EmailContact Table that are not showing up in the EmailContactCampaign table. Here is the actual code:
public List<EmailContact> GetNotAssignedContactsForCampaign(int campaignId)
{
string sqlCommand = "SELECT * FROM EmailContactCampaign LEFT JOIN EmailContact";
sqlCommand += " ON EmailContactCampaign.EmailContact_EmailContactId = EmailContact.EmailContactId";
sqlCommand += " WHERE EmailContactCampaign.EmailContact_EmailContactId = " + campaignId.ToString() AND EmailContact.EmailContactId IS NULL ;
sqlCommand += " ORDER BY EmailContact.EmailContactId DESC";
var emailContacts = new List<EmailContact>();
string CS = db.Database.Connection.ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
con.Open();
SqlCommand cmd = new SqlCommand(sqlCommand, con);
//Create sql datareader
using (SqlDataReader sqlDataReader = cmd.ExecuteReader())
{
while (sqlDataReader.Read())
{
var emailContact = new EmailContact();
emailContact.Assigned = ((bool)sqlDataReader["Assigned"]);
emailContact.Cell1 = _crypto.DecryptAndSanitize(sqlDataReader["Cell1"] as string);
emailContact.Cell2 = _crypto.DecryptAndSanitize(sqlDataReader["Cell2"] as string);
emailContact.City = _crypto.DecryptAndSanitize(sqlDataReader["City"] as string);
emailContact.Company = _crypto.DecryptAndSanitize(sqlDataReader["Company"] as string);
emailContact.EmailAddress = _crypto.DecryptAndSanitize(sqlDataReader["EmailAddress"] as string);
emailContact.EmailContactId = (int)sqlDataReader["EmailContactId"];
emailContact.FullName = _crypto.DecryptAndSanitize(sqlDataReader["FullName"] as string);
emailContact.Hold = (bool)sqlDataReader["Hold"];
emailContact.Phone1 = _crypto.DecryptAndSanitize(sqlDataReader["Phone1"] as string);
emailContact.Phone2 = _crypto.DecryptAndSanitize(sqlDataReader["Phone2"] as string);
emailContact.State = _crypto.DecryptAndSanitize(sqlDataReader["State"] as string);
emailContact.Status = (Status)sqlDataReader["Status"];
emailContact.Zip = _crypto.DecryptAndSanitize(sqlDataReader["Zip"] as string);
emailContacts.Add(emailContact);
}
}
return (emailContacts);
}
}

Have you tried this?
SELECT * FROM tableB WHERE EmailContactId NOT IN (SELECT EmailContactId FROM tableA)

i think you got 0 probably because of this AND TableB.EmailContactId IS NULL
Please try this one
SELECT * FROM TableA
LEFT JOIN TableB
ON TableA.EmailContactId = TableB.EmailContactId
WHERE TableA.CampaignId = 1
ORDER BY TableB.EmailContactId DESC

I'm sorry my question was not clear enough. Did some digging and found the answer on another post. Sorry but I accidentally closed it and can't find it again. Anyway, here is my implementation of it.
SELECT * FROM EmailContact
WHERE NOT EXISTS
(SELECT * FROM EmailContactCampaign WHERE EmailContactCampaign.EmailContact_EmailContactId = EmailContact.EmailContactId AND EmailContactCampaign.Campaign_CampaignId = 1)

If i understood your question correctly, you are looking for B's that are not in A. But your query will return A's that are not in B. Turn it aroung (tableB left join tableA where a... is NULL)

Your problem was that you had it the wrong way around: your query would return all contacts from EmailContactCampaign that were not in EmailContact.
The correct solution for your problem would look like this:
SELECT * FROM EmailContact
WHERE EmailContactId NOT IN (
SELECT EmailContact_EmailContactId FROM EmailContactCampaign
WHERE Campaign_CampaignId = ?
)
ORDER BY EmailContact.EmailContactId DESC

Related

MS SQL Query in C# - poor performance

I calculate outstanding customers balance in C# Winforms. The code below works, but it's slow. Is there any way to improve its performance?
public DataTable GetOutStandingCustomers()
{
decimal Tot = 0;
DataTable table = new DataTable();
SqlConnection con = null;
try
{
table.Columns.Add("Code", typeof(Int32));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("Tot", typeof(decimal));
string constr = ConfigHelper.GetConnectionString();
string query = "SELECT Code, Name,City FROM Chart WHERE LEFT(CODE,3)='401' AND Code > 401001 ";
string query0 = " SELECT(SELECT ISNULL( SUM(SalSum.Grand),'0' ) FROM SalSum WHERE SalSum.Code = #Code ) +( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.DrCode = #Code ) -( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = #Code ) -( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.CrCode = #Code )+(SELECT ISNULL(SUM(Chart.Debit),'0' ) FROM Chart WHERE Chart.Code = #Code) - (SELECT ISNULL(SUM(Chart.Credit), '0') FROM Chart WHERE Chart.Code = #Code)";
Person per = new Person();
con = new SqlConnection(constr);
SqlCommand com = new SqlCommand(query, con);
SqlCommand com0 = new SqlCommand(query0, con);
con.Open();
SqlDataReader r = com.ExecuteReader();
if (r.HasRows)
{
while (r.Read())
{
per.Name = Convert.ToString(r["Name"]);
per.City = Convert.ToString(r["City"]);
per.Code = Convert.ToString(r["Code"]);
com0.Parameters.Clear();
com0.Parameters.Add("#Code", SqlDbType.Int).Value = per.Code;
Tot = Convert.ToDecimal(com0.ExecuteScalar());
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
}
}
r.Close();
con.Close();
return table;
}
catch (Exception)
{
throw new Exception();
}
}
The performance problem is due to you retrieve all data from the server and filter data in the client using the complex computed expression that sum from seven tables:
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
This represent overhead over network plus you manually add the result to the datatable row by row.
The provided solution do filter in the server based on the computed expression using the CROSS APPLY
and auto create the datatable directly from the DataReader.
The benefit of CROSS APPLY is all columns are feasible to the main sql query, so you can filter on ToT column, filtering is done in the server (not the client).
public void SelctChart()
{
string sql2 = #"
select c.Code, c.Name,c.City ,oo.T
from chart c
cross apply
( select c.code,
(
(select ISNULL( SUM(SalSum.Grand),0 ) FROM SalSum WHERE SalSum.Code = c.code )
+( select ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.DrCode = c.code)
-( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = c.Code )
-( SELECT ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.CrCode = c.code )
+(SELECT ISNULL(SUM( c0.Debit),0 ) FROM [dbo].Chart c0 WHERE c0.Code = c.code)
- (SELECT ISNULL(SUM(c1.Credit), 0) FROM [dbo].Chart c1 WHERE c1.Code = c.code)
)T
) oo
where
oo.T >0
and LEFT(c.CODE,3)='401' AND c.Code > 401001
";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(sql2, connection);
//in case you pass #code as a a parameter
//command.Parameters.Add("#code", SqlDbType.Int);
//command.Parameters["#code"].Value = code;
try
{
connection.Open();
var reader = command.ExecuteReader();
while (!reader.IsClosed)
{
DataTable dt = new DataTable();
// Autoload datatable
dt.Load(reader);
Console.WriteLine(dt.Rows.Count);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
You can modify the method and pass code as a parameter
In this case it seems you're looping through and performing multiple queries using multiple Codes, you're also querying Chart twice. In this case you'd want to use a LEFT JOIN from Chart to your other tables.
ON Chart.Code = Salsum.Code
ON Chart.Code = Journal.Code
for example.
You will have to look at GROUP BY as well because you're aggregating some table columns by using SUM.
You may also need to make sure that Code is indexed on the tables you're querying. As long as Code is often queried like this and comparatively rarely Updated or Inserted to, then indexing the Code column on these tables is probably appropriate.
Left Joins : https://technet.microsoft.com/en-us/library/ms187518(v=sql.105).aspx
Indexing: https://technet.microsoft.com/en-us/library/jj835095(v=sql.110).aspx
Sorry I wrote a book on you here, but optimization often leads to a long answer (especially with SQL).
tldr;
Use a LEFT JOIN, grouping by Code
Index the Code columns

Powershell Single row datatable return to fill combobox

I'm in Powershell v4.0. I have a dropdown on my UI that I am filling with data from my database. I call a function that grabs the data like so:
$ConfigDynamicContractID.ItemsSource = (GetDynamicContracts).DefaultView
On the backend it looks like this:
function GetDynamicContracts{
$ContractQuery = "select con.ContractNumber as Name, c.ContractID from foo..campaign c inner join foo..campaigntemplate ct on c.campaignid = ct.campaignid
inner join foo..template t on ct.templateid = t.templateid inner join bar..contracts con on con.ContractID = c.ContractID
where c.ProductType = 'INTRO' and t.FileName = 'Intro Dynamic Template' and c.DeleteFlag = 0"
$ContractResult = ExecuteSelect "Bar" $ContractQuery
return , $ContractResult
}
This code works fine. I was then told that the tables referenced above can't talk to each other so I have to do individual calls to each and filter in powershell. So here is my code for that:
function GetDynamicContracts{
$CampaignQuery = "Select c.* From foo..Campaign c join foo..CampaignTemplate ct on c.CampaignID = ct.CampaignID
join foo..Template t on ct.TemplateID = t.TemplateID
Where c.ProductType = 'INTRO' and t.FileName = 'Intro Dynamic Template' and c.DeleteFlag = 0"
$CampaignResults = ExecuteSelect "foo" $CampaignQuery
$ContractIDs = New-Object System.Collections.ArrayList
foreach($row in $CampaignResults.Rows){
$ContractIDs.Add($row.ContractID)
}
$ContractIDs = $ContractIDs -join ","
$PHQuery = "Select ContractID, ContractNumber as Name From Contracts Where ContractID in ($ContractIDs)"
$PHResults = ExecuteSelect "Bar" $PHQuery
#Log $MyInvocation.MyCommand $PSBoundParameters
return ,$PHResults
}
This code for some reason does not work when returning a single row. But if I break this function call into two separate, then it works. See below:
function GetDynamicContracts{
$ContractIDs = GetDynamicCampaign
$ContractIDs = $ContractIDs[1]
$PHQuery = "Select ContractID, ContractNumber as Name From Contracts Where ContractID in ($ContractIDs)"
$PHResults = ExecuteSelect "PhytelMaster" $PHQuery
Log $MyInvocation.MyCommand $PSBoundParameters
return ,$PHResults
}
function GetDynamicCampaign{
$CampaignQuery = "Select c.* From Campaign..Campaign c join Campaign..CampaignTemplate ct on c.CampaignID = ct.CampaignID
join Campaign..Template t on ct.TemplateID = t.TemplateID
Where c.ProductType = 'INTRO' and t.FileName = 'Intro Dynamic Template' and c.DeleteFlag = 0"
$CampaignResults = ExecuteSelect "Campaign" $CampaignQuery
$ContractIDs = New-Object System.Collections.ArrayList
foreach($row in $CampaignResults.Rows){
$ContractIDs.Add($row.ContractID)
}
$ContractIDs = $ContractIDs -join ","
return $ContractIDs
}
It seems that making two execute selects in a single function causes the ',' to fail to do what it does when returning the data. Can anyone explain why?? Thanks ahead of time and since I have a workaround there is no rush.
As #TheMadTechnician and #wOxxOm point out ArrayList spams the indexes into the return and return, returns everything that gets spammed in the function. So when I expected $PHResults I also got the ArrayList spam. I could use something other than the ArrayList or I also found that if I wrap my function code in $null = #{ 'Code' } and then return the variable I want, all I get is that variable. It's ugly but it works.

SQL statement where clause by two dates

I am trying to write a Sql statement which filter the records based on two date variables entered by users. Here is my Sql statement:
public List<DistributionPacking> filterByDeliveryDate()
{
List<DistributionPacking> dateList = new List<DistributionPacking>();
using (var connection = new SqlConnection(FoodBankDB.connectionString)) // get your connection string from the other class here
{
SqlCommand command = new SqlCommand("SELECT d.id, d.packingDate, d.deliveryDate, b.name FROM dbo.Distributions d " +
"INNER JOIN dbo.Beneficiaries b ON d.beneficiary = b.id WHERE d.deliveryDate BETWEEN #fromDate AND #toDate", connection);
command.Parameters.AddWithValue("#fromDate", startDate);
command.Parameters.AddWithValue("#toDate", endDate);
connection.Open();
using (var dr = command.ExecuteReader())
{
while (dr.Read())
{
string id = dr["id"].ToString();
DateTime packingDate = DateTime.Parse(dr["packingDate"].ToString());
DateTime deliveryDate = DateTime.Parse(dr["deliveryDate"].ToString());
string name = dr["name"].ToString();
dateList.Add(new DistributionPacking(id, packingDate, deliveryDate, name));
}
}
}
However, it told me that Conversion failed when converting date and/or time from character string although my data type for packingDate and deliveryDate is DateTime. I wonder why is it so.
Thanks in advance.
Try something like this ...
SELECT d.id, d.packingDate, d.deliveryDate, b.name
FROM dbo.Distributions d INNER JOIN dbo.Beneficiaries b
ON d.beneficiary = b.id
WHERE d.deliveryDate
BETWEEN CAST(#fromDate AS DATETIME) AND CAST(#toDate AS DATETIME)

Tree View Query

I created a query to get all addresses(child) with in municipality(parent) but it is coming back empty/ Any help is appreciated.
drop table #tmp1
go
WITH Emp_CTE AS (
SELECT tablesysID, MunicNo, StreetName
FROM table1
UNION ALL
SELECT e.tablesysID,e.MunicNo,e.StreetName
FROM table1 e
INNER JOIN Emp_CTE ecte ON ecte.MunicNo = e.MunicNo
)
SELECT * into #tmp1
FROM Emp_CTE
select * from #tmp1
it will be used in asp.net tree view control.
Thanks Andomar for your answer, which is correct but I just wanted to share how I solved this problem as follows:
1: make the query as stored procedure that returns xml data type as response like:
create PROCEDURE [dbo].[sp_someproc]
#XmlResponse xml output
AS
BEGIN
--
-- Insert statements for procedure here
set #XmlResponse=(SELECT DISTINCT
table1.MunicNo + ' ' + table1.StreetName + ' ' + table1.City AS firstRow, table1.MunicNo, table1.StreetName, table1.City,
table1.XPOS, table1.YPOS, table1.RollID, table2.Asset_ID, table2.Feature_ID, table2.FeatureName + ',' + table2.Feature_ID + ' ' + table2.[DESC] AS secondRow,
table2.FeatureName, table2.xxxID, table2.[DESC],table3.WONOs, table3.WONOs + ', ' + table3.AssetType + ', ' +table3.Feature_ID AS workONumber
FROM table4 INNER JOIN
table3 ON table4.xxxxID = table3.xxxxID INNER JOIN
table2 ON table4.Asset_ID = table2.xxx_ID INNER JOIN
table1 ON table2.StreetName = table1.StreetName AND table3.MunicNo = table1.MunicNo
for xml auto,root('xml'))
END
select #XmlResponse
2: Aspx page code:
<telerik:RadTreeView ID="rtrvxxxxx" runat="server" >
<DataBindings>
<telerik:RadTreeNodeBinding DataMember="table1" TextField="firstRow" ValueField="firstRow" />
<telerik:RadTreeNodeBinding DataMember="table2" TextField="secondRow" />
<telerik:RadTreeNodeBinding DataMember="table3" TextField="workONumber" />
</DataBindings>
</telerik:RadTreeView>
3: C# Code:
private void loadXmlDocument()
{
try
{
#region Load and Bind xml to treeview
XmlDataSource xDS = new XmlDataSource();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc = callingdatalayerclass.list_XML();
xDS.Data = xmlDoc.InnerXml;
xDS.XPath = "/xml/table1";
xDS.EnableCaching = false;
//bind to treeview
rtrvxxxx.DataSource = xDS;
rtrvxxxx.DataBind();
#endregion
}
catch (Exception ex)
{
ex.Message()}
}
}
4: Data access Layer Code:
public static XmlDocument List_XML()
{
XmlDocument xmlDoc = new XmlDocument();
SqlConnection SQLConn = new SqlConnection();
SQLConn.ConnectionString = someclass.someotherclass.GetConnectionString();
try
{
SQLConn.Open();
SqlCommand custCMD = new SqlCommand("sp_someproc", SQLConn);
custCMD.CommandType = CommandType.StoredProcedure;
custCMD.Parameters.Add("#XmlResponse", SqlDbType.Xml).Direction = ParameterDirection.Output;
custCMD.ExecuteNonQuery();
if (custCMD.Parameters["#XmlResponse"].Value != null)
{
string xml = custCMD.Parameters["#XmlResponse"].Value.ToString();
xmlDoc.LoadXml(xml);
}
return xmlDoc;
}
catch (Exception exGEN)
{
throw exGEN;
}
finally
{
SQLConn.Close();
}
}
Notes: if you see query has rows those are first layer of treeview and 2ndrow is inner row of 1st row or can call it a child of parentnode, 3rd row is inner row or child row of 2nd row, so if 1st row has some records it will output that, if 2nd row has some reocrds it will ouput that. if you see the databining section in treeview datamember is table name and
textfiled is firstrow, value field could be some other coulmn. Have not worked with asp.net orginal treeview so don't know how that works this is for telerik treeview control. I also found that the speed is way faster if data is slected as xml. "DS.XPath = "/xml/table1";" <= this code is selecting xml element or root element and then with in that element the first element which by the data is sorted(first row record's table)
Have a look at your recursive condition:
INNER JOIN Emp_CTE ecte ON ecte.MunicNo = e.MunicNo
This recurses into the same municipality. (Except when MinicNo is null.)
You haven't posted enough information to find the proper condition, but it might look like:
INNER JOIN Emp_CTE ecte ON ecte.tablesysID = e.MunicNo

How do I map lists of nested objects with Dapper

I'm currently using Entity Framework for my db access but want to have a look at Dapper. I have classes like this:
public class Course{
public string Title{get;set;}
public IList<Location> Locations {get;set;}
...
}
public class Location{
public string Name {get;set;}
...
}
So one course can be taught at several locations. Entity Framework does the mapping for me so my Course object is populated with a list of locations. How would I go about this with Dapper, is it even possible or do I have to do it in several query steps?
Alternatively, you can use one query with a lookup:
var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course))
lookup.Add(c.Id, course = c);
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(l); /* Add locations to course */
return course;
}).AsQueryable();
var resultList = lookup.Values;
See here https://www.tritac.com/blog/dappernet-by-example/
Dapper is not a full blown ORM it does not handle magic generation of queries and such.
For your particular example the following would probably work:
Grab the courses:
var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");
Grab the relevant mapping:
var mappings = cnn.Query<CourseLocation>(
"select * from CourseLocations where CourseId in #Ids",
new {Ids = courses.Select(c => c.Id).Distinct()});
Grab the relevant locations
var locations = cnn.Query<Location>(
"select * from Locations where Id in #Ids",
new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);
Map it all up
Leaving this to the reader, you create a few maps and iterate through your courses populating with the locations.
Caveat the in trick will work if you have less than 2100 lookups (Sql Server), if you have more you probably want to amend the query to select * from CourseLocations where CourseId in (select Id from Courses ... ) if that is the case you may as well yank all the results in one go using QueryMultiple
No need for lookup Dictionary
var coursesWithLocations =
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (course, location) => {
course.Locations = course.Locations ?? new List<Location>();
course.Locations.Add(location);
return course;
}).AsQueryable();
I know I'm really late to this, but there is another option. You can use QueryMultiple here. Something like this:
var results = cnn.QueryMultiple(#"
SELECT *
FROM Courses
WHERE Category = 1
ORDER BY CreationDate
;
SELECT A.*
,B.CourseId
FROM Locations A
INNER JOIN CourseLocations B
ON A.LocationId = B.LocationId
INNER JOIN Course C
ON B.CourseId = B.CourseId
AND C.Category = 1
");
var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}
Sorry to be late to the party (like always). For me, it's easier to use a Dictionary, like Jeroen K did, in terms of performance and readability. Also, to avoid header multiplication across locations, I use Distinct() to remove potential dups:
string query = #"SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id";
using (SqlConnection conn = DB.getConnection())
{
conn.Open();
var courseDictionary = new Dictionary<Guid, Course>();
var list = conn.Query<Course, Location, Course>(
query,
(course, location) =>
{
if (!courseDictionary.TryGetValue(course.Id, out Course courseEntry))
{
courseEntry = course;
courseEntry.Locations = courseEntry.Locations ?? new List<Location>();
courseDictionary.Add(courseEntry.Id, courseEntry);
}
courseEntry.Locations.Add(location);
return courseEntry;
},
splitOn: "Id")
.Distinct()
.ToList();
return list;
}
Something is missing. If you do not specify each field from Locations in the SQL query, the object Location cannot be filled. Take a look:
var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.Name, l.otherField, l.secondField
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(a);
return course;
},
).AsQueryable();
var resultList = lookup.Values;
Using l.* in the query, I had the list of locations but without data.
Not sure if anybody needs it, but I have dynamic version of it without Model for quick & flexible coding.
var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(#"
SELECT A.*, B.*
FROM Client A
INNER JOIN Instance B ON A.ClientID = B.ClientID
", (A, B) => {
// If dict has no key, allocate new obj
// with another level of array
if (!lookup.ContainsKey(A.ClientID)) {
lookup[A.ClientID] = new {
ClientID = A.ClientID,
ClientName = A.Name,
Instances = new List<dynamic>()
};
}
// Add each instance
lookup[A.ClientID].Instances.Add(new {
InstanceName = B.Name,
BaseURL = B.BaseURL,
WebAppPath = B.WebAppPath
});
return lookup[A.ClientID];
}, splitOn: "ClientID,InstanceID").AsQueryable();
var resultList = lookup.Values;
return resultList;
There is another approach using the JSON result. Even though the accepted answer and others are well explained, I just thought about an another approach to get the result.
Create a stored procedure or a select qry to return the result in json format. then Deserialize the the result object to required class format. please go through the sample code.
using (var db = connection.OpenConnection())
{
var results = await db.QueryAsync("your_sp_name",..);
var result = results.FirstOrDefault();
string Json = result?.your_result_json_row;
if (!string.IsNullOrEmpty(Json))
{
List<Course> Courses= JsonConvert.DeserializeObject<List<Course>>(Json);
}
//map to your custom class and dto then return the result
}
This is an another thought process. Please review the same.