Güncelleştirilebilir Sağlayıcı Oluşturma
Visual C++, veri depolarını güncelleştirebilen (yazabilen) güncelleştirilebilir sağlayıcıları veya sağlayıcıları destekler. Bu konuda, OLE DB şablonlarını kullanarak güncelleştirilebilir sağlayıcıların nasıl oluşturulacağı açıklanır.
Bu konu başlığında, çalışılabilir bir sağlayıcıyla başladığınız varsayılır. Güncelleştirilebilir sağlayıcı oluşturmanın iki adımı vardır. Önce sağlayıcının veri deposunda nasıl değişiklik yapacağına karar vermelisiniz; özellikle, bir güncelleştirme komutu verilene kadar değişikliklerin hemen mi yoksa ertelenerek mi yapılacağını belirtin. "Sağlayıcıları Güncelleştirilebilir Hale Getirme" bölümünde, sağlayıcı kodunda yapmanız gereken değişiklikler ve ayarlar açıklanmaktadır.
Ardından, sağlayıcınızın tüketicinin istediği her şeyi destekleyecek tüm işlevleri içerdiğinden emin olmanız gerekir. Tüketici veri depoyu güncelleştirmek istiyorsa, sağlayıcının verileri veri deposunda kalıcı hale getiren kod içermesi gerekir. Örneğin, veri kaynağınızda bu tür işlemleri gerçekleştirmek için C Çalışma Zamanı Kitaplığı'nı veya MFC'yi kullanabilirsiniz. "Veri Kaynağına Yazma" bölümünde veri kaynağına yazma, NULL ve varsayılan değerlerle ilgilenme ve sütun bayraklarını ayarlama işlemleri açıklanır.
Not
UpdatePV , güncelleştirilebilir bir sağlayıcı örneğidir. UpdatePV, MyProv ile aynıdır ancak güncelleştirilebilir destek içerir.
Sağlayıcıyı güncelleştirilebilir hale getirmenin anahtarı, sağlayıcınızın veri deposunda gerçekleştirmesini istediğiniz işlemleri ve sağlayıcının bu işlemleri nasıl gerçekleştirmesini istediğinizi anlamaktır. Özellikle, önemli sorun veri deposu güncelleştirmelerinin bir güncelleştirme komutu verilene kadar hemen mi yoksa ertelenmiş mi (toplu) olarak yapılmasıdır.
Önce satır kümesi sınıfınızdan mı IRowsetUpdateImpl
yoksa sınıfından IRowsetChangeImpl
mı devralmaya karar vermelisiniz. Bunlardan hangisini uygulamayı seçtiğinize bağlı olarak, üç yöntemin işlevselliği etkilenir: SetData
, InsertRows
ve DeleteRows
.
IRowsetChangeImpl'den devralırsanız, bu üç yöntemin çağrılması veri depoyu hemen değiştirir.
IRowsetUpdateImpl'den devralırsanız, yöntemler ,
GetOriginalData
veyaUndo
çağrılanaUpdate
kadar veri deposuna yapılan değişiklikleri erteler. Güncelleştirme birkaç değişiklik içeriyorsa, bunlar toplu iş modunda gerçekleştirilir (toplu işlem değişikliklerinin önemli bellek yükü ekleyebileceğini unutmayın).
öğesinin türetildiğini IRowsetUpdateImpl
IRowsetChangeImpl
unutmayın. Böylece, IRowsetUpdateImpl
değişiklik özelliğine ek olarak toplu iş özelliği sağlar.
Satır kümesi sınıfınızda veya
IRowsetUpdateImpl
öğesindenIRowsetChangeImpl
devralın. Bu sınıflar, veri depolarını değiştirmek için uygun arabirimler sağlar:IRowsetChange Ekleme
Bu formu kullanarak devralma zincirinize ekleyin
IRowsetChangeImpl
:IRowsetChangeImpl< rowset-name, storage-name >
Satır kümesi sınıfınızdaki bölüme
BEGIN_COM_MAP
de ekleyinCOM_INTERFACE_ENTRY(IRowsetChange)
.IRowsetUpdate Ekleme
Bu formu kullanarak devralma zincirinize ekleyin
IRowsetUpdate
:IRowsetUpdateImpl< rowset-name, storage>
Not
Satırı devralma zincirinizden kaldırmanız
IRowsetChangeImpl
gerekir. Daha önce bahsedilen yönergenin bu özel durumu içinIRowsetChangeImpl
kodunu içermelidir.COM haritanıza aşağıdakileri ekleyin (
BEGIN_COM_MAP ... END_COM_MAP
):Uygularsanız COM haritasına ekle IRowsetChangeImpl
COM_INTERFACE_ENTRY(IRowsetChange)
IRowsetUpdateImpl
COM_INTERFACE_ENTRY(IRowsetUpdate)
Uygularsanız Özellik kümesi eşlemesine ekle IRowsetChangeImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)
IRowsetUpdateImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetUpdate, VARIANT_FALSE)
Komutunuzda, aşağıdakileri özellik kümesi eşlemenize (
BEGIN_PROPSET_MAP ... END_PROPSET_MAP
) ekleyin:Uygularsanız Özellik kümesi eşlemesine ekle IRowsetChangeImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)
IRowsetUpdateImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)PROPERTY_INFO_ENTRY_VALUE(IRowsetUpdate, VARIANT_FALSE)
Özellik kümesi haritanızda aşağıdaki ayarların tümünü aşağıda gösterildiği gibi de eklemelisiniz:
PROPERTY_INFO_ENTRY_VALUE(UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE) PROPERTY_INFO_ENTRY_VALUE(CHANGEINSERTEDROWS, VARIANT_TRUE) PROPERTY_INFO_ENTRY_VALUE(IMMOBILEROWS, VARIANT_TRUE) PROPERTY_INFO_ENTRY_EX(OWNINSERT, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(OWNUPDATEDELETE, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(OTHERINSERT, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(OTHERUPDATEDELETE, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(REMOVEDELETED, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_FALSE, 0)
Özellik kimlikleri ve değerleri için Atldb.h'ye bakarak bu makro çağrılarında kullanılan değerleri bulabilirsiniz (Atldb.h çevrimiçi belgelerden farklıysa, Atldb.h belgelerin yerini alır).
Not
VE
VARIANT_TRUE
ayarlarınınVARIANT_FALSE
çoğu OLE DB şablonları için gereklidir; OLE DB belirtimi okunabilir/yazılabilir olduğunu belirtir, ancak OLE DB şablonları yalnızca bir değeri destekleyebilir.IRowsetChangeImpl uygularsanız
uygularsanız
IRowsetChangeImpl
, sağlayıcınızda aşağıdaki özellikleri ayarlamanız gerekir. Bu özellikler öncelikli olarak aracılığıylaICommandProperties::SetProperties
arabirim istemek için kullanılır.DBPROP_IRowsetChange
: Bu ayar otomatik olarak ayarlanırDBPROP_IRowsetChange
.DBPROP_UPDATABILITY
: , veyaInsertRow
üzerindeIRowsetChange
SetData
DeleteRows
desteklenen yöntemleri belirten bir bit maskesi.DBPROP_CHANGEINSERTEDROWS
: Tüketici, yeni eklenen satırları çağırabilirIRowsetChange::DeleteRows
veyaSetData
çağırabilir.DBPROP_IMMOBILEROWS
: Satır kümesi eklenen veya güncelleştirilen satırları yeniden sıralamaz.
IRowsetUpdateImpl uygularsanız
uygularsanız
IRowsetUpdateImpl
, daha önce listelenen tüm özellikleri ayarlamanın yanı sıra sağlayıcınızda aşağıdaki özellikleriIRowsetChangeImpl
ayarlamanız gerekir:DBPROP_IRowsetUpdate
.DBPROP_OWNINSERT
: READ_ONLY VE VARIANT_TRUE olmalıdır.DBPROP_OWNUPDATEDELETE
: READ_ONLY VE VARIANT_TRUE olmalıdır.DBPROP_OTHERINSERT
: READ_ONLY VE VARIANT_TRUE olmalıdır.DBPROP_OTHERUPDATEDELETE
: READ_ONLY VE VARIANT_TRUE olmalıdır.DBPROP_REMOVEDELETED
: READ_ONLY VE VARIANT_TRUE olmalıdır.DBPROP_MAXPENDINGROWS
.
Not
Bildirimleri destekliyorsanız, bazı başka özellikleriniz de olabilir; bu listenin üzerindeki
IRowsetNotifyCP
bölümüne bakın.
Veri kaynağından okumak için işlevini çağırın Execute
. Veri kaynağına yazmak için işlevini çağırın FlushData
. (Genel olarak, temizleme, bir tabloda veya dizinde yaptığınız değişiklikleri diske kaydetmek anlamına gelir.)
FlushData(HROW, HACCESSOR);
Satır tutamacı (HROW) ve erişimci tutamacı (HACCESSOR) bağımsız değişkenleri, yazılacak bölgeyi belirtmenize olanak sağlar. Genellikle tek seferde tek bir veri alanı yazarsınız.
FlushData
yöntemi, verileri ilk olarak depolandığı biçimde yazar. Bu işlevi geçersiz kılmazsanız, sağlayıcınız düzgün çalışır ancak değişiklikler veri deposuna boşaltılmaz.
Sağlayıcı şablonları, verilerin veri deposuna yazılması gerektiğinde FlushData'yı çağırır; bu genellikle (ancak her zaman değil) aşağıdaki işlevlere yapılan çağrıların bir sonucu olarak gerçekleşir:
IRowsetChange::DeleteRows
IRowsetChange::SetData
IRowsetChange::InsertRows
(satıra eklenecek yeni veriler varsa)IRowsetUpdate::Update
Tüketici, temizleme (Güncelleştirme gibi) gerektiren bir çağrı yapar ve bu çağrı sağlayıcıya geçirilir ve her zaman aşağıdakileri yapar:
Durum değeri bağlı olduğunda çağrılar
SetDBStatus
.Sütun bayraklarını denetler.
IsUpdateAllowed
çağrısı yapar.
Bu üç adım güvenliği sağlamaya yardımcı olur. Ardından sağlayıcı öğesini çağırır FlushData
.
uygulamasını uygulamak FlushData
için birkaç sorunu dikkate almanız gerekir:
Veri deposunun değişiklikleri işleyebileceğinden emin olun.
NULL değerleri işleme.
Kendi FlushData
yönteminizi uygulamak için şunları yapmanız gerekir:
Satır kümesi sınıfınıza gidin.
Satır kümesi sınıfına şu bildirimini koyun:
HRESULT FlushData(HROW, HACCESSOR) { // Insert your implementation here and return an HRESULT. }
uygulamasını
FlushData
sağlayın.
İyi bir uygulama yalnızca FlushData
gerçekten güncelleştirilen satırları ve sütunları depolar. İyileştirme için depolanmakta olan geçerli satırı ve sütunu belirlemek için HROW ve HACCESSOR parametrelerini kullanabilirsiniz.
Genellikle en büyük zorluk kendi yerel veri deponuzla çalışmaktır. Mümkünse şunları deneyin:
Veri deponuza yazma yöntemini mümkün olduğunca basit tutun.
NULL değerlerini işleyebilir (isteğe bağlı ancak önerilen).
Varsayılan değerleri işleyebilir (isteğe bağlı ancak önerilen).
Yapılacak en iyi şey, veri deponuzda NULL ve varsayılan değerler için gerçek belirtilen değerlere sahip olmaktır. Bu verileri tahmin etmeniz en iyisidir. Aksi takdirde NULL ve varsayılan değerlere izin vermemeye dikkat edin.
Aşağıdaki örnekte, örnekteki RUpdateRowset
sınıfında UpdatePV
nasıl FlushData
uygulandığı gösterilmektedir (örnek kodda Rowset.h bölümüne bakın):
///////////////////////////////////////////////////////////////////////////
// class RUpdateRowset (in rowset.h)
...
HRESULT FlushData(HROW, HACCESSOR)
{
ATLTRACE2(atlTraceDBProvider, 0, "RUpdateRowset::FlushData\n");
USES_CONVERSION;
enum {
sizeOfString = 256,
sizeOfFileName = MAX_PATH
};
FILE* pFile = NULL;
TCHAR szString[sizeOfString];
TCHAR szFile[sizeOfFileName];
errcode err = 0;
ObjectLock lock(this);
// From a filename, passed in as a command text,
// scan the file placing data in the data array.
if (m_strCommandText == (BSTR)NULL)
{
ATLTRACE( "RRowsetUpdate::FlushData -- "
"No filename specified\n");
return E_FAIL;
}
// Open the file
_tcscpy_s(szFile, sizeOfFileName, OLE2T(m_strCommandText));
if ((szFile[0] == _T('\0')) ||
((err = _tfopen_s(&pFile, &szFile[0], _T("w"))) != 0))
{
ATLTRACE("RUpdateRowset::FlushData -- Could not open file\n");
return DB_E_NOTABLE;
}
// Iterate through the row data and store it.
for (long l=0; l<m_rgRowData.GetSize(); l++)
{
CAgentMan am = m_rgRowData[l];
_putw((int)am.dwFixed, pFile);
if (_tcscmp(&am.szCommand[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szCommand);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
if (_tcscmp(&am.szText[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szText);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
if (_tcscmp(&am.szCommand2[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szCommand2);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
if (_tcscmp(&am.szText2[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szText2);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
}
if (fflush(pFile) == EOF || fclose(pFile) == EOF)
{
ATLTRACE("RRowsetUpdate::FlushData -- "
"Couldn't flush or close file\n");
}
return S_OK;
}
Sağlayıcınızın değişiklikleri işleyebilmesi için öncelikle veri deponuzda (metin dosyası veya video dosyası gibi) üzerinde değişiklik yapmanıza olanak tanıyan olanaklar olduğundan emin olmanız gerekir. Aksi takdirde, bu kodu sağlayıcı projesinden ayrı olarak oluşturmanız gerekir.
Son kullanıcının NULL veri göndermesi mümkündür. Veri kaynağındaki alanlara NULL değerler yazdığınızda olası sorunlar olabilir. Şehir ve posta kodu değerlerini kabul eden bir sipariş alma uygulaması düşünün; ya da her iki değeri de kabul edebilir, ancak hiçbirini kabul etmeyebilir, çünkü bu durumda teslim mümkün olmazdı. Bu nedenle, uygulamanız için anlamlı olan alanlardaki null değerlerin belirli birleşimlerini kısıtlamanız gerekir.
Sağlayıcı geliştiricisi olarak bu verileri nasıl depolayabileceğinizi, bu verileri veri deposundan nasıl okuyabileceğinizi ve bunu kullanıcıya nasıl belirttiğinizi göz önünde bulundurmanız gerekir. Özellikle, veri kaynağındaki satır kümesi verilerinin veri durumunun nasıl değiştirileceğini göz önünde bulundurmanız gerekir (örneğin, DataStatus = NULL). Bir tüketici NULL değeri içeren bir alana eriştiğinde hangi değerin döndürüleceğine siz karar verirsiniz.
UpdatePV örneğindeki koda bakın; bir sağlayıcının NULL verileri nasıl işleyebileceğini gösterir. UpdatePV'de sağlayıcı, veri deposunda "NULL" dizesini yazarak NULL verileri depolar. Veri deposundan NULL verileri okuduğunda, bu dizeyi görür ve ardından boş bir dize oluşturarak arabelleği boşaltabilir. Ayrıca, veri değeri boşsa DBSTATUS_S_ISNULL döndürdüğü geçersiz kılmaya IRowsetImpl::GetDBStatus
da sahiptir.
Şema satır kümelerini de uygularsanız (bkz IDBSchemaRowsetImpl
. ) uygulamanız DBSCHEMA_COLUMNS satır kümesinde (genellikle CxxxSchemaColSchemaRowset tarafından işaretlenmiştir) sütunun null atanabilir olduğunu belirtmelidir.
Ayrıca, tüm null atanabilir sütunların sürümünüzdeki DBCOLUMNFLAGS_ISNULLABLE değerini içermesini GetColumnInfo
belirtmeniz gerekir.
OLE DB şablonları uygulamasında sütunları null atanabilir olarak işaretlemezseniz, sağlayıcı bir değer içermesi gerektiğini varsayar ve tüketicinin null değerler göndermesine izin vermez.
Aşağıdaki örnek, işlevin CommonGetColInfo
UpdatePV'deki CUpdateCommand'da nasıl uygulandığını gösterir (bkz. UpProvRS.cpp). Sütunların null atanabilir sütunlar için nasıl bu DBCOLUMNFLAGS_ISNULLABLE olduğunu unutmayın.
/////////////////////////////////////////////////////////////////////////////
// CUpdateCommand (in UpProvRS.cpp)
ATLCOLUMNINFO* CommonGetColInfo(IUnknown* pPropsUnk, ULONG* pcCols, bool bBookmark)
{
static ATLCOLUMNINFO _rgColumns[6];
ULONG ulCols = 0;
if (bBookmark)
{
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Bookmark"), 0,
sizeof(DWORD), DBTYPE_BYTES,
0, 0, GUID_NULL, CAgentMan, dwBookmark,
DBCOLUMNFLAGS_ISBOOKMARK)
ulCols++;
}
// Next set the other columns up.
// Add a fixed length entry for OLE DB conformance testing purposes
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Fixed"), 1, 4, DBTYPE_UI4,
10, 255, GUID_NULL, CAgentMan, dwFixed,
DBCOLUMNFLAGS_WRITE |
DBCOLUMNFLAGS_ISFIXEDLENGTH)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Command"), 2, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szCommand,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Text"), 3, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szText,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Command2"), 4, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szCommand2,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Text2"), 5, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szText2,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
if (pcCols != NULL)
{
*pcCols = ulCols;
}
return _rgColumns;
}
NULL verilerde olduğu gibi, varsayılan değerleri değiştirme sorumluluğunuz vardır.
ve Execute
varsayılanı FlushData
S_OK döndürmektir. Bu nedenle, bu işlevi geçersiz kılmazsanız, değişiklikler başarılı gibi görünür (S_OK döndürülür), ancak veri deposuna iletilmez.
UpdatePV
Örnekte (Rowset.h dosyasında), SetDBStatus
yöntemi varsayılan değerleri aşağıdaki gibi işler:
virtual HRESULT SetDBStatus(DBSTATUS* pdbStatus, CSimpleRow* pRow,
ATLCOLUMNINFO* pColInfo)
{
ATLASSERT(pRow != NULL && pColInfo != NULL && pdbStatus != NULL);
void* pData = NULL;
char* pDefaultData = NULL;
DWORD* pFixedData = NULL;
switch (*pdbStatus)
{
case DBSTATUS_S_DEFAULT:
pData = (void*)&m_rgRowData[pRow->m_iRowset];
if (pColInfo->wType == DBTYPE_STR)
{
pDefaultData = (char*)pData + pColInfo->cbOffset;
strcpy_s(pDefaultData, "Default");
}
else
{
pFixedData = (DWORD*)((BYTE*)pData +
pColInfo->cbOffset);
*pFixedData = 0;
return S_OK;
}
break;
case DBSTATUS_S_ISNULL:
default:
break;
}
return S_OK;
}
Sütunlarınızda varsayılan değerleri destekliyorsanız SchemaRowset sağlayıcı sınıfındaki <>meta verileri kullanarak ayarlamanız gerekir. öğesini ayarlayın m_bColumnHasDefault = VARIANT_TRUE
.
Ayrıca DBCOLUMNFLAGS numaralandırılmış türü kullanılarak belirtilen sütun bayraklarını ayarlama sorumluluğunuz da vardır. Sütun bayrakları sütun özelliklerini açıklar.
Örneğin, CUpdateSessionColSchemaRowset
içindeki UpdatePV
(Session.h'de) sınıfında ilk sütun şu şekilde ayarlanır:
// Set up column 1
trData[0].m_ulOrdinalPosition = 1;
trData[0].m_bIsNullable = VARIANT_FALSE;
trData[0].m_bColumnHasDefault = VARIANT_TRUE;
trData[0].m_nDataType = DBTYPE_UI4;
trData[0].m_nNumericPrecision = 10;
trData[0].m_ulColumnFlags = DBCOLUMNFLAGS_WRITE |
DBCOLUMNFLAGS_ISFIXEDLENGTH;
lstrcpyW(trData[0].m_szColumnDefault, OLESTR("0"));
m_rgRowData.Add(trData[0]);
Bu kod, sütunun 0 varsayılan değerini desteklediğini, yazılabilir olduğunu ve sütundaki tüm verilerin aynı uzunlukta olduğunu belirtir. Bir sütundaki verilerin değişken uzunluğu olmasını istiyorsanız, bu bayrağı ayarlamazsınız.