Data Session'i senin tablolarinin, cursorlerinin acildigi calisma masasi gibi dusun. Ikimizde ayni sirkette, ayni odada calisiyoruz (oda VFP'nin kendisi olsun).
Ikimizin de calisma masalari var. Benim masama actigim evraklar ve senin kendi masana actigin evraklar var. Tesadufen ilimiz de, evrak arsivinden aldigimiz, musteriler dosyasi ile calisiyoruz (gercek hayatta ya sen ya ben aliriz burada birer kopyesini aldik diyelim). Benim actigim sayfa X, seninki Y. Benim Z'ye gecmem seni etkilemiyor, sen hala Y ile calisiyorsun. Ben Z'yi degistirsem ve kaydetsem (tableupdate, commit ya da hic buffer yok) senin sihirli evrak yonetimi sayesinde, Z'ye bakarsan o degisiklik orada. Sen tum dosyalarini kapatsan ve toplayip evrak arsivine teslim etsen de ben calismaya devam edebiliyorum. Uzun lafin kisasi masalarimiz bagimsiz. Bu private data session gibi.
Default data session arsivin kendisi gibi ya da masalarimizdan birisi (VFP acildiginda aktif olan data session 1, global data session, o her zaman var). Eger ikimiz de orada calissaydik (biz ornegin Form'uz) illa ki ayni anda X'e, Y'e. Z'e vs bakmamiz gerekecekti. Ben kapatirsam butun dosyalari senin icin de kapanmis olacakti (senin haberin olmadan kapttigimi dusun Y'nin adi ne diye bakmaya kaltiginda veya Z'ye sunu yaz dedigin anda ortada dosya yok yazacak).
Default ile Global'i karistirma. Global data session daima datasessionId = 1 olan VFP acildigindaki session. Default biraz farkli ondan, soyle ki eger ben private datasessionda isem ve onun IDsi 4 ise, burada acilan default data session ayni session'i yani 4'u kullanir. Biraz karisik oldu galiba:
*Form calisiyor - private datasession. DataSessionID = 4
-Bir buton ile modal form aciyorum (ya da nonModal)
-Acilan yeni form private degil default kullaniyor. O zaman, acildigi session onun session'i yani 4 Id'li. Dataenvironment'inda AutoCloseTables var ve benim onceki formda kullandigim tablolardan birisi o dataenvironmentda mevcut ise, donuste onu kapatir ve benim gariban formumun bundan haberi yoksa vay haline. Ya da daha vahimi o cagirdigim form donuste "close tables all" derse cagiran form yandi.
Bir baska benzetme ile, private session ayni VFP icinde calisan bircok bagimsiz data kutucuklari gibi (data slots). Ama ne kutucuk, her birinde 32767 dosya acabiliyorsun (toplamda 65535 file handle'i gecemedigini varsayarsak), istediginde istedigin gibi dolas, diger kutucuklarda acilanlar ile ilgin yok.
Tek bir makine, tek bir VFP uygulamasinin icinde multiuser gibi (nerdeyse).
Private Datasession benim bakis acimdan foxpro2x'ten sonra VFP ile gelen bir nimet:)
Data sessionlar arasinda bir bariyer var, bunu bilmen gerekiyor. Bir session'in tablosuna baska bir sessiondan dokunamazsin ancak tablo diskte olduguna gore sende acar istedigini yaparsin. Cursor ise diskte karsiligi olasa da olmasa da tamamen yaratana ozel olduguna sessionlar arasinda hicbir sekilde gecemez. Simdi yanlis anlamalari onlemek ve kendimi yalanci durumuna durumemek icin biraz ornekleyeyim (tabii bu arada ihtiyacin olursa ne yapabilirsin) - tum formlarda private session oldugunu varsayalim:
-Form1
Visual Fox Pro
use customer
locate for cust_id = "ANATR"
select * from customer where country = 'USA' into cursor crsWork nofilter
-Form2
* Form1'de hangi customer secili bilmek istiyor. oForm, form1'in referansi olsun
? customer.cust_id && form1 session'a ulasmiyor. 2 sonuc olabilir.
* 1) Burada customer acilmamissa alias not found - ozel bir durum var hata almayacagin ama konu disi
* 2) Burada customer aciksa, kendisinde o sirada hangi kayitta ise o. Herkesin kendi masasi:)
Form1'inkini almakta israr ediyor. Alamaz mi? Alir. DataSession'larin IDsi var.
set datasession to oForm1.DataSessionId
lcID = customer.cust_id && sadece PKyi sordu
set datasession to thisform.dataSessionID
Gecisi olarak datasession degisitirip o hangisinde bakti, dondu.
Simdi gelelim form1 deki crsWork'e. Ona datasessionId degisitirip bakabilir, browse edebilir vs. Ancak butun bu islemleri bariyaerin karsi tarafinda yapiyor olur. O Country='USA' olanlari secti, ben o settekilerin ordelarini almak istiyorum:
set datasession to oForm1.DataSessionId
select * from Orders where cust_ID in (select cust_id from crsWork) into cursor crsSiparis nofilter
browse && vs gecerli - ancak crsSiparis artik bariyerin o tarafinda, o form icin var
set datasession to thisform.dataSessionID
select crsSiparis && ooops. crsSiparis yok burada
* Ya da bu gecersiz - bu session crsWork'ten habersiz
select * from Orders where cust_ID in (select cust_id from crsWork) into cursor crsSiparis nofilter
Ne yazik ki, bu session icinde ayni islemi yapmasi lazim burada da istiyorsa onlari:
select * from customer where country = 'USA' into cursor crsWork nofilter
select * from Orders where cust_ID in (select cust_id from crsWork) into cursor crsSiparis nofilter
gibi. Sinirlar cok iyi korunuyor yani:) Datasession bariyerlerini "hic bir cursor gecemez". Peki cursorlar gecemezse ve:
select * from customer where country = 'USA' into cursor crsWork nofilter
milyonlarca kayittan yapilmis ya da SQL server vs'den gelmis, elde etmesi pahali ve zaman alan bir islem ise ne olacak (sonuc ise topu topu birkac yuz veya birkac bin record olsun)? Bircok formda da ihtiyacin var. Cevabi tartismali. Default datasessionda yap, onu kullanacaklar default datasession kullansin diyenler var, ona katilmayanlar da (ben katilmiyorum). Onun yerine bu bariyerleri nasil geceriz:
-Cursor gecemez. Ama tablolar diskte, session problemi yok.
select * from customer where country = 'USA' into table crsWork nofilter
use && kapatilana kadar exclusive kullaniyor
olabilirdi. O zaman butun sessionlar buna ulasir (use, select...). Bunun yaratacagi problem ortada runtime'da ayni anda birden fazla yer bu tabloyu yaratmaya kalkar vs.
-Viewler cozum degil. Cunku view SQL'in tanimi iceriyor. Select yapmakla ayni sey zaman alir.
-O bariyerleden sadece cursorlar gecemiyor, degiskenler gecer:) O zaman o tablonun icerigini bir degiskene alabiliriz:
Visual Fox Pro
select * from customer where country = 'USA' into table crsWork nofilter
loData = createobject('Empty') && vfp3,5,... ise custom,line filan ile oluyor. kod biraz farkli
addproperty(loData, "RecordCount", reccount('crsWork') )
addproperty(loData, "aRecord[' + transform( reccount('crsWork') ) + ']')
scan
scatter name loData.aRecord[recno()] memo
endscan
Tepe tepe kullanilmaya, datasessionlar arasinda dolasmaya hazir bir objemiz var:) Yukaridaki yaklasimi daha iyi gosteren hazir bir classim var. Onunla ornek gostereyim ve bitsin (private sessionli formlari taklit etmek icin private session kullanan 'session' objeleri var. Olayin nasil dondugunu anlamak icin bunu denerken Window\Data session ac. 3 farkli yerde farkli tablolar var - default(1)'de acilan hcbirsey yok - DataSessionPass.prg adiyla saklayip denemezsen biriki yerde ufak degisikilik yapman gerekli, NewObject()lere bak):
Visual Fox Pro
* DatasessionPass.prg
Local loSession1, loSession2
* Create 1st private datasession
loSession1 = Createobject('Session1')
loSession1.SomeQueries() && run some queries
loSession2 = Createobject('Session2')
loSession2.GetTheDataAndBrowse(loSession1) && transfer data from session1
Define Class session1 As Session
DataSession = 2 && Private
Procedure SomeQueries
Select * From (_samples+'data\customer') Where country = 'USA' Into Cursor crsUSA
Select ord.* From (_samples+'data\orders') ord ;
INNER Join crsUSA On crsUSA.cust_id == ord.cust_id ;
ORDER By ord.cust_id, ord.order_date ;
INTO Cursor crsUSAOrders ;
nofilter
Endproc
Procedure GetDataCursors( tcAliasList, tcAsAliasList )
If Empty(m.tcAsAliasList)
tcAsAliasList = m.tcAliasList
Endif
Local laAlias[1], laAsAlias[1]
Local oDataSetPacker As 'DataSet' Of Fullpath('DatasessionPass.prg')
Local ix, lcAlias, lcAsAlias
oDataSetPacker = Newobject('DataSet',Fullpath('DatasessionPass.prg'))
Alines(laAsAlias, m.tcAsAliasList,1,',')
For ix = 1 To Alines(laAlias, m.tcAliasList,1,',')
lcAlias = laAlias[m.ix]
lcAsAlias = Iif(m.ix > Alen( laAsAlias ), laAlias[m.ix], laAsAlias[m.ix] )
oDataSetPacker.AddTable(m.lcAlias, m.lcAsAlias)
Endfor
Return oDataSetPacker
Endproc
Enddefine
Define Class session2 As Session
DataSession = 2 && Private
Procedure GetTheDataAndBrowse(toOtherSession)
Local loDataSet
loDataSet = toOtherSession.GetDataCursors( ;
'crsUSA, crsUSAOrders', 'crsCustomer,crsOrders' )
loDataSet.RestoreDataset(This.DataSessionId)
Select crsCustomer
Browse
Select crsOrders
Browse
Endproc
Enddefine
** Dataset class to pack given cursors into an object to pass between data sessions
Define Class DataSet As Custom
TableCount = 0
DataSessionId = 0
Dimension TableInfo[1]
Procedure Init
This.DataSessionId = Set("Datasession")
Endproc
Procedure AddTable(tcAlias, tcAsAlias)
Local Array aStruc[1],aStripped[1]
Afields(aStruc,m.tcAlias)
Dimension aStripped[ALEN(aStruc,1),5]
For ix = 1 To Alen(aStruc,1)
aStripped[m.ix,1] = aStruc[m.ix,1]
aStripped[m.ix,2] = aStruc[m.ix,2]
aStripped[m.ix,3] = aStruc[m.ix,3]
aStripped[m.ix,4] = aStruc[m.ix,4]
aStripped[m.ix,5] = aStruc[m.ix,5]
Endfor
This.TableCount = This.TableCount + 1
Dimension This.TableInfo[this.TableCount]
This.TableInfo[this.TableCount] = Createobject('Custom')
With This.TableInfo[this.TableCount]
.AddProperty('aStructure[1]')
.AddProperty('aRecords[1]')
.AddProperty('Records')
.AddProperty('Alias',m.tcAlias)
.AddProperty('AsAlias', Iif(Empty(m.tcAsAlias),m.tcAlias,m.tcAsAlias) )
Acopy(aStripped, .aStructure)
Select * From (m.tcAlias) Where !Deleted() Into Cursor __crsTemp__ nofilter
.RecordS = Reccount()
If .RecordS > 0
Dimension .aRecords[RECCOUNT()]
Scan
Scatter Name .aRecords[RECNO()] Memo
Endscan
Endif
Use In '__crsTemp__'
Endwith
Endproc
Procedure RestoreDataSet(tnDataSessionID) && restore all with saved aliases
For ix = 1 To This.TableCount
This.Obj2Cursor( This.TableInfo[m.ix].Alias, '', m.tnDataSessionID )
Endfor
Endproc
Procedure Obj2Cursor( tcStoredAlias, tcAlias, tnDataSessionID )
Local ix,jx
If !Empty(m.tnDataSessionID)
Set DataSession To m.tnDataSessionID
Endif
For ix = 1 To This.TableCount
With This.TableInfo[m.ix]
If Upper(.Alias) == Upper(m.tcStoredAlias)
tcAlias = Iif(Empty(m.tcAlias), .AsAlias, m.tcAlias )
Create Cursor (m.tcAlias) From Array .aStructure
For jx = 1 To .RecordS
Append Blank
Gather Name .aRecordS[m.jx] Memo
Endfor
Exit
Endif
Endwith
Endfor
Set DataSession To This.DataSessionId
Endproc
Enddefine