Dicas para um Código Mais Performático

Objetivo

Este documento tem por objetivo mostrar alguns itens para código mais performático que foram localizados no dia a dia do Analista.

Dicas

Evitar chamadas "burras" ao server. Ao executar o ReadRecord é feita uma consulta que não vai retornar nada porque as chaves estão null e se existir campos complementares a lib vai executar uma consulta no banco que também não irá retornar nada.

//Criação do DataServer
IEduDataServer serverFilter = (IEduDataServer)RMSBroker.CreateLocalObject(dataServerUserFilterTable);

//Chamada burra somente para que seja setado o contexto no DataServer
serverFilter.ReadRecord(Context, null, null);

No caso acima é possível passar para o dataserver o contexto sem executar o ReadRecord, pois o método GetUsrFilter é um método do EduDataServer que estende de RMSDataServer.

using (IEduDataServer serverFilter = RMSBroker.CreateServer<IEduDataServer>(dataServerUserFilterTable))
  userFilters = userFilters.Append(serverFilter != null ? serverFilter.GetUsrFilter(Context) : string.Empty);
public string GetUsrFilter(RMSContext contexto)
{
  if (this.DBS == null)
	this.InitServer();

  if(contexto != null)
	this.Context = contexto;

Evitar chamadas desnecessárias de ReadRecord, se deseja apenas trazer apenas alguns campos. Ao executar o ReadRecord é feita uma consulta que ir retornar um dataset completo com vários campos que nem serão utilizados, além de trazer os campos complementares do mesmo no dataset. Por exemplo, se há a necessidade de buscar os valores dos campos CODCURSO, CODHABILITACAO e CODGRADE não se dá a necessidade de executar um ReadRecord para tal fim.

IEduDataServer dtServer = RMSBroker.CreateServer<IEduDataServer>("EduHabilitacaoFilialData");
DataSet ds = dtServer.ReadRecord(RMSSession.Context, new object[] { base.CodColigada, idHabFilial }, null);
if ((ds != null) && (ds.Tables.Count > 0) && (ds.Tables[0].Rows.Count > 0))
this.SetHabilitacaoFilial(
  ds.Tables[0].Rows[0]["CODCURSO"].ToString(),
  ds.Tables[0].Rows[0]["CODHABILITACAO"].ToString(),
  ds.Tables[0].Rows[0]["CODGRADE"].ToString(),
  idHabFilial);

No caso acima ao invés de executar o um ReadRecord, executa-se um consulta simples trazendo apenas os campos pertinentes conforme abaixo.

using (IEduHabilitacaoFilial habFilial = (IEduHabilitacaoFilial)RMSBroker.CreateRemoteObject(
																typeof(IEduHabilitacaoFilial), "EduHabilitacaoFilialData"))
{
	EduHabilitacaoFilialItem habFilialItem = habFilial.GetHabilitacaoFilial(codColigada, idHabilitacaoFilial);

	if (!string.IsNullOrEmpty(habFilialItem.CodCurso) 
		&& !string.IsNullOrEmpty(habFilialItem.CodHabilitacao) && !string.IsNullOrEmpty(habFilialItem.CodGrade))
	  this.SetHabilitacaoFilial(habFilialItem.CodCurso, habFilialItem.CodHabilitacao, 
											habFilialItem.CodGrade, idHabilitacaoFilial);
public EduHabilitacaoFilialItem GetHabilitacaoFilial(int codColigada, int idHabilitacaoFilial)
{
  EduHabilitacaoFilialItem habilitacaoFilial = new EduHabilitacaoFilialItem();

  if (this.DBS == null)
	this.InitServer();

  DataTable dtHabFilial = this.DBS.QuerySelect(EduHabilitacaoFilialProps.SHabilitacaoFilial,
	@"SELECT SHABILITACAOFILIAL.CODCURSO,
			 SHABILITACAOFILIAL.CODHABILITACAO,
			 SHABILITACAOFILIAL.CODGRADE
	  FROM   SHABILITACAOFILIAL (NOLOCK)
	  WHERE  SHABILITACAOFILIAL.CODCOLIGADA = :CODCOLIGADA_N
			 AND SHABILITACAOFILIAL.IDHABILITACAOFILIAL = :IDHABILITACAOFILIAL_N /*AND*/", codColigada, idHabilitacaoFilial);

  SetHabilitacaoFilialItem(codColigada, idHabilitacaoFilial, habilitacaoFilial, dtHabFilial);

  return habilitacaoFilial;
}

Sempre observar a ordem das chaves para fazer um join, ao colocar na ordem correta se torna mais performática a consulta. Abaixo temos um join com chaves fora da ordem no banco de dados.

JOIN SGRADE (NOLOCK)
 ON SGRADE.CODGRADE = SHABILITACAOFILIAL.CODGRADE
	AND SGRADE.CODCOLIGADA = SHABILITACAO.CODCOLIGADA
	AND SGRADE.CODCURSO = SHABILITACAO.CODCURSO
	AND SGRADE.CODHABILITACAO = SHABILITACAO.CODHABILITACAO

Abaixo temos o mesmo join com a ordem correta do banco.

JOIN SGRADE (NOLOCK)
 ON SGRADE.CODCOLIGADA = SHABILITACAOFILIAL.CODCOLIGADA
	AND SGRADE.CODCURSO = SHABILITACAOFILIAL.CODCURSO
	AND SGRADE.CODHABILITACAO = SHABILITACAOFILIAL.CODHABILITACAO
	AND SGRADE.CODGRADE = SHABILITACAOFILIAL.CODGRADE

Ordem das chaves no banco.

Sempre informar o tipo do campo no filtro da sua consulta. Abaixo temos um exemplo de filtro sem o tipo. A importância deste tipo é fazer com que a lib não procure que tipo (string, número, data e etc.) é aquele campo do filtro.

WHERE  SMATRICPL.CODCOLIGADA = :CODCOLIGADA
	   AND SMATRICPL.IDPERLET = :IDPERLET
	   AND SMATRICPL.IDHABILITACAOFILIAL = :IDHABILITACAOFILIAL
	   AND SMATRICPL.RA = :RA

Abaixo temos a tipagem de campos do filtro.

WHERE  SMATRICPL.CODCOLIGADA = :CODCOLIGADA_N
	   AND SMATRICPL.IDPERLET = :IDPERLET_N
	   AND SMATRICPL.IDHABILITACAOFILIAL = :IDHABILITACAOFILIAL_N
	   AND SMATRICPL.RA = :RA_S

Tipos disponíveis:

  • _D: DateTime;
  • _V: Float;
  • _N: Int;
  • _S: String;
  • _H: Short.

Sempre utilizar o cache de parâmetro no lado Client, o carregamento da classe de parâmetros é muito custoso. Abaixo temos um exemplo que sempre ao chamar o EduParametros é feito o carregamento de toda a classe de parâmetros.

private EduParams EduParametros
{
  get
  {
	_params = new EduParams(RMSSession.Context.CodColigada, RMSSession.Context.CodFilial, RMSSession.Context.CodTipoCurso);
	return _params;
  }
}

Segue abaixo a utilização de cache de parâmetros.

private EduParams EduParametros
{
  get
  {
	if (_params == null)
	  _params = new EduParams(RMSSession.Context.CodColigada, RMSSession.Context.CodFilial, RMSSession.Context.CodTipoCurso);
	return _params;
  }
}
private EduParams _params = null;
Evite utilizar os serviços abaixo para atualização de informações em lote, pois os mesmo geram muito IO no banco.
Dependendo do volume de dados isso se torna muito custoso, principalmente se estiver dentro de um laço de repetição.
EduDatasetUtils.EditaRegistro<EduLancamentoParcelaDescPontualItem>
("SLANDESCPONTUAL", descontoPontual, campos, valoresPK, this.DBS, codUsuario, out registroExiste, out pk);
EduDatasetUtils.ExcluiRegistro<EduLancamentoParcelaDescPontualItem>
("SLANDESCPONTUAL", campos, valores, this.DBS, codUsuario);
EduDatasetUtils.InsereRegistro<EduLancamentoParcelaDescPontualItem>
("SLANDESCPONTUAL", descontoPontual, this.DBS, codUsuario);

Uma solução seria conforme abaixo, carregar uma lista e executar um QueryUpdate com todos os dados.

DataTable tbMatriculaDisc = this.DBS.QuerySelect(string.Empty, 
"SELECT * FROM SMATRICULA (NOLOCK) WHERE 0=1 /*AND*/"); // donotlocalize
ObjectList<EduMatriculaDiscItem> MatriculaDisc = this.dbo.ReadFromTable<EduMatriculaDiscItem>(tbMatriculaDisc);
EduMatriculaDiscItem mat = MatriculaDisc.NewObject();
this.dbo.MoveValues(paramDisc, mat);
this.dbo.WriteToTable<EduMatriculaDiscItem>(MatriculaDisc);
this.DBS.QueryUpdate(tbMatriculaDisc, paramDisc.CodUsuario);