RSS


[ Pobierz całość w formacie PDF ]
., Alameda, CA www.sybex.com 2874c17.qxd 7/2/01 4:39 PM Page 760760 Chapter 17 " Multitier Database Applications with DataSnapwhile i =InternalRecourdCount.To fully understand the various cases,you might want to read the code a couple of times.It took me some trial and error (and system crashes caused by recursive calls) to get itstraight when I wrote my first custom dataset a few years back.To test it, consider that if youuse a DBGrid, the system will perform a series of GetRecord calls, until either the grid is fullor GetRecord return grEOF.Here s the entire code of the GetRecord method:// III: Retrieve data for current, previous, or next record// (moving to it if necessary) and return the statusfunction TMdCustomDataSet.GetRecord(Buffer: PChar;GetMode: TGetMode; DoCheck: Boolean): TGetResult;beginResult := grOK; // defaultcase GetMode ofgmNext: // move onif FCurrentRecord 0 thenDec (FCurrentRecord)elseResult := grBOF; // begin of filegmCurrent: // check if emptyif FCurrentRecord >= InternalRecordCount thenResult := grError;end;// load the dataif Result = grOK thenInternalLoadCurrentRecord (Buffer)else if (Result = grError) and DoCheck thenraise EMdDataSetError.Create ( GetRecord: Invalid record );end;Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 2874c18.qxd 7/2/01 4:40 PM Page 804804 Chapter 18 " Writing Database ComponentsIf there s an error and the DoCheck parameter was True, GetRecord raises an exception.Ifeverything goes fine during record selection, the component loads the data from the stream,moving to the position of the current record (given by the record size multiplied by therecord number).In addition, we need to initialize the buffer with the proper bookmark flagand bookmark (or record number) value.This is accomplished by another virtual method Iintroduced, so that derived classes will only need to implement this portion of the code,while the complex GetRecord method remains unchanged:procedure TMdDataSetStream.InternalLoadCurrentRecord (Buffer: PChar);beginFStream.Position := FDataFileHeaderSize + FRecordSize * FCurrentRecord;FStream.ReadBuffer (Buffer^, FRecordSize);with PMdRecInfo(Buffer + FRecordSize)^ dobeginBookmarkFlag := bfCurrent;Bookmark := FCurrentRecord;end;end;We move data to the file in two different cases: when you modify the current record (thatis, a post after an edit) or when you add a new record (a post after an insert or append).Weuse the InternalPost method in both cases, but we can check the dataset s State property todetermine which type of post we re performing.In both cases we don t receive a recordbuffer as a parameter, so we must use the ActiveRecord property of TDataSet, which pointsto the buffer for the current record:procedure TMdDataSetStream.InternalPost;beginCheckActive;if State = dsEdit thenbegin// replace data with new dataFStream.Position := FDataFileHeaderSize + FRecordSize * FCurrentRecord;FStream.WriteBuffer (ActiveBuffer^, FRecordSize);endelsebegin// always appendInternalLast;FStream.Seek (0, soFromEnd);FStream.WriteBuffer (ActiveBuffer^, FRecordSize);Inc (FRecordCount);end;end;Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 2874c18.qxd 7/2/01 4:40 PM Page 805Building Custom Datasets 805In addition, there s another related method, InternalAddRecord.This method is called bythe AddRecord method, which in turn is called by InsertRecord and AppendRecord.These lasttwo are public methods a user can call.This is an alternative to inserting or appending a newrecord to the dataset, editing the values of the various fields, and then posting the data, sincethe InsertRecord and AppendRecord calls receive the values of the fields as parameters.All wemust do at that point is replicate the code used to add a new record in the InternalPostmethod:procedure TMdDataSetOne.InternalAddRecord(Buffer: Pointer; Append: Boolean);begin// always append at the endInternalLast;FStream.Seek (0, soFromEnd);FStream.WriteBuffer (ActiveBuffer^, FRecordSize);Inc (FRecordCount);end;The last file operation I should have implemented is one that removes the current record.This operation is common, but it is actually quite complex.If we take a simple approach,such as creating an empty spot in the file, then we ll need to keep track of that spot and makethe code for reading or writing a specific record work around that spot.An alternate solutionis to make a copy of the entire file, without the given record, and then replace the originalfile with the copy.Given these choices, I felt that for this example I could forgo supportingrecord deletion.Section IV: From Buffers to FieldsIn the last few methods, we ve seen how datasets move data from the data file to the memorybuffer.However, there s little Delphi can do with this record buffer, because it doesn t yet knowhow to interpret the data in the buffer.We need to provide two more methods: GetData, whichcopies the data from the record buffer to the field objects of the dataset, and SetData, whichmoves the data back from the fields to the record buffer.What Delphi will do automatically forus is move the data from the field objects to the data-aware controls, and back.The code for these two methods isn t very complex, primarily because we saved the fieldoffsets inside the record data in a TList object called FFieldOffset.By simply incrementingthe pointer to the initial position in the record buffer of the current field s offset, we ll beable to get the specific data, which takes Field.DataSize bytes.A confusing element of these two methods is that they both accept a Field parameter and aBuffer parameter.At first, one might think that the buffer passed as parameter is the recordbuffer.Actually, I found out that the Buffer is a pointer to the field object s raw data.If youuse one of the field object s methods to move that data, it will call the dataset s GetData orCopyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 2874c18.qxd 7/2/01 4:40 PM Page 806806 Chapter 18 " Writing Database ComponentsSetData methods, probably causing an infinite recursion.Instead, you should use the Active-Buffer pointer to access the record buffer, use the proper offset to get to the data for the cur-rent field in the record buffer, and then use the provided Bufferto access the field data.Theonly difference between the two methods is the direction we re moving the data:function TMdDataSetOne.GetFieldData (Field: TField; Buffer: Pointer): Boolean;varFieldOffset: Integer;Ptr: PChar;beginResult := False;if not IsEmpty and (Field.FieldNo > 0) thenbeginFieldOffset := Integer (FFieldOffset [Field.FieldNo - 1]);Ptr := ActiveBuffer;Inc (Ptr, FieldOffset);if Assigned (Buffer) thenMove (Ptr^, Buffer^, Field.DataSize);Result := True;if (Field is TDateTimeField) and (PInteger(Ptr)^ = 0) thenResult := False;end;end;procedure TMdDataSetOne.SetFieldData(Field: TField; Buffer: Pointer);varFieldOffset: Integer;Ptr: PChar;beginif Field.FieldNo >= 0 thenbeginFieldOffset := Integer (FFieldOffset [Field [ Pobierz caÅ‚ość w formacie PDF ]
  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • wblaskucienia.xlx.pl