Translation of SQL query with INNER JOIN to Entity SQL query - C# - c#

I build the following SQL query dynamically:
StringBuilder query = new StringBuilder();
StringBuilder query2 = new StringBuilder();
if (ComboRuleType.Text.Equals("Standard"))
{
query.Append("select * from [dbo].[" + ComboRuleTableName.Text + "]" + " WHERE" + "\n");
query.Append("(" + "\n");
for (int i = 0; i < dgvUpdateCriteria.RowCount; i++)
{
DataGridViewRow row = dgvUpdateCriteria.Rows[i];
if (i != 0)
{
query.Append(row.Cells[1].Value.ToString() + " " + row.Cells[3].Value.ToString() + " ");
}
else
{
query.Append(row.Cells[3].Value.ToString() + " ");
}
if (row.Cells[4].Value.ToString().Equals("Contains"))
{
query.Append("like " + "'%" + row.Cells[5].Value.ToString() + "%'" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("Equals"))
{
query.Append("= " + "'" + row.Cells[5].Value.ToString() + "'" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("StartsWith"))
{
query.Append("like " + "'" + row.Cells[5].Value.ToString() + "%'" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("EndsWith"))
{
query.Append("like " + "'%" + row.Cells[5].Value.ToString() + "'" + "\n");
}
}
query.Append(")" + "\n");
return query.ToString();
}
After converting the above to Entity SQL, it looks like:
StringBuilder query = new StringBuilder();
StringBuilder query2 = new StringBuilder();
if (ComboRuleType.Text.Equals("Standard"))
{
query.Append("select value q1 from ");
query.Append(ComboRuleTableName.Text);
query.Append("s");
query.Append(" as q1 where " + "\n");
for (int i = 0; i < dgvUpdateCriteria.RowCount; i++)
{
DataGridViewRow row = dgvUpdateCriteria.Rows[i];
if (i != 0)
{
if (row.Cells[1].Value.ToString().Equals("AND"))
{
query.Append("&&" + " " + "q1." + row.Cells[3].Value.ToString());
}
else
{
query.Append("||" + " " + "q1." + row.Cells[3].Value.ToString());
}
}
else
{
query.Append("q1." + row.Cells[3].Value.ToString());
}
if (row.Cells[4].Value.ToString().Equals("Contains"))
{
query.Append(" LIKE (" + "'%" + row.Cells[5].Value.ToString() + "%'" + ")" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("Equals"))
{
query.Append(" == (" + "'" + row.Cells[5].Value.ToString() + "'" + ")" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("StartsWith"))
{
query.Append(" LIKE (" + "'" + row.Cells[5].Value.ToString() + "%'" + ")" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("EndsWith"))
{
query.Append(" LIKE (" + "'%" + row.Cells[5].Value.ToString() + "'" + ")" + "\n");
}
}
return query.ToString();
}
I construct another SQL query that contains INNER JOIN and I have looked EVERYWHERE but cannot find the equivalent translation of that SQL query to an Entity SQL query. I would really appreciate if you can help me out. The dynamic SQL query with INNER JOIN is as follows:
query.Append("SELECT * ");
query.Append("FROM [dbo].[membership] mm \n");
query.Append("INNER JOIN [dbo].[" + ComboRuleTableName.Text + "] xx \n");
query.Append("ON (mm.m_" + ComboRuleTableName.Text + "_id = xx.id) \n");
query.Append("WHERE xx.id IN ( \n");
query.Append("SELECT id from [dbo].[" + ComboRuleTableName.Text + "] \n");
query.Append("WHERE \n");
query.Append("mm.platform_name = '" + ComboRulePlatformName.Text + "' AND (\n");
for (int i = 0; i < dgvUpdateCriteria.RowCount; i++)
{
DataGridViewRow row = dgvUpdateCriteria.Rows[i];
if (i != 0)
{
query2.Append(row.Cells[1].Value.ToString() + " " + row.Cells[3].Value.ToString() + " ");
}
else
{
query2.Append(row.Cells[3].Value.ToString() + " ");
}
if (row.Cells[4].Value.ToString().Equals("Contains"))
{
query2.Append("like " + "'%" + row.Cells[5].Value.ToString() + "%'" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("Equals"))
{
query2.Append("= " + "'" + row.Cells[5].Value.ToString() + "'" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("StartsWith"))
{
query2.Append("like " + "'" + row.Cells[5].Value.ToString() + "%'" + "\n");
}
else if (row.Cells[4].Value.ToString().Equals("EndsWith"))
{
query2.Append("like " + "'%" + row.Cells[5].Value.ToString() + "'" + "\n");
}
else
{
query2.Append(" \n");
}
}
query2.Append("))\n");
return query.Append(query2).ToString();
I NEED it to be in a string format. I later convert it from string to query format. I just do not know how the INNER JOIN syntax works with Entity queries.
Thank you.
Edit 1:
Here is how I convert that Query into Entity Framework Object Query:
string query = EntityPreview(); //EntityPreview() is the method that gives me Raw Entity SQL Query
var objctx = (context as IObjectContextAdapter).ObjectContext;
if (ComboRuleTableName.Text.Equals("system"))
{
ObjectQuery<system> standardList = objctx.CreateQuery<system>(query);
rulePreviewForm.dataGridViewCriteriaRulePreview.DataSource = standardList;
rulePreviewForm.Show();
}

One of the greatest things about EntityFramework is it builds SQL for you and allows you to manipulate objects instead of SQL. There are other libraries like Dapper that are quicker when using straight SQL if you have a choice. If you have to use EntityFramework, you would be better off writing Linq.
When using Linq instead of SQL, you can still build Dynamic queries using IQueryable. This allows you to build a query without pulling any data from the database up front.
Without knowing much about what you are trying to do with your application, I can only offer a few tips of things to try. In the answer below I am making some assumptions on the naming of how you have your entities set up.
For getting a list of memberships from the membership table, assuming your entity for that table is called Membership:
IQueryable<Membership> memberships = context.Memberships;
That is your
query.Append("SELECT * ");
query.Append("FROM [dbo].[membership] mm \n");
For your filters you will likely want to put them into a List.
From this point on is where the dynamic part of this comes in. If you have a table for your ComboRule named [ComboRuleTable1] and another called [ComboRuleTable2], but you have to query based on input from ComboRuleTableName.Text, you can do something like this.
var filters = new List<string>() { "name1", "name2" };
// Get which table you should join to
switch (ComboRuleTable1)
{
// Join to tables and get filtered data
case "ComboRuleTable1":
memberships = memberships.ComboRuleTable1.Where(x => filters.Contains(x.PlatFormName));
break;
case "ComboRuleTable2":
memberships = memberships.ComboRuleTable2.Where(x => filters.Contains(x.PlatFormName));
break;
default:
break;
}
// This pulls the data from the database
var result = memberships.ToList();
Some of this will vary on how your EntityFramework is set up.
I hope this helps.

Related

Losing 2nd page of filtered query in paging links asp.net webforms

I have a page with a table that has rows of translated and untranslated text from a specific project. (This is being rendered in a loop programmatically when I'm reading from database). I also have a search box that has filters for the query.
For the overall rendering of the rows, the paging links work fine (shows 10 rows per page) but when I search for a keyword, only the first page shows the rows of the filtered query. When I click the 2nd page link, it reverts back to the 2nd page of the overall rows (without filters). I'm assuming I should keep the search keyword and if it's in search mode in a Session variable but I'm unsure of how to do that exactly...
Example of how I'm rendering the query. I wrote the query this way to be able to reuse my query when filtering for the search keyword.
string query = #"select * from (SELECT ROW_NUMBER() OVER (ORDER BY " + sortByValue + #") AS
NUMBER, *
FROM tableS s
left join tableT t
on s.source_text_id = t.source_text_id and t.language_id = #LanguageID
left join users u
on t.user_id = u.user_id
where " + searchWhereClause + searchQuery + #") AS TBL
where number between ((#PageNumber - 1) * #RowspPage + 1) and (#PageNumber * #RowspPage)
order by " + sortBy;"
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
//parameters are created here
using (SqlDataReader dr = cmd.ExecuteReader())
{
try
{
while (dr.Read())
{
//get and assign variables here
if (text.Contains(#"\n"))
{
results += "<tr><td> </td><td style=\"width:5%\"><input
id=\"copy" + counter + "\" type=\"button\" value=\"↘\"
onclick=\"doCopy(" + counter + ",'" + text + "');\"" + "/></td>"+
"<td><b id=\"" + counter +" \">" + text + " </b><i>(" + source + ")</i>" + "</td></tr>" +"<tr><td> </td><td><span id=\"dot" + counter + "\" style=\"color:" + statusColor + "\">" + "■</span></td>" +
"<td>" + "<TextArea onfocus=\"beginEdit(" + counter + "," + sourceTextID + ");\" onblur=\"endEdit(" + counter + ")\"" + "ID=\"text" + counter + "\"" + "style=\"width:90%\" TextMode=\"MultiLine\" Rows=\"2\">" + translatedText + "</textarea></td>" +
"<td><p style=\"font-size: 12px !important;\">Last Translated: " + lastMod + " by " + initials + "</p></td>" +
"</tr>";
}
else
{
results += "<tr><td> </td><td><input id=\"copy" + counter + "\" type=\"button\" value=\"↘\" onclick=\"doCopy(" + counter + ",'" + text + "');\"" + "/></td>" +
"<td><b id=\"" + counter +" \">" + text + " </b><i>(" + source + ")</i>" + "</td></tr>" +
"<tr><td> </td><td><span id=\"dot" + counter + "\" style=\"color:" + statusColor + "\">" + "■</span></td>" +
"<td><input onfocus=\"beginEdit(" + counter + "," + sourceTextID + ");\" onblur=\"endEdit(" + counter + ")\"" + "id=\"text" + counter + "\"" + "style=\"width:90%\"" + "type=\"text\"" + "value=\"" + translatedText + "\"></td>" +
"<td><p style=\"font-size: 12px !important;\">Last Translated: " + lastMod + " by " + initials + "</p></td>" +
"</tr>";
}
counter++;
}
}
catch (Exception ex)
{
ex.Message.ToString();
}
}
}
//this is where I pass in variables to paging link function
pagingLinks = CreatePagingLinks(int.Parse(pageNumber), matchCount, "default.aspx?", 10);
My paging links are created like this:
string pagingInfo= "";
int rangeFrom = rowsPerPage * (currentPage - 1) + 1;
int rangeTo = rangeFrom + rowsPerPage - 1;
int maxpages = (matches / rowsPerPage) + 1;
if (currentPage == 1)
{
pagingInfo += "Prev | ";
} else
{
pagingInfo += "Prev | ";
}
for (int k = 1; k <= maxpages; k++)
{
pagingInfo += " " + k.ToString() + " ";
}
if (currentPage == maxpages)
{
pagingInfo += " | Next";
}
else
{
pagingInfo += " | Next";
}
return pagingInfo;
I see my problem is that my filters are not persisting through the pageloads...but I'm not sure how and where to fix it.
Any help would be appreciated thank you!!

Simplify list.add of unknown amount of entries

So I am currently working on a calculator.
One of the requirements is to store the history, which I currently do in a list.
While I have simplified alot of code I can't get my head around simplifying this
if (amountNumbers == 2)
{
memory.Add(userNumbers[0].ToString() + " " + op + " " + userNumbers[1].ToString() + " = " + calculation.ToString());
userNumbers.Clear();
}
if (amountNumbers == 3)
{
memory.Add(userNumbers[0].ToString() + " " + op + " " + userNumbers[1].ToString() + " " + op + " " + userNumbers[2].ToString() + " = " + calculation.ToString());
userNumbers.Clear();
}
if (amountNumbers == 4)
{
memory.Add(userNumbers[0].ToString() + " " + op + " " + userNumbers[1].ToString() + " " + op + " " + userNumbers[2].ToString() + " " + op + " " + userNumbers[3].ToString() + " = " + calculation.ToString());
userNumbers.Clear();
}
if (amountNumbers == 5)
{
memory.Add(userNumbers[0].ToString() + " " + op + " " + userNumbers[1].ToString() + " " + op + " " + userNumbers[2].ToString() + " " + op + " " + userNumbers[3].ToString() + " " + op + " " + userNumbers[4].ToString() + " = " + calculation.ToString());
userNumbers.Clear();
}
Any idea how I simplify this and make the adding dynamic depending on how many values the user has chosen?
Preferably I want to store the whole string within one index since that is how the history is being displayed.
You can just Join userNumbers values with " " + op + " " separator, then concat calculation at end:
string lastExpression = string.Join(" " + op + " ", userNumbers) + // Join values
" = " + calculation.ToString(); // Concat calculation result
memory.Add(lastExpression);
userNumbers.Clear();
There is no matter which amount of values in userNumbers - they all will be joined with " " + op + " " between each other.
Shorter version with string interpolation:
memory.Add($"{string.Join($" {op} ", userNumbers)} = {calculation}");
userNumbers.Clear();
EDIT.
This is very similar idea with #JonasH's answer, just without converting userNumbers to a collection of strings. Join makes it implicitly at behind.
Easy, loop over userNumbers and build your string, then add it to memory once you're done. Like so:
// Ensure that we don't get an 'IndexOutOfBoundsException'
// By clamping 'amountNumbers' to the length if 'userNumbers' if it's larger
if (amountNumbers > userNumbers.Length)
amountNumbers = userNumbers.Length;
// You could use a normal 'string' and '... += ...' but 'StringBuilder' is more efficient
var sb = new StringBuilder();
for (int i = 0; i < amountNumbers; i++)
sb.Append($"{userNumbers[i].ToString()} ");
// Add the calculation, no space at the start required as the loop adds a trailing space
sb.Append($"= {calculation.ToString()}");
memory.Add(sb.ToString());
Use .Take(amountNumbers) to get at most amountNumbers of user numbers, . And String.Join to combine strings with some separator between each:
var users = userNumbers.Take(amountNumbers).Select(s => s.ToString());
var str = string.Join(" " + op + " ", users);
memory.Add(str + " = " + calculation.ToString());
If you need some special handling for cases, like if amountNumbers< userNumbers.Count or amountNumbers == 1 you need to add that separately. If you know that amountNumbers == userNumbers.Count, then the .Take(...) is not needed.

Generate Dynamic SQL Query based on Filter Criteria

We have a requirement where Client can Filter Records based on Certain Conditions.
Below is the sample Filter condition which i'm getting in Controller
[
{
"Field":"BANKNAME",
"Operator":"=",
"Value":"35",
"ConditionOperator":"OR",
"DField":"N"
},
{
"Field":"BANKNAME",
"Operator":"=",
"Value":"15",
"ConditionOperator":"AND",
"DField":"N"
},
{
"Field":"PTLOCATION",
"Operator":"=",
"Value":"261",
"ConditionOperator":" ",
"DField":"Y"
]
Based on Filter condition we are generating SQL Query, below is code which we have tried
var FilterCondition = JsonConvert.DeserializeObject<List<RptFilterCondition>>(filter);
string filterCondition = string.Empty;
var lstFilterCondition = FilterCondition.Where(a => a.DField == "N").ToList();
for (int i = 0; i < lstFilterCondition.Count; i++)
{
if (i == 0 && FilterCondition.Count == 1)
{
filterCondition += " custom.CustomeFieldName ='" + lstFilterCondition[i].Field + "' and custom.FIELDVALUE " + lstFilterCondition[i].Operator + "'" + lstFilterCondition[i].Value + "' ";
}
else if (i == 0 && !isanycondition)
{
filterCondition += " (custom.CustomeFieldName ='" + lstFilterCondition[i].Field + "' and custom.FIELDVALUE " + lstFilterCondition[i].Operator + "'" + lstFilterCondition[i].Value + "' )" + lstFilterCondition[i].ConditionOperator;
}
else if (i == FilterCondition.Count - 1)
{
filterCondition += " (custom.CustomeFieldName='" + lstFilterCondition[i].Field + "' and custom.FIELDVALUE " + lstFilterCondition[i].Operator + "'" + lstFilterCondition[i].Value + "' )";
}
else
{
filterCondition += " (custom.CustomeFieldName='" + lstFilterCondition[i].Field + "' and custom.FIELDVALUE" + lstFilterCondition[i].Operator + "'" + lstFilterCondition[i].Value + "' )" + lstFilterCondition[i].ConditionOperator;
}
}
return filterCondition;
Below is Filter condition generated
(custom.CustomeFieldName ='BANKNAME' and custom.FIELDVALUE ='35') OR
(custom.CustomeFieldName='BANKNAME' and custom.FIELDVALUE='15')
and (PTLOCATION = 261)
Below is expected output round bracket should come for same field names
((custom.CustomeFieldName ='BANKNAME' and custom.FIELDVALUE ='35') OR
(custom.CustomeFieldName='BANKNAME' and custom.FIELDVALUE='15'))
and (PTLOCATION = 261)
As mentioned in the comment, the code is vulnerable to the SQL injection.
One alternative is to use ORM, e.g. EF and write your own "engine" to translate from custom query model to the LINQ - https://learn.microsoft.com/en-us/ef/core/querying/
Easier option might be to use OData, which supports user defined querying out of the box with connection to the EF - https://learn.microsoft.com/en-us/odata/webapi/getting-started, https://learn.microsoft.com/en-us/odata/client/query-options

CSVHelper - ignore blank cells in a row unless they are populated

Using CsvHelper with .NET Core 2.2.
We're parsing a CSV file to then export to a SQL table. There are two different mappings of the CSV columns to the SQL columns, which depend on the first value of each row of the CSV.
This is what we have:
public List<TaskProdEntity> ParseCSVFile()
{
using (var reader = new StreamReader(#"C:\Users\me\Desktop\pfile.csv"))
using (var csv = new CsvReader(reader))
{
csv.Configuration.PrepareHeaderForMatch = (string header, int index) =>
header.Replace(" ", "_").Replace("(", "").Replace(")", "").Replace(".", "");
List<TaskProdEntity> records = new List<TaskProdEntity>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
if (csv.GetField<int>(0) == 1)
{
var record = new TaskProdEntity
{
Identifier = "ID Number:" + " " + csv.GetField<string>("Id"),
Region = "Business Mailing Address:" + " " + csv.GetField<string>("Provider_First_Line_Business_Mailing_Address") + " " + csv.GetField<string>("Provider_Second_Line_Business_Mailing_Address") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_City_Name") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_State_Name") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_Postal_Code") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_Country_Code_If_outside_US") + " | " + "Business Practice Location:" + " " + csv.GetField<string>("Provider_First_Line_Business_Practice_Location_Address") + " " + csv.GetField<string>("Provider_Second_Line_Business_Practice_Location_Address") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_City_Name") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_State_Name") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_Postal_Code") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_Country_Code_If_outside_US"),
Program = "Taxonomy Group:" + " " + csv.GetField<string>("Taxonomy_Group_1")
};
records.Add(record);
}
else
{
var record = new TaskProdEntity
{
Identifier = "ID Number:" + " " + csv.GetField<string>("Id3"),
Region = "Business Mailing Address:" + " " + csv.GetField<string>("Provider_First_Line_Business_Mailing_Address2") + " " + csv.GetField<string>("Provider_Second_Line_Business_Mailing_Address2") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_City_Name2") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_State_Name2") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_Postal_Code2") + " " + csv.GetField<string>("Provider_Business_Mailing_Address_Country_Code_If_outside_US2") + " | " + "Business Practice Location:" + " " + csv.GetField<string>("Provider_First_Line_Business_Practice_Location_Address2") + " " + csv.GetField<string>("Provider_Second_Line_Business_Practice_Location_Address2") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_City_Name2") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_State_Name2") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_Postal_Code2") + " " + csv.GetField<string>("Provider_Business_Practice_Location_Address_Country_Code_If_outside_US2"),
Program = "Taxonomy Group:" + " " + csv.GetField<string>("Taxonomy_Group_2")
};
records.Add(record);
}
}
return records;
}
}
Because the mapping for the Region field specifically is so messy and long, I really want to extract values from those csv fields only if the field is not blank. In many cases, many of those fields will be blank, and the business does not want a ton of concatenated blanks to end up in the database in these cases.
I am wondering if CsvHelper already has a built-in function to achieve this? If not, how would I implement that logic into the above code?
Since the second TaskProdEntity appears to add a 2 each time to the column header, you could have one method that builds your address.
public List<TaskProdEntity> ParseCSVFile()
{
using (var reader = new StreamReader(#"C:\Users\me\Desktop\pfile.csv"))
using (var csv = new CsvReader(reader))
{
csv.Configuration.PrepareHeaderForMatch = (string header, int index) =>
header.Replace(" ", "_").Replace("(", "").Replace(")", "").Replace(".", "");
List<TaskProdEntity> records = new List<TaskProdEntity>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
if (csv.GetField<int>(0) == 1)
{
var record = new TaskProdEntity
{
Identifier = "ID Number:" + " " + csv.GetField<string>("Id"),
Region = GetAddress(csv),
Program = "Taxonomy Group:" + " " + csv.GetField<string>("Taxonomy_Group_1")
};
records.Add(record);
}
else
{
var record = new TaskProdEntity
{
Identifier = "ID Number:" + " " + csv.GetField<string>("Id3"),
Region = GetAddress(csv, "2"),
Program = "Taxonomy Group:" + " " + csv.GetField<string>("Taxonomy_Group_2")
};
records.Add(record);
}
}
return records;
}
}
private string GetAddress(CsvReader csv, string extension = "")
{
var value = new StringBuilder("Business Mailing Address:");
if (csv.GetField<string>("Provider_First_Line_Business_Mailing_Address" + extension) != string.Empty)
{
value.Append(" " + csv.GetField<string>("Provider_First_Line_Business_Mailing_Address" + extension));
}
if (csv.GetField<string>("Provider_Second_Line_Business_Mailing_Address" + extension) != string.Empty)
{
value.Append(" " + csv.GetField<string>("Provider_Second_Line_Business_Mailing_Address" + extension));
}
// The rest of the if statements..............
return value.ToString();
}

Query using Entity Framework is Very SLOW [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I´m handling with a project from another company who developed a method to retrieve informations from database using Entity Framework 6, in order to populate a grid component.
We are facing a huge performance problem with this approach, even when we get just a few rows, like 70, 100, etc. I just don´t know what to do here.
I will be grateful if you can show me the correct way.
Some informations:
Visual Studio 2013
.NET 4.5
SQL Server 2017
This the method:
[HttpPost]
public object RetornaRelatorioEnvioSms(RetornaRelatorioEnvioSmsRequestModel model)
{
try
{
using (ctx = new SebraeContainer())
{
ctx.Configuration.LazyLoadingEnabled = false;
ctx.Configuration.ProxyCreationEnabled = false;
ctx.Configuration.AutoDetectChangesEnabled = false;
decimal cpfDecimal = string.IsNullOrWhiteSpace(model.CPF) ? decimal.Zero : decimal.Parse(model.CPF.Replace(".", string.Empty).Replace("-", string.Empty));
decimal cnpjDecimal = string.IsNullOrWhiteSpace(model.CNPJ) ? decimal.Zero : decimal.Parse(model.CNPJ.Replace(".", string.Empty).Replace("-", string.Empty));
string query = "select d.id_diagnostico idDiagnostico " +
" , ppf.nm_razao_social nomeCliente " +
" , sx.nm_descricao sexo " +
" , CONVERT(VARCHAR, d.dt_fim, 103) dataAtendimento " +
" , r.nm_regiao regional " +
" , cem.nm_numero email " +
" , cel.nm_numero celular " +
" , p.nm_nome porte " +
" , u.nm_completo agente " +
" from tb_diagnostico d " +
" left join tb_parceiro ppf on ppf.id_parceiro = d.id_parceiropf " +
" left join tb_parceiro ppj on ppj.id_parceiro = d.id_parceiropj " +
" left join tb_pessoa_fisica pf on pf.id_parceiro = ppf.id_parceiro " +
" left join tb_pessoa_juridica pj on pj.id_parceiro = ppj.id_parceiro " +
" left join tb_sexo sx on pf.id_sexo = sx.id_sexo " +
" left join tb_acao_regiao acr on d.id_acao = acr.id_acao " +
" left join tb_regiao r on acr.id_regiao = r.id_regiao " +
" left join tb_comunicacao cem on ppf.id_parceiro = cem.id_parceiro " +
" left join tb_comunicacao cel on ppf.id_parceiro = cel.id_parceiro " +
" left join tb_porte p on pj.id_porte = p.id_porte " +
" left join tb_usuario u on d.id_usuario = u.id_usuario " +
" where " +
" exists (select 1 " +
" from tb_historico_envio_sms_sse " +
" where id_diagnostico = d.id_diagnostico " +
" and id_tipo_mensagem_envio = 4)" +
" and d.dt_fim is not null " +
" and d.id_sistema = " + DatabaseIDs.IdSistemaSSE + " " +
" and cem.id_tipo_comunicacao = " + DatabaseIDs.IdTipoComunicacaoEmail + " " +
" and cel.id_tipo_comunicacao = " + DatabaseIDs.IdTipoComunicacaoTelefoneCelular + " ";
if (model.DataInicio != DateTime.MinValue)
query = query + " and d.dt_fim >= CONVERT(DATETIME, '" + model.DataInicio.ToString("yyyy-MM-ddT00:00:00", new CultureInfo("pt-BR")) + "', 126)";
if (model.DataFinal != DateTime.MinValue)
query = query + " and d.dt_fim <= CONVERT(DATETIME, '" + model.DataFinal.ToString("yyyy-MM-ddT23:59:59", new CultureInfo("pt-BR")) + "', 126)";
if (!string.IsNullOrWhiteSpace(model.Nome))
query = query + " and ppf.nm_razao_social like '%" + model.Nome.Replace("'", string.Empty) + "%' ";
if (!string.IsNullOrWhiteSpace(model.CPF))
query = query + " and ppf.nu_CGCCPF = " + cpfDecimal + " ";
if (model.Sexo.HasValue)
query = query + " and pf.id_sexo = " + model.Sexo.Value + " ";
if (!string.IsNullOrWhiteSpace(model.RazaoSocial))
query = query + " and ppj.nm_razao_social like '%" + model.RazaoSocial.Replace("'", string.Empty) + "%' ";
if (!string.IsNullOrWhiteSpace(model.CNPJ))
query = query + " and ppj.nu_CGCCPF = " + cnpjDecimal + " ";
if (model.Porte != null && model.Porte.Any())
query = query + " and pj.id_porte in (" + String.Join(",", model.Porte.Select(idporte => idporte.ToString())) + ") ";
if (model.Perfil != null && model.Perfil.Any())
query = query + " and d.id_perfil in (" + String.Join(",", model.Perfil.Select(idperfil => idperfil.ToString())) + ") ";
if (model.Regional != null && model.Regional.Any())
query = query + " and r.id_regiao in (" + String.Join(",", model.Regional.Select(idregiao => idregiao.ToString())) + ") ";
if (model.CNAE != null && model.CNAE.Any())
query = query + " and exists (select 1 " +
" from (select DISTINCT " +
" REPLICATE('0', 2 - LEN(cd_class)) + CONVERT(VARCHAR, cd_class) " +
" + REPLICATE('0', 5 - LEN(cd_atividade)) + cd_atividade " +
" + REPLICATE('0', 2 - LEN(id_cnaefiscal)) + id_cnaefiscal cnae " +
" from tb_diagnosticoCNAE " +
" where id_diagnostico = d.id_diagnostico) dc " +
" where dc.cnae in ('" + String.Join("', '", model.CNAE.Select(cnae => cnae)) + "'))";
if (model.Setor != null && model.Setor.Any())
query = query + " and exists (select 1 " +
" from tb_diagnosticoCNAE dc " +
" join tb_atividade_economica atv on dc.cd_atividade = atv.ds_codAtivEcon and dc.cd_class = atv.cd_class " +
" where dc.id_diagnostico = d.id_diagnostico " +
" and atv.id_setor_economico in (" + String.Join(",", model.Setor.Select(idsetor => idsetor.ToString())) + "))";
if (model.Tematica != null && model.Tematica.Any())
query = query + " and exists (select 1 " +
" from tb_diagnosticoperfiltema dpt " +
" join tb_perfiltema pt on dpt.id_perfiltema = pt.id_perfiltema " +
" join tb_tema t on pt.id_tema = t.id_tema " +
" where dpt.id_diagnostico = d.id_diagnostico " +
" and t.id_tema in (" + String.Join(",", model.Tematica.Select(idtema => idtema.ToString())) + "))";
if (model.Nivel != null && model.Nivel.Any())
query = query + " and exists (select 1 " +
" from tb_diagnosticoperfiltema " +
" where id_diagnostico = d.id_diagnostico " +
" and id_nivel in (" + String.Join(",", model.Nivel.Select(idnivel => idnivel.ToString())) + "))";
if (model.Agente != null && model.Agente.Any())
query = query + " and exists (select 1 " +
" from tb_usuario_sse " +
" where id_usuario = d.id_usuario " +
" and id_agente in (" + String.Join(",", model.Agente.Select(idagente => idagente.ToString())) + "))";
ctx.Database.CommandTimeout = 60;
IEnumerable<RetornaRelatorioEnvioSmsModel> ret = ctx.Database.SqlQuery<RetornaRelatorioEnvioSmsModel>(query).ToList();
if (ret.Any())
{
foreach (RetornaRelatorioEnvioSmsModel item in ret)
{
item.formasContato = (from d in ctx.tb_diagnostico.AsNoTracking()
where d.id_diagnostico == item.idDiagnostico
from fc in d.tb_parceiro.tb_tipo_forma_contato
select new RetornaRelatorioEnvioSmsFormaContatoModel
{
formaContato = fc.nm_tipo_forma_contato
}).ToList();
item.temas = (from d in ctx.tb_diagnostico.AsNoTracking()
where d.id_diagnostico == item.idDiagnostico
from dpt in d.tb_diagnosticoperfiltema
select new RetornaRelatorioEnvioSmsTemaModel
{
tema = dpt.tb_perfiltema.tb_tema.nm_tema,
nivel = dpt.tb_nivelmaturidade.nm_nivel
}).ToList();
item.temaPrioritario = (from d in ctx.tb_diagnostico.AsNoTracking()
where d.id_diagnostico == item.idDiagnostico
from dpt in d.tb_diagnosticoperfiltema
orderby dpt.nu_pontuacao descending, dpt.tb_perfiltema.nu_prioridade ascending
select new RetornaRelatorioEnvioSmsTemaModel
{
tema = dpt.tb_perfiltema.tb_tema.nm_tema,
nivel = dpt.tb_nivelmaturidade.nm_nivel
}).FirstOrDefault();
}
}
return ret;
}
}
catch (Exception)
{
throw;
}
}
Have the entities been set up with navigation properties? For instance does the Diagnostico entitiy have something like below declared?
public virtual Parciero ParcieroPf {get; set;}
public virtual Parciero ParcieroPj {get; set;}
From reading the second part of the query it does look like there are related entities mapped.
If you have navigation properties at your disposal then you can structure those queries to use the navigation properties rather than the embedded SQL. As mentioned, that way of querying is vulnerable to SQL injection and it should be a priority to eliminate it.
The performance cost you are likely seeing is due to the manual lazy-load that is being done to populate the various related details for the query results.
At a minimum you can speed up the loading of these related details by first extracting your "idDiagnostico" values from the query results and using those to load all of the related child records in one hit, then associate them to their respective Diagnostico entities:
So assuming you need to keep the SQL query at least to begin with:
// ... load SQL based initial data ...
List<RetornaRelatorioEnvioSmsModel> models = ctx.Database.SqlQuery<RetornaRelatorioEnvioSmsModel>(query).ToList();
if (models.Count == 0)
return models;
// Fetch all applicable IDs.
var diagnosticoIds = ret.Select(x => x.idDiagnostico).ToList();
// Load the related data for *all* applicable diagnostico IDs above. Load into their view models, then at the end, split them among the related diagnostico.
var formasContatos = ctx.tb_diagnostico
.Where(x => diagnosticoIds.Contains(x.id_diagnostico))
.Select(x => new RetornaRelatorioEnvioSmsFormaContatoModel
{
formaContato = x.tb_parceiro.tb_tipo_forma_contato.nm_tipo_forma_contato
}).ToList();
var temas = ctx.tb_diagnostico
.Where(x => diagosticoIds.Contains(x.id_diagnostico))
.Select(x => new RetornaRelatorioEnvioSmsTemaModel
{
tema = x.tb_diagnosticoperfiltema.tb_perfiltema.tb_tema.nm_tema,
nivel = x.tb_diagnosticoperfiltema.tb_nivelmaturidade.nm.nivel
}).ToList();
// This part is a bit tricky.. It looks like you want the the lowest nu_prioridade of the highest nu_pontuacao
var temaPrioritario = ctx.tb_diagnostico
.SelectMany(x => x.tb_diagnosticoperfiltema) // from diagnostico
.SelectMany(x => x.tb_perfiltema) // from diagnostico.diagnosticoperfiltema
.GroupBy(x => x.tb_diagnosticoperfiltema.tb_diagnostico.id_diagnostico) // group by diagnostico ID. Requires bi-directional references...
.Select(x => new
{
x.Key, // id_diagnostico
Tema = x.OrderByDescending(y => y.tb_diagnosticoperfiltema.nu_pontuacao)
.ThenBy(y => y.nu_prioridade)
.Select(y => new RetornaRelatorioEnvioSmsTemaModel
{
tema = y.tb_tema.nm_tema,
nivel = y.tb_diagnosticoperfiltema.tb_nivelmaturidade.nm_nivel
}).FirstOrDefault())
.Where(x => diagnosticoIds.Contains(x.Key))
.Select(x => x.Tema)
.ToList();
// Caveat, the above needs to be tested but should give you an idea on how to select the desired data.
foreach(var model in models)
{
model.formasContato = formasContatos.Where(x => x.id_diagnostico == model.id_diagnostico).ToList();
model.temas = temas.Where(x => x.id_diagnostico == model.id_diagnostico).ToList();
model.temaPrioritario = temaPrioritarios.Where(x => x.id_diagnostico == model.id_diagnostico).ToList();
}
With the navigation properties though, this can all be done away with and loaded from the initial data model retrieved. It's a pretty complex model, and the (Italian?) naming convention makes it a bit hard to follow but hopefully that gives you some ideas on how to tackle the performance issues.

Categories