Есть ли простой способ сравнить строки подключения, не разбирая его сам?
Мне нужно уметь сравнивать две разные строки соединения и определять, являются ли они одной и той же информацией. Я не могу сделать простое сравнение строк, потому что свойства могут быть размечены по-разному, но все же представляют одно и то же соединение.
Прежде чем я пойду и напишу свое собственное сравнение для этого, уже есть что-то, что может сделать это?
Я искал способ, и я ничего не могу найти об этом.
2 ответа
Вы можете использовать IDataInitialize::GetDataSource
метод, который возвращает неинициализированный объект источника данных из заданной строки подключения. Из-за этого метода возвращает указатель на объект источника данных IUnknown
типа, вы не можете напрямую сравнивать объекты, полученные для двух сравниваемых строк соединения. Тем не мение, you can query
IDBProperties
интерфейс на этих неинициализированных объектах источника данных, что дает вам возможность получить доступ ко всем свойствам, поддерживаемым данным провайдером.
Чтобы получить набор свойств, вам нужно использовать IDBProperties::GetProperties
метод. Это вернет DBPROPSET
структура, которая содержит массив DBPROP
элементы (свойства). Затем вы просто итерируете этот массив и сравните свойства этих двух объектов источника данных так, как вам нужно.
Следующие IsSameConnStr
функция возвращает True, если строки подключения равны, False в противном случае. Обратите внимание, что сравнение значений используемых свойств не чувствительно к регистру, кроме DBPROP_AUTH_PASSWORD
свойство, которое сравнивается с чувствительностью к регистру:
uses
ActiveX, ComObj, OleDB;
function IsSameVarWideStr(const AValue1, AValue2: OleVariant;
ACaseSensitive: Boolean = False): Boolean;
begin
Result := VarType(AValue1) = VarType(AValue2);
if Result then
begin
if ACaseSensitive then
Result := WideCompareStr(VarToWideStr(AValue1),
VarToWideStr(AValue2)) = 0
else
Result := WideCompareText(VarToWideStr(AValue1),
VarToWideStr(AValue2)) = 0;
end;
end;
function IsSameConnStr(const AConnStr1, AConnStr2: WideString): Boolean;
var
I: Integer;
DataSrc1: IUnknown;
DataSrc2: IUnknown;
DataInit: IDataInitialize;
PropSet1: PDBPropSet;
PropSet2: PDBPropSet;
PropSetCnt1: ULONG;
PropSetCnt2: ULONG;
Properties1: IDBProperties;
Properties2: IDBProperties;
const
DBPROP_AUTH_PASSWORD = $00000009;
begin
// first check if the input connection strings aren't exactly the same
Result := CompareStr(AConnStr1, AConnStr2) = 0;
// if they are not same, then...
if not Result then
begin
// create IDataInitialize object instance
OleCheck(CoCreateInstance(CLSID_DataLinks, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IID_IDataInitialize, DataInit));
// get data source objects for both input connection strings
OleCheck(DataInit.GetDataSource(nil, CLSCTX_INPROC_SERVER,
PWideChar(AConnStr1), IUnknown, DataSrc1));
OleCheck(DataInit.GetDataSource(nil, CLSCTX_INPROC_SERVER,
PWideChar(AConnStr2), IUnknown, DataSrc2));
// query for IDBProperties objects of the data source objects
if Succeeded(DataSrc1.QueryInterface(IID_IDBProperties, Properties1)) and
Succeeded(DataSrc2.QueryInterface(IID_IDBProperties, Properties2)) then
begin
// get properties of data source objects
OleCheck(Properties1.GetProperties(0, nil, PropSetCnt1, PropSet1));
OleCheck(Properties2.GetProperties(0, nil, PropSetCnt2, PropSet2));
try
// same DB provider will have the same set of initialization properties,
// so the first check might be the property count, if that differs, then
// at least DB provider is different, so if this equals, then...
if PropSetCnt1 = PropSetCnt2 then
begin
// initialize positive result
Result := True;
// iterate all the properties
for I := 0 to PropSet1.cProperties - 1 do
begin
// check if we're comparing the same property and if so, compare the
// property values; for password property compare the value with case
// sensitivity, for all the others case insensitively; if any of this
// doesn't match, we're done with False result and we can exit
if (PropSet1.rgProperties[I].dwPropertyID <>
PropSet2.rgProperties[I].dwPropertyID) or
not IsSameVarWideStr(PropSet1.rgProperties[I].vValue,
PropSet2.rgProperties[I].vValue,
PropSet1.rgProperties[I].dwPropertyID = DBPROP_AUTH_PASSWORD) then
begin
Result := False;
Break;
end;
end;
end;
finally
// release the property sets; note that you should avoid this common
// try..finally block and that you should free also each property array
// element by using IMalloc::Free; why I've used CoTaskMemFree see this
// question http://stackru.com/q/3079508/960757
CoTaskMemFree(PropSet1);
CoTaskMemFree(PropSet2);
end;
end;
end;
end;
Я думаю, что использование понятно, поэтому я скорее упомяну результаты для некоторых строк подключения:
IsSameConnStr = True
AConnStr1: Provider=MSDASQL.1;Persist Security Info=True;Data Source=datasource
AConnStr2: Provider=MSDASQL.1;Persist Security Info=True;Data Source=DATASOURCE
IsSameConnStr = True
AConnStr1: Provider=MSDASQL.1;Data Source=datasource;Persist Security Info=True
AConnStr2: Provider=MSDASQL.1;Persist Security Info=True;Data Source=DATASOURCE
IsSameConnStr = True
AConnStr1: Provider=MSDASQL.1;Password=PASSWORD;Data Source=datasource;Persist Security Info=True
AConnStr2: Provider=MSDASQL.1;Data Source=DATASOURCE;Password=PASSWORD;Persist Security Info=True
IsSameConnStr = False - password differs in case sensitivity
AConnStr1: Provider=MSDASQL.1;Password=PASSWORd;Data Source=datasource;Persist Security Info=True
AConnStr2: Provider=MSDASQL.1;Data Source=DATASOURCE;Password=PASSWORD;Persist Security Info=True
Чтобы получить коллекцию ConnectionString
свойства, вы можете назначить ConnectionString
в TADOConnection
(без фактического подключения к БД) и использовать TADOConnection.Properties
коллекция (элемент коллекции ADOInt.Property_
) например:
ADOConnection.Properties.Get_Item('Data Source')
Вероятно, вам следует сравнить конкретные свойства, чтобы определить, установлено ли соединение для определенного хранилища данных через определенного поставщика. например:Provider
, Data Source
, Initial Catalog
, User ID
\ Password
(необязательный).
Есть много свойств, которые вы можете игнорировать в зависимости от поставщика, например:Workstation ID
, Persist Security Info
, Use Procedure for Prepare
, Auto Translate
, так далее...
Вот пример того, как перебрать TADOConnection
коллекция свойств:
var
ADOConnection: TADOConnection;
PropName, PropValue: WideString;
I: Integer;
ADOConnection := TADOConnection.Create(nil);
try
ADOConnection.ConnectionString := 'Provider=MSDASQL.1;Password=secret;Data Source=127.0.0.1;User ID=user;Initial Catalog=mycatalog';
for I := 0 to ADOConnection.Properties.Count - 1 do
begin
// Properties.Item[I] is ADOInt.Property_
PropName := ADOConnection.Properties.Item[I].Name;
PropValue := VarToWideStr(ADOConnection.Properties.Item[I].Value);
ShowMessage(Format('%s=%s', [PropName, PropValue]));
end;
finally
ADOConnection.Free;
end;
Там может быть гораздо больше свойств, которые добавляются / изменяются в ConnectionString
после TADOConnection
был подключен к БД, поэтому необходимо принять это во внимание.