で働いています。私は今まで私の解決策についてまだ懐疑的ですが(私は2日目にそれをテストしています、それでもまだ問題はありません)、私は今でもそれを働かせて嬉しいです。
私はそれが私のポストの全体の読みやすさをメリットはありませんが、私はこのテーマについての十分な詳細を提供するために、別の可能性を見ていないと思われる私は、この答えの長さのために謝罪しなければなりません。
他の人が同じようなことに取り組んでいる場合は、解決策として答えを投稿することにしました。私はそれが助けてくれることを願っています。もちろん、私が何かを見逃していたかどうかを知りたいと思っています。
差分が見つかったときにメタデータを更新しようとする関数を記述しました。 ローカルデータセットはXML形式で格納されているので(ローカルに格納されたものはすべて文字列とみなすことができます)、それらをバリアントとして扱うことができます。データを追加するときに実際に大きな利点となるのは、次のとおりです。
procedure TdmDatabase.UpdateMetaDataFor(cds : TCustomClientDataSet; folder : String);
次に、ネストされたプロシージャと関数が続きます。私はまだこのアプローチを利用についてあまりにもわからないので、これは、後から変更される場合があります...フィールドを追加
procedure AddInLocalData(local, newCds : TCustomClientDataSet);
var i : Integer;
begin
try
(* Assume that the new dataset is still closed... *)
newCds.CreateDataSet;
newCds.Insert;
for i := 0 to Pred(local.Fields.Count) do
begin
if (i < newCds.FieldCount) then
newCds.Fields[i].AsVariant := local.Fields[i].AsVariant;
end;
newCds.Post;
newCds.SaveToFile(folder + newCds.TableName + '_updated.xml', dfXMLUTF8);
except on E: Exception do
raise Exception.Create(_Translate(RS_ERROR_UNABLE_TO_SYNC_LOCAL_AND_REMOTE));
end;
end;
には制約がない場合は特に、簡単な部分です。追加のフィールド はリモートデータセット(実際にはデータベース自体から来ている)から見つけられ、データはそこでは重要ではありません。したがって、そこに挿入しなければならないデータを邪魔することなく、新しいフィールドを挿入することができます。そうであれば、この関数は必ず更新する必要があります(このプロジェクトでは必要ありません)。この関数は確実に更新する必要があります:フィールドの削除は、より具体的です。 1つは、削除が必要なフィールドに制約があるかどうかを確認する必要があります。その場合は、メソッドを続行しないでください。適切な機能を確保するために、すべてのテーブルを含むローカルデータセット全体をデータベースから削除して再構築する必要があります。現在、これらの変更は大きな変更と考えられています。私は大規模な変更が適用されているかどうかをチェックし、そうであればアプリケーションの新しいバージョンが必要になる可能性が最も高いでしょう。
function RemoveFieldsLocally(remote, local, newCds : TCustomClientDataSet) : TCustomClientDataSet;
var i : Integer;
fieldDef : TFieldDef;
field : TField;
begin
try
(* Remote provider has lead here! *)
newCds.SetProvider(remote);
newCds.FieldDefs.Update;
(* Find the already existing fields and add them *)
for i := 0 to Pred(newCds.FieldDefs.Count) do
begin
field := newCds.FieldDefs[i].CreateField(cds);
if assigned(field) then
begin
field.FieldName := local.Fields[i].FieldName;
field.Calculated := local.Fields[i].Calculated;
field.Required := local.Fields[i].Required;
(* Necessary for compatibility with for example StringFields, BlobFields, etc *)
if (HasProperty(field, 'Size')) then
Field.Size := local.FIelds[i].Size;
end;
end;
(* Now add in the existing data from the local dataset.
Warning: since fields have been removed in the remote dataset, these
will not be added as well. If constraints were put up, these become
lost *)
AddInLocalData(local, newCds);
result := newCds;
except on E:Exception do
raise E;
end;
end;
以下の関数は、フィールド間の等しいかどうかをチェックします。 DataType(FieldType)、FieldNameなどの点で差異が検出された場合、リモートデータセットのメタデータに従って更新を試みます。
function VerifyInternalStructuresAndFields(remote, local, newCds : TCustomClientDataSet) : boolean;
var i, equalityCounter : Integer;
equal : boolean;
begin
try
(* We know that both datasets (local and remote) are equal for when it comes to
the fieldcount. In this case, the structure of the dataset from the remote dataset is leading. *)
newCds.SetProvider(remote);
newCds.FieldDefs.Update;
equal := false;
equalityCounter := 0;
for i := 0 to Pred(newCds.FieldDefs.Count) do
begin
(* 1. Fielddefinitions which are exactly equal, can be copied *)
equal := (remote.Fields[i].FieldName = local.Fields[i].FieldName) and
(remote.Fields[i].Required = local.Fields[i].Required) and
(remote.Fields[i].Calculated = local.Fields[i].Calculated) and
(remote.Fields[i].DataType = local.Fields[i].DataType) and
(remote.Fields[i].Size = local.Fields[i].Size);
if (equal) then
begin
inc(equalityCounter);
with newCds.FieldDefs[i].CreateField(cds) do
begin
FieldName := local.Fields[i].FieldName;
Calculated := local.Fields[i].Calculated;
Required := local.Fields[i].Required;
Size := local.FIelds[i].Size;
end;
end
else (* fields differ, try to update it, here the remote fields are leading! *)
begin
if (MessageDlg(_Translate(RS_WARNING_DIFFERENCES_IN_FIELDS), mtWarning, mbYesNo, 0) = IDYES) then
begin
with newCds.FieldDefs[i].CreateField(cds) do
begin
FieldName := remote.Fields[i].FieldName;
Calculated := remote.Fields[i].Calculated;
Required := remote.Fields[i].Required;
if (HasProperty(remote, 'Size')) then
Size := remote.Fields[i].Size;
SetFieldType(remote.Fields[i].DataType); //TODO: If this turns out to be unnecessary, remove it.
end;
end
else
begin
result := false;
exit;
end;
end;
end;
if (equalityCounter = local.FieldCount) then
begin
result := false;
end else
begin
AddInLocalData(local, newCds);
result := true;
end;
except on E:Exception do
raise E;
end;
end;
これは、フィールドとリモートとローカルのデータセットのフィールド 定義間の差異を検出しようとする主な機能です。
function FindDifferencesInFields(remote, local: TCustomClientDataSet) : TCustomClientDataSet;
var i, k : Integer;
fieldDef : TFieldDef;
newCds : TKLAClientDataSet;
begin
try
newCds := TCustomClientDataSet.Create(nil);
newCds.FileName := local.FileName;
newCds.Name := local.Name;
newCds.TableName := local.TableName;
(* First check if the remote dataset has added fields. *)
if (remote.FieldDefs.Count > local.FieldDefs.Count) then
begin
result := AddFieldsLocally(remote, local, newCds);
end
(* If no added fields could be found, check for removed fields *)
else if (remote.FieldDefs.Count < local.FieldDefs.Count) then
begin
result := RemoveFieldsLocally(remote, local, newCds);
end
(* Finally, check if the fieldcounts are equal and if renames have taken place *)
else if (remote.FieldDefs.Count = local.FieldDefs.Count) then
begin
if (VerifyInternalStructuresAndFields(remote, local, newCds)) then
result := newCds
else result := local;
end;
except on E:Exception do
raise Exception.Create('Could not verify remote and local dataset: ' + E.Message);
end;
end;
上記のすべてに使用する機能や手順は非常に重要なので、私は彼らがUpdateMetaDataForと呼ばれるメインプロシージャ内でネストすることにしました。私はこれを後で変えるかもしれませんが、今は十分です。
var fieldDefs : TFieldDefs;
remotecds : TCustomClientDataSet;
constraints : TCheckConstraints;
fileName : String;
k : integer;
begin
try
try
ConnectDB(false);
fileName := folder + cds.TableName + '_metadata_update.xml';
(* Retrieve the latest metadata if applicable *)
remotecds := CreateDataset;
remotecds.SQLConnection := SQLConnection;
remotecds.TableName := cds.TableName;
remotecds.SQL.Text := Format('SELECT * from %s where id=-1', [cds.TableName]);
remotecds.Open;
remotecds.SaveToFile(fileName , dfXMLUTF8);
(* Load the local dataset with data for comparison *)
cds.LoadFromFile(folder + cds.FileName);
SyncProgress(_Translate(RS_SYNC_INTEGRITY_CHECK) + ' ' + cds.TableName);
cds := FindDifferencesInFields(remotecds, cds);
cds.SaveToFile(folder + cds.FileName, dfXMLUTF8);
except on E: Exception do
ShowMessage(E.Message);
end;
finally
if assigned(remotecds) then
remotecds.Free;
if FileExists(fileName) then
SysUtils.DeleteFile(fileName);
end;
end;
これで私の非常に包括的な答えが終わりましたが、まだいくつか検討の余地があります。たとえば、制約を使用して何を行う必要があるか(これらはローカルデータセットでは直接表示されません)。
もう1つの方法として、データベースにテーブルを追加する方法があります。これには、バージョン番号に応じてすべての変更が含まれます。これらの変更が軽微(制約のないフィールドまたは名前が変更されたフィールドの名前変更)であると考えられる場合、この半自動アプローチを引き続き使用できます。
いつものように、私はデータベースのブリーフケースモデルを適用する際に、データベースのエンテリティを保証するための他のアプローチについてはまだ非常に興味があります。
データベース構造の新しいメタデータでのみ、列の名前が変更されたか古い列が削除され、新しい列が追加されたかを判断できません。 –
私は同意します。その場合は、両方のデータセットのフィールド定義を繰り返して差分を探します。これはまだ効率的であるかどうか疑問に思っています。 – RvdV79
ローカルデータベースは読み取り専用ですか? –