我正在处理另一家公司的项目,该公司开发了一种使用Entity Framework 6从数据库检索信息的方法,以便填充网格组件。
即使我们只得到几行,例如70、100等,我们也用这种方法面临着巨大的性能问题。我只是不知道该怎么办。
如果您能以正确的方式告诉我,我将不胜感激。
一些信息: Visual Studio 2013 .NET 4.5 SQL Server 2017
此方法:
[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;
}
}
答案 0 :(得分:1)
是否已使用导航属性设置实体?例如,Diagnostico实体是否具有以下声明的内容?
public virtual Parciero ParcieroPf {get; set;}
public virtual Parciero ParcieroPj {get; set;}
从阅读查询的第二部分看,它确实看起来像有映射的相关实体。
如果可以使用导航属性,则可以构造这些查询以使用导航属性而不是嵌入式SQL。如前所述,这种查询方式容易受到SQL注入的影响,因此应该优先考虑消除它。
您可能会看到的性能成本是由于手动执行延迟加载而产生的,以填充查询结果的各种相关详细信息。
至少,您可以通过以下方法来加快这些相关详细信息的加载:首先从查询结果中提取“ idDiagnostico”值,然后使用这些值在一次匹配中加载所有相关的子记录,然后将它们关联到各自的Diagnostico实体:
因此,假设您至少需要保留SQL查询以开始:
// ... 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();
}
尽管有了导航属性,这一切都可以免除并从检索到的初始数据模型中加载。这是一个非常复杂的模型,并且(意大利(Italian?))命名约定使遵循起来有点困难,但希望它能为您提供一些有关如何解决性能问题的想法。