Manter Dataset aberto condicionalmente

26/10/2005

14

Pessoal,

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

Aerreira

Responder

Posts

26/10/2005

Bon Jovi

O ideal que acho é que cada form deve usar uma instância diferente deste data module. Ou seja, tirar do auto create e criar/destruir em cada form o data module. Nao esquecendo também de apagar a variavel global de unit que o Delphi escreve de forma nao OO (var DataModule1: TDataModule1;). Cada classe de form deverá ter uma variavel privada para a instância do data module.

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.


Responder

26/10/2005

Bon Jovi

[i:9816be9088]Cada classe de form deverá ter uma variavel privada para a instância do data module. [/i:9816be9088]
Complementando: com isso terá que setar o Connection e demais objetos em tempo de execução se quiser chamar de outra classe.


Responder

27/10/2005

Anfm

Eu costumo criar uma cópia do datamodule para cada form.

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;


Responder

27/10/2005

Aerreira

DMLocal := TDataModule.Create(Self) DataSource1.DataSet := DMLocal.NomeTabela;


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.


Responder

27/10/2005

Aerreira

[quote:e8554fd7f0=´Bon Jovi´]Nao esquecendo também de apagar a variavel global de unit que o Delphi escreve de forma nao OO (var DataModule1: TDataModule1;).[/quote:e8554fd7f0]
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 ?


Responder

27/10/2005

Anfm

Então,

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.


Responder

27/10/2005

Aerreira

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.


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.


Responder

27/10/2005

Firekiller

Você poderia trabalhar com a sua idéia inicial de trabalhar com as tags... mas a cada form que abrisse (e que utilizasse o dataset), seria adicionado 1 na tag do dataset. Ao fechar o form, tira-se 1 da tag do dataset. Se o dataset estiver com a tag = 0 então, ele deve ser fechado, senão não. É uma forma mais simples de implementar o que já estava pronto.

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.


Responder

27/10/2005

Aerreira

Você poderia trabalhar com a sua idéia inicial de trabalhar com as tags... mas a cada form que abrisse (e que utilizasse o dataset), seria adicionado 1 na tag do dataset. Ao fechar o form, tira-se 1 da tag do dataset. Se o dataset estiver com a tag = 0 então, ele deve ser fechado, senão não. É uma forma mais simples de implementar o que já estava pronto.


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 ?


Responder

27/10/2005

Bon Jovi

O modo mais simples:
//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.



Responder

28/10/2005

Aerreira

Bom dessa vez funcionou com as explicações do BonJovi.

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?


Responder

28/10/2005

Aerreira

Bom, quanto ao tópico fiquei com duas opções:

- 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?


Responder

29/10/2005

Bon Jovi

[i:2ee3bad4cb]´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?´[/i:2ee3bad4cb]
É 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.


Responder

29/10/2005

Bon Jovi

[i:293aefaf11]´um banco com tamanho previsto de aproximadamente uns 200Mb´[/i:293aefaf11]
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.


Responder

29/10/2005

Aerreira

[quote:86d7b7a7c8=´Bon Jovi´][i:86d7b7a7c8]´um banco com tamanho previsto de aproximadamente uns 200Mb´[/i:86d7b7a7c8]
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?


Responder

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários,
consulte nossa política de privacidade.

Aceitar