Manter Dataset aberto condicionalmente
26/10/2005
0
Estou modificando algumas coisas aqui num sistema, visando principalmente permitir o acesso a diversos forms simultaneamente ou não, porém estou com um problema. Uso sempre DataModule para armazenar os datasets e datasources (IBX) de praticamente tudo nos meus sistemas, apenas algumas poucas queries coloco no próprio form que a usa.
Normalmente ao abrir um form, eu abro os dm.datasets que serão usados por ele, e ao fechar o form eu fecho os datasets abertos. Porém um mesmo dataset, lá do DM, também é aberto por outro form, que por sua vez também o fecha ao sair... Mas e o form que ficou aberto que e continua precisando daquelas informações? O que faço?
Pensei em usar a propriedade TAG do dataset para armazenar um número referente ao primeiro form que abriu o dataset e somente fechar o dataset se o pedido de fechamento viesse deste mesmo form, os demais não fechariam o dataset se o ´dono´ dele, informado na propriedade tag do dataset, fosse outro.
Porém isso fica muito numérico para meu gosto (cada form teria que ser identificado por um número para ser colocado nessas tags), então pensei em criar variáveis globais no DM, uma para cada dataset, armazenando sempre que um dataset for aberto o nome do form ´dono´. E sempre que algum form for fechar o dataset verificar se dono do dataset é ele próprio, senão, não fecha o dataset.
Será que deu pra entender? Será que estou pensando corretamente ou viajei na maionese... O que me recomendariam fazer nestes casos?
Aerreira
Posts
26/10/2005
Bon Jovi
Outras coisas:
Ter um único data module pra tudo num sistema médio ou grande pode ficar confuso. Pra alguns conjuntos de regras de negócio é bom separar. E particularmente acho mais legal usar classes TObject em conjunto com datamodule.
DataSource acho melhor ficar melhor no Form.
26/10/2005
Bon Jovi
Complementando: com isso terá que setar o Connection e demais objetos em tempo de execução se quiser chamar de outra classe.
27/10/2005
Anfm
Para fazer isto faça o seguinte:
No uses da sua unit declare o unit do datamodule;
Na seção private declare uma variável que receberá a cópia do datamodule: DMLocal : TDataModule;
No on create do seu form coloque:
DMLocal := TDataModule.Create(Self)
DataSource1.DataSet := DMLocal.NomeTabela;
27/10/2005
Aerreira
Fiz o que você falou: o datamodule já está no uses do form, declarei dmLocal em private, coloquei um DataSource no form e chamei de dsConv, e coloquei o código abaixo no oncreate do form:
dmLocal := TDataModule.Create(Self);
dsConv.dataset := dmLocal.tbConv;
O problema nesse caso é que não existe dmLocal.tbConv (leia-se tbConv como o DataSet que está no DM), o que existe sim é dm.tbConv, ou seja apenas a tabela no DM original e não em dmLocal. Não faltou alguma coisa aí não? Ligar o dmLocal ao dm real?
Como criar uma instância diferente do mesmo DM em cada form ?
Desculpem a ignorância, mas realmente não faço esse tipo de coisa.
27/10/2005
Aerreira
O problema acima é: se eu tiro o ´var dm: Tdm;´ do datamodule eu fico com tudo que o sistema atualmente se referencia a ´dm...´ sem funcionar, então não sei se o dmLocal funciona mesmo ou não... Pois no teste que fiz acima com dmLocal não funcionou. Será que é porque ainda tenho o ´var dm: Tdm´ lá no datamodule ?
27/10/2005
Anfm
Onde vc está criando o Data Module local
dmLocal := TDataModule.Create(Self);
vc tem q se referenciar ao DataModule que vc já possui, ou seja, vc vai criar uma cópia dele com todos componentes que ele possui.
Suponhamos q o seu DataModule chame dmtabelas,
então vc deve criar da seguinte maneira:
dmLocal := Tdmtabelas.Create(Self);
A unit que contém o DataModule deve ser colocado no uses principal da Unit, onde estão as units forms, dialogs, etc.
27/10/2005
Aerreira
dmLocal := Tdmtabelas.Create(Self);
Meu DataModule chama-se ´dm´, então tentei:
dmLocal := Tdm.Create(Self);
dsConv.dataset := dmLocal.tbConv;
Resultado: Undeclared Identifier na segunda linha
Outro teste (o que eu estava fazendo antes):
dmLocal := TDataModule.Create(Self);
dsConv.dataset := dmLocal.tbConv;
Resultado: o mesmo erro na segunda linha.
O nome da unit do DataModule é ´datamodule´ e o nome do form é ´dm´. Fiz os testes acima com o ´datamodule´ no uses após implementation, e também colocando no uses lá de cima, após interface. Não funcionou em nenhuma das duas situações.
Tá faltando alguma coisa... mas não sei o que.
27/10/2005
Firekiller
Eu particularmente, trabalho com vários datamodules. Por exemplo, para o form de cadastro de clientes, tenho um datamodule que contem 1 dataset para conexão com a tabela de clientes. Para o form de vendas, tenho um datamodule com 1 dataset para a tabela vendas, 1 para Itens, 1 para Produtos e 1 para Clientes. Dessa forma aplico as regras que desejo para cada datamodule individualmente.
Para facilitar (quando devo abrir o que), crio procedimentos dentro do datamodule do tipo Abrir e Fechar, assim, para abrir o que preciso de um datamodule faço:
DmClientes.Abrir;
ou
DmClientes.Fechar;
Normalmente, antes de criar esses datamodules, crio um datamodule matriz que dará origem a todos os outros, nele é que coloco esse tipo de função, então trato, cada um de acordo com a necessidade. Assim, não preciso ficar criando instancias do datamodule dentro do sistema.
27/10/2005
Aerreira
Taí, gostei da sua ideia de usar o tag para controlar ´quantos´ abriram o dataset e não ´quem´ como eu havia pensado, simplifica tudo, basta ir incrementando ao abrir e diminuindo ao pedir para fechar, mas realmente só fechar quando o tag estiver em 0.
Acredito que isso irá funcionar, mas fiquei curioso em ver a situação passada pelo ANFM e pelo BON JOVI com relação a criação instâncias locais para o DataModule, pena que ainda não funcionou... Alguem saberia dizer o que ainda está errado ?
27/10/2005
Bon Jovi
//Project1.dpr program Project1; uses Forms, Unit1 in ´Unit1.pas´ , Unit2 in ´Unit2.pas´ {DataModule1: TDataModule}; {$R *.RES} var Form1: TForm1; begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. //Form... //Unit1.pas unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, Unit2, Grids, DBGrids; type TForm1 = class(TForm) DataSource1: TDataSource; DBGrid1: TDBGrid; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } DataModule1: TDataModule1; public { Public declarations } end; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin DataModule1 := TDataModule1.Create(nil); DataSource1.DataSet := DataModule1.ADODataSet1; end; procedure TForm1.FormDestroy(Sender: TObject); begin DataModule1.Free; end; end. //Unit1.DFM object Form1: TForm1 Left = 192 Top = 107 Width = 696 Height = 480 Caption = ´Form1´ OnCreate = FormCreate OnDestroy = FormDestroy object DBGrid1: TDBGrid Left = 4 Top = 4 Width = 320 Height = 120 DataSource = DataSource1 end object DataSource1: TDataSource Left = 328 Top = 4 end end //Data module... //Unit2.pas unit Unit2; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, ADODB; type TDataModule1 = class(TDataModule) ADOConnection1: TADOConnection; ADODataSet1: TADODataSet; private { Private declarations } public { Public declarations } end; implementation {$R *.DFM} end. //Unit2.DFM object DataModule1: TDataModule1 Left = 272 Top = 162 Height = 480 Width = 696 object ADOConnection1: TADOConnection Left = 40 Top = 20 end object ADODataSet1: TADODataSet Connection = ADOConnection1 Left = 124 Top = 20 end end
Melhorando..
//dpr program Project1; uses Forms, Unit1 in ´Unit1.pas´ , Unit2 in ´Unit2.pas´ {DataModule1: TDataModule}, Unit3 in ´Unit3.pas´; {$R *.RES} var Form1: TForm1; begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. //pas unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, Grids, DBGrids, Unit3; type TForm1 = class(TForm) DataSource1: TDataSource; DBGrid1: TDBGrid; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormShow(Sender: TObject); private { Private declarations } Negocio1: TNegocio1; public { Public declarations } end; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin Negocio1 := TNegocio1.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin Negocio1.Free; end; procedure TForm1.FormShow(Sender: TObject); begin DataSource1.DataSet := Negocio1.Abre; end; end. //dfm object Form1: TForm1 Left = 192 Top = 107 Width = 696 Height = 480 Caption = ´Form1´ Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = ´MS Sans Serif´ Font.Style = [] OldCreateOrder = True OnCreate = FormCreate OnDestroy = FormDestroy OnShow = FormShow PixelsPerInch = 96 TextHeight = 13 object DBGrid1: TDBGrid Left = 4 Top = 4 Width = 320 Height = 120 DataSource = DataSource1 TabOrder = 0 TitleFont.Charset = DEFAULT_CHARSET TitleFont.Color = clWindowText TitleFont.Height = -11 TitleFont.Name = ´MS Sans Serif´ TitleFont.Style = [] end object DataSource1: TDataSource Left = 328 Top = 4 end end //pas unit Unit2; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, DbClient, ADODB, Provider; type TDataModule1 = class(TDataModule) ADOConnection1: TADOConnection; ADODataSet1: TADODataSet; ClientDataSet1: TClientDataSet; DataSetProvider1: TDataSetProvider; private { Private declarations } public { Public declarations } end; implementation {$R *.DFM} end. //dfm object DataModule1: TDataModule1 OldCreateOrder = True Left = 272 Top = 162 Height = 480 Width = 696 object ADOConnection1: TADOConnection Left = 40 Top = 20 end object ADODataSet1: TADODataSet Connection = ADOConnection1 Parameters = <> Left = 124 Top = 20 end object ClientDataSet1: TClientDataSet Aggregates = <> Params = <> ProviderName = ´DataSetProvider1´ Left = 292 Top = 20 end object DataSetProvider1: TDataSetProvider DataSet = ADODataSet1 Constraints = True UpdateMode = upWhereKeyOnly Left = 208 Top = 20 end end //pas unit Unit3; interface uses DbClient, Unit2; type TNegocio1 = class(TObject) private DataModule1: TDataModule1; public constructor Create; destructor Destroy; override; function Abre: TClientDataSet; procedure Fecha; end; implementation function TNegocio1.Abre: TClientDataSet; begin DataModule1.ClientDataSet1.Close; DataModule1.ADODataSet1.CommandText := ´SELECT...´; DataModule1.ClientDataSet1.Open; Result := DataModule1.ClientDataSet1; end; procedure TNegocio1.Fecha; begin DataModule1.ClientDataSet1.Close; end; constructor TNegocio1.Create; begin DataModule1 := TDataModule1.Create(nil); end; destructor TNegocio1.Destroy; begin DataModule1.Free; inherited; end; end.
28/10/2005
Aerreira
Estou testando em apenas um form da aplicação para ver se funciona, que usa apenas dois datasets, em linhas gerais o que mudou com as últimas explicações foi:
No form:
- a declaração do DataModule vai para o uses da Interface
- é criada a variável privada ´dmLocal: Tdm;´ (acho que era isso que estava faltando, lembrando que meu DataModule principal chama-se ´dm´)
- no OnCreate do form, vai a criação da instância local do DM: ´dmLocal := Tdm.Create(nil);´ mais a ligação dos DataSources aos DataSets do dmLocal: ´dsConv.dataset := dmLocal.tbConv;´ e ´dsTab.dataset := dmLocal.tbTab;´
- no OnShow do form, vai a abertura dos datasets ´dmLocal.tbConv.open;´ e ´dmLocal.tbTab.open;´
- no OnClose do form, vai o fechamento dos datasets ´dmLocal.tbConv.close;´ e ´dmLocal.tbTab.close;´
- no OnDestroy do form, libero o dmLocal da memória: ´dmLocal.free;´
No DataModule:
- não muda nada, apenas passarei a usar os DataSets dentro dos forms e não no datamodule.
Funcionou, pois outros forms que abrem os datasets do DM original, continuam abrindo e fechando enquanto o datasets do form de teste continua aberto, ou seja, está sendo usado o dmLocal de forma independente do DM principal.
Não usei as funções que você apresenta em seu segundo exemplo como Abre e Fecha. Falando em segundo exemplo, se eu entendi vc tem uma unit (unit2) para o datamodule e outra unit (unit3) para tratar a outra instância do datamodule e não no próprio form como no seu primeiro exemplo. É isso?
De qualquer modo, vou testar implementando os DM locais em vários forms e ver se funciona bem. Te agradeço pelas dicas.
Efeito colateral: quando a instância do DM é criada o OnCreate do datamodule é executado, e é lá que identifico a localização do banco e abro o IBDataBase... Isso está correto? Provavelmente com isso estou abrindo o banco totalmente independente como se fosse outra aplicação.
Uma questão: se eu tiver muitos forms abertos, cada um com uma instância do DM com vários datasets abertos... isso não vai sobrecarregar muito a memória não?
28/10/2005
Aerreira
- Usar instâncias diferentes do DataModule em cada form da aplicação;
ou
- Abrir os DataSets no próprio DataModule original, controlando pela propriedade TAG quantas vezes o dataset foi aberto, e fechar apenas quando o último form solicitar o fechamento.
As duas soluções estão funcionando, agora resta a dúvida: qual das duas funcionará melhor com a aplicação funcionando, com umas 20 estações de rede num banco com tamanho previsto de aproximadamente uns 200Mb (tamanho baseado na aplicação atual em DOS) ?
A primeira solução dá um isolamento total entre os forms, porém acho que poderá ocupar muita memória, talvez também gerar mais tráfego na rede por abrir e fechar o banco em cada form, e não sei quais poderão ser as conseguencias em máquinas pequenas.
A segunda solução mantém tudo ligado. Num teste que fiz, abri o mesmo form mais de uma vez e a navegação numa tabela ocorre simultaneamente nas duas telas, com a primeira solução isso não aconteceria.
Alguem poderia dar uma opinião para tirar essa minha dúvida?
29/10/2005
Bon Jovi
É por aí, faço assim pra separar totalmente a interface grafica do usuário com as regras de negócio e acesso ao servidor de banco de dados. Colocar o ClientDataSet direto no form também é correto.
[i:2ee3bad4cb]´Efeito colateral: quando a instância do DM é criada o OnCreate do datamodule é executado, e é lá que identifico a localização do banco e abro o IBDataBase... Isso está correto? Provavelmente com isso estou abrindo o banco totalmente independente como se fosse outra aplicação.´[/i:2ee3bad4cb]
Basta vc criar um DataModule só para a conexao, assim abrirá somente uma vez a conexão.
[i:2ee3bad4cb]´Uma questão: se eu tiver muitos forms abertos, cada um com uma instância do DM com vários datasets abertos... isso não vai sobrecarregar muito a memória não?´[/i:2ee3bad4cb]
Depende do ponto de vista, mas eu considero que não, pois estará usando recursos sob demanda, não terá nada aberto a toa e sem malabarismos de tags. E não deixe de usar ClientDataSet, senão realmente estará cutucando muito os servidor. Outra saída tb seria trabalhar com clonagem de ClientDataSet, mas só gosto de usar quando é extremamente necessário.
29/10/2005
Bon Jovi
Isso não importa, pode ser 10 GB, portanto que logicamente vc não traga todos os registros da tabela, e sim o necessário que o usuário quer consultar, geralmente um único registro.
29/10/2005
Aerreira
Isso não importa, pode ser 10 GB, portanto que logicamente vc não traga todos os registros da tabela, e sim o necessário que o usuário quer consultar, geralmente um único registro.[/quote:86d7b7a7c8]
Certo, isso já uso, na medida do possível, por praxe, mas veja uma questão:
Uma tela de atendimento de pacientes, onde tenho os seguintes datasets abertos (uns para atualização outros apenas para consulta): pacientes, convênios, planos, medicos, especialidades, atendimentos, itens do atendimento, serviços, materiais, medicamentos, circulares e parâmetros, considerando todos visualizando apenas um registro. Ainda assim são 12 datasets abertos numa instancia do DM. Em outra tela o usuário abre uma agenda com mais uns 10 datasets, e ao mesmo tempo está gerando um determinado relatório com mais uns 5 datasets em outro DM.
Será que isso fica melhor usando a opção de DM locais ou abrindo tudo num único DM controlando os datasets abertos? O que pesa menos na rede e também em termos de memória nas estações?
Clique aqui para fazer login e interagir na Comunidade :)