Apaixonado por tecnologia. Trabalho com tecnologia desde 2003.

Saiba mais sobre minha vida profissional aqui .

Fale comigo.
Siga-me no Twitter
Ultimos comentários
Calendário de Posts
<<  novembro 2017  >>
stqqssd
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Siga o @DicaDoNerd no Twitter e acompanhe as novidades


Depois de apanhar um pouco com o evento SelectedNodeChanged do TreeView do ASP.NET descobri que o fato de você clicar no checkbox não dispara o evendo para o servidor. Procurei um pouco e encontrei este site que funcionou perfeitamente.

Publiquei este artigo que demostra como preencher um TreeView de forma recursiva utilizando LinqToSql, como este não é o foco, não vou entrar em detalhes sobre o preenchimento.

Primeiro vamos montrar a estrutura para exclusão, neste artigo é possível entender as propriedades ObjectTrackingEnabled e DeferredLoadingEnabled. Para a estrutura do Context utilizei uma técnica bem interessante deste livro (que aliás é excelente) que foi originado deste site

image Criei uma tabela de segmentos que utiliza uma recursividade (ParentID) para preechimento e o grande problema era excluir os dados no TreeView.

Criei uma solução bastante simples para este problema. Habilitei o Checkbox no TreeView e passo uma lista de Ids que serão excluídas.

O primeiro passo é habilitar o TreeView e criar um botão para excluir os itens selecionados.

 
 
 
 
 
 
<asp:LinkButton ID="lk" runat="server" OnClick="Excluir" Text="Excluir" />
<asp:TreeView ID="tv" ShowCheckBoxes="All" runat="server" />

image
HTML Resultado.

 

Agora vamos incluir o javascript no projeto que será resposável para selecionar os checkboxs.

<script type="text/javascript">
function OnCheckBoxCheckChanged(evt) {
var src = window.event != window.undefined ? window.event.srcElement : evt.target;
var isChkBoxClick = (src.tagName.toLowerCase() == "input" && src.type == "checkbox");
if (isChkBoxClick) {
var parentTable = GetParentByTagName("table", src);
var nxtSibling = parentTable.nextSibling;
if (nxtSibling && nxtSibling.nodeType == 1)//check if nxt sibling is not null & is an element node
{
if (nxtSibling.tagName.toLowerCase() == "div") //if node has children
{
//check or uncheck children at all levels
CheckUncheckChildren(parentTable.nextSibling, src.checked);
}
}
//check or uncheck parents at all levels
CheckUncheckParents(src, src.checked);
}
}
function CheckUncheckChildren(childContainer, check) {
var childChkBoxes = childContainer.getElementsByTagName("input");
var childChkBoxCount = childChkBoxes.length;
for (var i = 0; i < childChkBoxCount; i++) {
childChkBoxes[i].checked = check;
}
}
function CheckUncheckParents(srcChild, check) {
var parentDiv = GetParentByTagName("div", srcChild);
var parentNodeTable = parentDiv.previousSibling;

if (parentNodeTable) {
var checkUncheckSwitch;

if (check) //checkbox checked
{
var isAllSiblingsChecked = AreAllSiblingsChecked(srcChild);
if (isAllSiblingsChecked)
checkUncheckSwitch = true;
else
return; //do not need to check parent if any(one or more) child not checked
}
else //checkbox unchecked
{
checkUncheckSwitch = false;
}

var inpElemsInParentTable = parentNodeTable.getElementsByTagName("input");
if (inpElemsInParentTable.length > 0) {
var parentNodeChkBox = inpElemsInParentTable[0];
parentNodeChkBox.checked = checkUncheckSwitch;
//do the same recursively
CheckUncheckParents(parentNodeChkBox, checkUncheckSwitch);
}
}
}
function AreAllSiblingsChecked(chkBox) {
var parentDiv = GetParentByTagName("div", chkBox);
var childCount = parentDiv.childNodes.length;
for (var i = 0; i < childCount; i++) {
if (parentDiv.childNodes[i].nodeType == 1) //check if the child node is an element node
{
if (parentDiv.childNodes[i].tagName.toLowerCase() == "table") {
var prevChkBox = parentDiv.childNodes[i].getElementsByTagName("input")[0];
//if any of sibling nodes are not checked, return false
if (!prevChkBox.checked) {
return false;
}
}
}
}
return true;
}
//utility function to get the container of an element by tagname
function GetParentByTagName(parentTagName, childElementObj) {
var parent = childElementObj.parentNode;
while (parent.tagName.toLowerCase() != parentTagName.toLowerCase()) {
parent = parent.parentNode;
}
return parent;
}

</script>

Pronto! Camada de apresentação pronta, vamos preparar a estruta que será responsável pela exclusão.

 

image Anexando a classe no dbml (LinqToSql)

Tem um artigo que demonstra como criar o objeto relacional com o Linq.

 

Crie uma classe DBHelper.

public static class DbHelper
{
public static dbDadosDataContext getContextData(bool ObjectTrackingEnabled)
{
var db = getContextData();
db.ObjectTrackingEnabled = ObjectTrackingEnabled;
return db;
}

public static dbDadosDataContext getContextData(bool ObjectTrackingEnabled, bool DeferredLoadingEnabled)
{
var db = getContextData();
db.ObjectTrackingEnabled = ObjectTrackingEnabled;
db.DeferredLoadingEnabled = DeferredLoadingEnabled;
return db;

}

public static dbDadosDataContext getContextData()
{
//Colocar a connectionString no web.Config
string strCnn = ConfigurationManager.ConnectionStrings["cnnConnectionString"].ConnectionString;
var db = new dbDadosDataContext(strCnn);
db.DeferredLoadingEnabled = false;
db.ObjectTrackingEnabled = false;
return db;
}

}

Agora vamos criar o método responsável pela exclusão.

 

public class Segmento : IDisposable
{
private dbDadosDataContext db;
public Segmento()
{
db = DbHelper.getContextData();
}

public void ExcluirSegmentos(List<int> listSegments)
{
db.ObjectTrackingEnabled = true; // Habilito o tracking por haver manipulação no BD.
var segs = db.SegmentoEntidades.Where(p => listSegments.Contains(p.IDSegmento));
db.SegmentoEntidades.DeleteAllOnSubmit(segs);
db.SubmitChanges();

}

public void PreencherTreeViewSegmento(TreeView objTreeView, int? IDNoCarregar)
{
//A vantagem de utilizar o IQuarable é que a execução do Banco só será realizada no Bind.
IQueryable<SegmentoEntidade> seg = db.SegmentoEntidades
.Where(p => p.Inativo == false)
.OrderBy(p => p.ParentID)
.OrderBy(p => p.Segmento);

//Limpo os nodes existentes.
objTreeView.Nodes.Clear();

var itens = seg.Where(p => (p.ParentID == null ? 0 : p.ParentID) == (IDNoCarregar == null ? 0 : IDNoCarregar))
.OrderBy(p => p.ParentID)
.OrderBy(p => p.Segmento);

//passo por todos os nodes que foi passado como parâmetro para carregar. (organizando em ordem alfabética
foreach (var item in itens)
{
//Preencho o nó pai
TreeNode nodePai = new TreeNode();
nodePai.Value = item.IDSegmento.ToString();
nodePai.Text = item.Segmento;
nodePai.ImageToolTip = item.Segmento;
//nodePai.ImageUrl = _db.IconeEntidades.Where(p => p.IDIcone == item.IDIcone).FirstOrDefault().urlImagem;
//Preecho os filhos
PreencherNoFilho(nodePai, seg);

//Adiciono o nó que foi preenchido
objTreeView.Nodes.Add(nodePai);
}

}
protected void PreencherNoFilho(TreeNode parentNode, IQueryable<SegmentoEntidade> segmentos)
{
//Passo por todos os elementos filhos do nó pai
var itens = segmentos.Where(p => p.ParentID == int.Parse(parentNode.Value)).OrderBy(p => p.ParentID)
.OrderBy(p => p.Segmento)

foreach (var item in itens)
{

TreeNode noFiltro = new TreeNode();
noFiltro.Value = item.IDSegmento.ToString();
noFiltro.Text = item.Segmento;
noFiltro.SelectAction = TreeNodeSelectAction.SelectExpand; //Expando no nó.
parentNode.ChildNodes.Add(noFiltro); //adiciono na coleção de nós
PreencherNoFilho(noFiltro, segmentos); //Preenchemos os filhos deste Node (recursividade)
}


}

~Segmento()
{
this.Dispose();
}

#region IDisposable Members

public void Dispose()
{
GC.Collet();
GC.SuppressFinalize(this);
}

#endregion
}

Agora vamos criar os métodos na camada de apresentação.

Page_Load.

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//Preencho o controle treeview
FillControls(); //
//atribuimos o evento click ao checkbox
tv.Attributes.Add("onclick", "OnCheckBoxCheckChanged(event)");

}

}

O método responsável para

Preencher o controle TreeView.

protected void FillControls()
{

using (Segmento seg = new Segmento())
{
seg.PreencherTreeViewSegmento(tv, null);

}

}

Agora vamos criar o método que irá excluir.

protected void Excluir(object sender, EventArgs e)
{
List<int> lista = new List<int>();
//Preencho a lista com os Nodes selecionados.

foreach (TreeNode item in tv.CheckedNodes)
lista.Add(int.Parse(item.Value));

using (Negocio.Cliente.Segmento seg = new Negocio.Cliente.Segmento())
{
seg.ExcluirSegmentos(lista);
}
FillControls();
}

Pronto, de uma forma bem simples e estruturada preenchemos e excluimos itens com checkbox.

Espero que tenham gostado.

Sigam o @DicaDoNerd no Twitter.

Acredito que não usamos nem 30% do que o Banco de Dados SQL Server oferece. Há alguns dias, conversava com alguns amigos e lembrei de uma dica que a ordem de criação dos índices e consultas faz diferença, então, resolvi testar.

Tive uma surpresa, realmente faz diferença, algo que nem sempre nos atentamos, seguem as evidências dos testes.

Tamanho da tabela

clip_image002

Criação do índice (Notem que inclusive há botões, mover para cima e mover para baixo) .

clip_image002[8]

 

clip_image002[10]

clip_image002[12]

Resultado usando o índice corretamente. (CPF, Nome) Resultado usando o índice com a ordem invertida

O que achei mais engraçado é que o execution Plain apontou 50% para cada execução mostrando que o indice CPF_Nome foi acionado, mas, fez diferença no resultado (Select acima). Eu executei muitas vezes, pois, o Sql Server sempre mantém informações das consultas realizadas…

image

Não contente, fiz uma aplicação para comparar a execução e….. surpresa!!! Mesmo com o ExecutionPlain dizer que o custo era igual para ambos, a execução tem o tempo diferente.

image

Se for um cara chato como eu, vai logo pensar… Bom, depende de como foram feitos os testes… então vaí aí como foi realizado o teste.

class Program
{
static void Main(string[] args)
{

string strCnn = System.Configuration.ConfigurationSettings.AppSettings["cnn"];
Stopwatch timer = new Stopwatch();

for (int i = 0; i < 10; i++)
{
timer.Start();

using (SqlConnection cnn = new SqlConnection(strCnn))
{
using (SqlCommand cmd = new SqlCommand("Select * from Clientes where cpf like '%317395%' and nome like '%Minato%'", cnn))
{
cnn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.CloseConnection);
}

}
timer.Stop();
Console.WriteLine("Utilizando Corretamente o Indice (CPf, Nome) " + timer.Elapsed);
timer.Reset();


timer.Start();

using (SqlConnection cnn = new SqlConnection(strCnn))
{
using (SqlCommand cmd = new SqlCommand("Select * from Clientes where nome like '%Minato%' and cpf like '%317395%'", cnn))
{
cnn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.CloseConnection);
}

}
timer.Stop();
Console.WriteLine("Utiliando com a ordem invertida (Nome, CPF) " + timer.Elapsed);
timer.Reset();

}

Console.ReadKey();


}
}

 

Então fica a dica. ;-), vamos usar os indices na sequencia correta.

Sigam @DicaDoNerd.

 

Uma das dúvidas que sempre tive é como uma aplicação de grande porte baseada no LinqToSql se comportaria. Hoje trabalho em um segmento (bolsa de valores) onde performance é extremamente necessário. Sabemos que tabelas com grande concorrência sem a utilização do NOLOCK pode apresentar grandes problemas, este problema ainda não sei como resolver, mas uma coisa que consegui é estudar algumas soluções que podem melhorar a performance no LinqToSql.

A primeira cois foi criar uma tabela com tamanho razoável (Estou utilizando uma tabela de uma aplicação real) para tentar espelhar uma realidade (com tipos Nullables, campos preenchidos, não preenchidos e etc). A tabela contém mais de um milhão de registros (1.015.232) com 16 campos.

Select com a quantidade de registros (1.015.232) mais de um milhão registros
Tipo de dados e campos da tabela
Entidade criada a partir do LinqToSql (dbml)

É importante entender quando podemos desativar as propriedades ObjectTrackingEnabled e DeferredLoadingEnabled.

ObjectTrackingEnabled = Essa propriedade só deve ser desativada quando for uma operação de SELECT, ou seja, qualquer outra ação que manipule informações no banco de dados (INSERT, UPDATE ou DELETE), deve ter a propriedade habilitada, caso contrário, você irá se deparar com a seguinte exception.

Object tracking is not enabled for the current data context instance.

Exception disparada pela falta do ObjectTracking

Quando existe um relacionamento no banco de dados e você cria o ORM com o LinqToSql são criadas relações entre as tableas e você pode ter acesso a tabela relacionadas, por exemplo, se na tabela cliente houvesse uma tabela relacionada com tipo de cliente, porderíamos ter acesso ao tipo desta forma

ClienteEntidade.TipoClienteEntidade.TipoCliente

Ocorre que pode se tornar um problema quando há uma grande quantidade de registros, por uma boa prática devemos desabilitar este objeto e habilitarmos apenas quando necessário. Uma alternativa para carregar apenas a tabela necessária (quando há mais de duas tabelas relacionadas) é utilizar LoadWith .

Para desabilitar o mapeamento de entidades relacionadas é desabilitar a propriedade DeferredLoadingEnabled.

Agora vamos ver na prática se isso funciona mesmo. Vamos aos testes.

Como sou desconfiado de resultados baseado em uma unica execução, eu sempre acho que fica algo em cache, que fica algo na memória, enfim, fiz algumas repetições para garantir.

class Program
{
static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();

int qtde = 0, qtdadeTracking = 0;
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Iteração: " + i);
timer.Start();
using (dbDadosDataContext db = new dbDadosDataContext())
{

qtde = db.ClientesEntidades.ToList().Count;
}
timer.Stop();
Console.WriteLine("Tempo com as propriedades habilitadas {0} qtade {1:N0}", timer.Elapsed, qtde);
timer.Reset();
timer.Start();
using (dbDadosDataContext db = new dbDadosDataContext())
{

db.DeferredLoadingEnabled = false; // Desabilitando as tabelas relacionadas.
db.ObjectTrackingEnabled = false; // Desabilito o tracking
qtdadeTracking = db.ClientesEntidades.ToList().Count;
}
timer.Stop();
Console.WriteLine("Tempo com as propriedades desabilitadas {0} - qtdade {1:N0}", timer.Elapsed, qtdadeTracking);
}
Console.ReadKey();

}
}

 

Resultado do teste: É possível diminuir pela metade o tempo de execução.

Espero que tenham gostado.

Que Jquery é uma bibliocate que facilita muito a vida do programador, isso ninguém duvida, mas, uma coisa que as vezes temos problemas e entender os plugins que são criados. Quando entendemos colocamos a mão na cabeça e dizemos “Como sou burro, era só isso!”… Sempre que tenho um problema que não consigo resolver a primeira coisa é pesquisar no Google mas como não gosto simplesmente do “Copy and Paste”, fico tentando entender bibliotecas, refatorando dll’s, estudando alguns frameworks, então, lá vai mais uma dica.

Vou utilizar neste exemplo o plugin masked input.

Existe uma forma bem simples de criar, basta criar um estilo (css) para seu Input (asp:TextBox)

.campoFormData {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormCEP {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormTel {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormDDD {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormCPF {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormCNPJ {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormD2 {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}

Criamos classes para cada Tipo de formatação (vale lembrar que isso será utilizado no Selector ($) do Jquery).

Feito isso, vamos para o HTML.

<asp:TextBox ID="txtCPF" runat="server" MaxLength="15" CssClass="campoFormCPF"  />
<asp:TextBox ID="txtCEP" runat="server" CssClass="campoFormCEP" MaxLength="9" />
<asp:TextBox ID="txtTelefone" runat="server" MaxLength="10" CssClass="campoFormTel" />
<asp:TextBox ID="txtCelularDDD" runat="server" MaxLength="2" CssClass="campoFormDDD" Width="20px" />
<asp:TextBox ID="txtCelular" runat="server" MaxLength="10" CssClass="campoFormTel" />

Agora vamos referenciar a biblioteca do Jquery.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://cloud.github.com/downloads/digitalBush/jquery.maskedinput/jquery.maskedinput-1.3.min.js">
</script>

Apenas lembrando para quem utiliza o Visual Studio 2010 (Framework 4.0) é possível habilitar a biblioteca do Jquery (sem precisar fazer a referência do script) habilitando a propriedade EnableCdn (Content Delivery Network). Neste link você encontra algumas dicas bem bacanas sobre algumas novidades e aqui você encontra as bibliotecas disponíveis no Cdn.

<asp:ScriptManager ID="smMaster" runat="server" EnablePartialRendering="true" EnableCdn="true" />

Agora vamos criar o script que será responsável por executar o script.

function AtribuirMascaras() {

$(".campoFormD2").unmask();
$(".campoFormD2").mask("99");

$(".campoFormData").unmask();
$(".campoFormData").mask("99/99/9999");

$(".campoFormCEP").unmask();
$(".campoFormCEP").mask("99999-999");

$(".campoFormTel").unmask();
$(".campoFormTel").mask("9999-9999");

$(".campoFormDDD").unmask();
$(".campoFormDDD").mask("99");

$(".campoFormCPF").unmask();
$(".campoFormCPF").mask("999.999.999-99");

$(".campoFormCNPJ").unmask();
$(".campoFormCNPJ").mask("99.999.999/9999-99");

}

Com a função pronta, vamos executar:

<script type="text/javascript">
$(document).ready(function () {
AtribuirMascaras();
});
</script>

Um problema comum para quem utiliza este plugin (Mask) com UpdatePanel é ele se “perder” isso acontece, pelo fato de que foi enviada uma requisição para o servidor e o HTML retornado não tem mais o evento vinculado aos controles.

Postei há algum tempo uma forma bem simples de resolver este problema, basta fazer isso:

<script>
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(registraScript);

function registraScript(sender, args) {
AtribuirMascaras();
}


</script>

Isso faz com que ao final de cada requisição ao servidor o script seja registrado novamente.

Sou um apaixonado por produtividade e inovação. Sempre procuro novas tecnologias, maneiras diferente de fazer a mesma coisa, no entando, isso sempre toma muito tempo este é o motivo de manter este blog. Compartilhar algumas dicas, truques sobre o que adoro fazer.

Há tempos utilizo LinqToSql e acredito que todos os componentes que fazem mapeamento relacional como LinqToSql, EntityFramework, Hibernate, JPA e etc, fazem com que o desenvolvimento reduza pela metade do tempo, desde que, saiba exatamente como usar o framework, neste caso a curva de aprendizado será grande, excepcionalmente se você não for um Nerd que dedica horas da sua vida à pesquisar coisas que talvez nunca use. :)

Você pode utilizar um LinqDataSource como fonte de dados para GridView, FormView, DetailsView.

Para isso você deverá criar um Contexto para o LinqToSql (existem diversos tutoriais na internet, por ser bastante básico, não vou entrar em detalhes por não ser o foco do post).

Criado o Contexto você inclui o objeto LinqDataSource, neste exemplo, simulo um objeto Contexto (dbDataContex) Ordenando por “Nome”, utilizo um objeto anônio (Anonymous Types), em TableName, informo qual a classe (Entidade) que contem o Mapeamente Relacional gerado pelo LinqToSql, por fim vinculamos o evento (linqDsClientes_Selected) que será utilizado na seleção de um item no GridView.

<asp:LinqDataSource ID="linqDsClientes" runat="server"  
ContextTypeName="dbDataContext" OrderBy="Nome"
Select="new (IDCliente, Nome, Email, DataNascimento)"
TableName="ClienteEntidades" onselected="linqDsClientes_Selected">
</asp:LinqDataSource>

Com objeto criado, vamos associar ao gridview, para isso basta na propriedade DataSourceID, informar o ID do objeto LinqDataSource.

<asp:GridView ID="GridView1" runat="server" DataSourceID="linqDsClientes" />
<!--Label que informará quantos registros foram encontrados -->
<asp:Label ID="lblQtdade" runat="server" />

Pronto, assim, já temos vinculado a grid com o controle, agora, vamos manipular as informações.

Para filtrar informações no LinqDataSource (WhereParameters).

linqDsClientes.WhereParameters.Clear(); //Limpa possíveis parâmetros
linqDsClientes.Where = "Nome.Contains(@Nome)"; // Procuro na propriedade Nome o texto que contenha o parâmetro passado
linqDsClientes.WhereParameters.Add("Nome", System.Data.DbType.String, txtNome.Text); // Adiciono os parâmetros.

Agora uma coisa bem simples, que demorei um tempão para descobrir é o count(), que pode ser feito através do Evento OnSelected.

protected void linqDsClientes_Selected(object sender, LinqDataSourceStatusEventArgs e)
{
lblResultado.Text = string.Format("Total de clientes encontrados: {0}", e.TotalRowCount);
}

Certamente ainda há muito o que ser pesquisado sobre este objeto, mas, foi uma pequena contribuição…

Mande sugestão ou dúvida para contato@alexandreminato.com.br

Até a próxima 8-)

 
teste