إنشاء موفر قابل للتحديث

Visual C++ 6.0 يدعم الموفرين الذين للقراءة فقط. يدعم Visual C++ .NET الموفرين القابلين للتحديث أو الموفرين الذين يمكنهم تحديث (الكتابة فى) مخزن البيانات. يناقش هذا الموضوع كيفية إنشاء موفرين قابلين للتحديث باستخدام قوالب OLE DB.

يفترض هذا الموضوع أنك تبدأ بموفر يعمل. هناك خطوتان لإنشاء موفر قابل للتحديث. يجب أولاً تحديد كيف سيقوم الموفر بإجراء تغييرات على مخزن البيانات ; خاصة، ما إذا كان سيتم إجراء التغييرات فوراً أو تأجيلها حتى يتم إصدار أمر تحديث. يصف القسم جعل الموفرين قابلين للتحديث التغييرات والإعدادات التي تحتاجها في التعليمات البرمجية الخاصة بالموفر.

بعد ذلك، يجب التأكد من أن الموفر يحتوي على كافة الوظائف لدعم أي شيء قد يطلبه المستخدم منه. إذا أراد المستهلك تحديث مخزن البيانات , يتوجب على الموفر أن يحتوي على التعليمات البرمجية التى تحفظ البيانات بمخزن البيانات. على سبيل المثال، قد تستخدم مكتبة وقت التشغيل C أو MFC لإجراء مثل هذه العمليات على مصدر البيانات الخاص بك. يوضح القسم الكتابة فى مصدر البيانات كيفية الكتابة إلى مصدر البيانات و التعامل مع NULL و القيم الافتراضية و مجموعة إشارات العمود.

ملاحظة

UpdatePV هو مثال على موفر قابل للتحديث . UpdatePV هو نفسه مثل MyProv ولكن مع دعم القابلية للتحديث.

جعل الموفرين قابلين للتحديث

المفتاح لجعل الموفر قابل للتحديث هو فهم أى عمليات تريد من الموفر تنفيذها على مخزن البيانات و كيف تريد من الموفر تنفيذ هذه العمليات. وعلى وجه الخصوص، المشكلة الرئيسية ما إذا كانت التحديثات على مخزن البيانات يتم تنفيذها فوراً أو تؤجل حتى يتم إصدار أمر تحديث.

يجب أولاً تحديد ما إذا كنت سوف ترث من IRowsetChangeImpl أو IRowsetUpdateImpl في فئة مجموعة الصفوف الخاصة بك. سوف تتأثر وظيفة ثلاثة أساليب استناداً إلى أي من هذه سوف تختار أن تطبقه : SetData ،و DeleteRows.

  • إذا كنت ترث من IRowsetChangeImpl، فإن استدعاء هذه الأساليب الثلاثة مباشرة سوف يغير في مخزن البيانات.

  • إذا كان ترث من IRowsetUpdateImpl, فإن الأساليب تؤجل التغييرات على مخزن البيانات حتى تستدعى Update أو GetOriginalData, أو Undo. إذا كان التحديث يتضمن تغييرات عديدة, يتم تنفيذها في الوضع الدفعي (لاحظ أن تغييرات التجميع قد تضيف حملاً زائداً كبيراً على ذاكرة ).

لاحظ أن IRowsetUpdateImpl يشتق من IRowsetChangeImpl. لذا، يعطيك IRowsetUpdateImpl القدرة على التغيير بالإضافة إلى القدرة على الدفع.

لدعم القابلية للتحديث في الموفر الخاص بك

  1. في فئة مجموعة الصفوف، قم بالوراثة من IRowsetChangeImpl أو IRowsetUpdateImpl. توفر هذه الفئات الواجهات المناسبة للتغيير فى مخزن البيانات:

    إضافة IRowsetChange

    أضف IRowsetChangeImpl إلى سلسلة الوراثة لديك باستخدام هذا النموذج:

    IRowsetChangeImpl< rowset-name, storage-name >
    

    أيضاً أضف COM_INTERFACE_ENTRY(IRowsetChange) إلى مقطع BEGIN_COM_MAP في فئة مجموعة الصفوف

    إضافة IRowsetUpdate

    أضف IRowsetUpdate إلى سلسلة الوراثة لديك باستخدام هذا النموذج:

    IRowsetUpdateImpl< rowset-name, storage>
    

    ملاحظة

    يجب إزالة سطر IRowsetChangeImpl من سلسلة الوراثة الخاصة بك . هذا الاستثناء إلى التوجيه المذكور سابقاً يجب أن يتضمن التعليمات البرمجية الخاصة بـ IRowsetChangeImpl.

  2. أضف التالي إلى مخطط COM الخاص بك (BEGIN_COM_MAP ... END_COM_MAP ):

    إذا قمت بتطبيق

    أضف إلى مخطط COM

    IRowsetChangeImpl

    COM_INTERFACE_ENTRY(IRowsetChange)

    IRowsetUpdateImpl

    COM_INTERFACE_ENTRY(IRowsetChange)COM_INTERFACE_ENTRY(IRowsetUpdate)

  3. في الأمر الخاص بك، أضف التالي إلى مخطط مجموعة الخصائص الخاص بك ( BEGIN_PROPSET_MAP... END_PROPSET_MAP ):

    إذا قمت بتطبيق

    أضف إلى مخطط مجموعة الخصائص

    IRowsetChangeImpl

    PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)

    IRowsetUpdateImpl

    PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)PROPERTY_INFO_ENTRY_VALUE(IRowsetUpdate, VARIANT_FALSE)

  4. في مخطط مجموعة الخصائص الخاص بك يجب عليك أيضاً تضمين كافة الإعدادات التالية كما تظهر أدناه:

    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)
    

    يمكنك البحث عن القيم المستخدمة في هذه الاستدعاءات للماكرو عن طريق البحث في Atldb.h عن معرفات و قيم الخاصية (إذا اختلفت Atldb.h عن الوثائق المنشورة على الإنترنت، تحل Atldb.h محل الوثائق).

    ملاحظة

    العديد من إعدادات VARIANT_FALSE و VARIANT_TRUE مطلوبة من قِبل قوالب OLE DB; مواصفات OLE DB تقول انه يمكن أن تكون للقراءة/الكتابة ولكن قوالب OLE DB يمكنها فقط دعم قيمة واحدة.

    إذا قمت بتطبيق IRowsetChangeImpl

    إذا قمت بتطبيق IRowsetChangeImpl ، يجب عليك تعيين الخصائص التالية على الموفر الخاص بك. يتم استخدام هذه الخصائص بشكل أساسي لطلب واجهات خلال ICommandProperties::SetProperties.

    • DBPROP_IRowsetChange: إعداد هذه يعيّن DBPROP_IRowsetChange تلقائياً.

    • DBPROP_UPDATABILITY: قناع بت يحدد الأساليب المعتمدة على IRowsetChange: SetData, DeleteRows, أو InsertRow.

    • DBPROP_CHANGEINSERTEDROWS: يمكن للمستخدم استدعاء IRowsetChange::DeleteRows أو SetData للصفوف المدرجة حديثاً.

    • DBPROP_IMMOBILEROWS: لن تقوم مجموعة الصفوف بإعادة ترتيب الصفوف المدرجة أو المحدّثة .

    إذا قمت بتطبيق IRowsetUpdateImpl

    إذا قمت بتطبيق IRowsetUpdateImpl, يجب تعيين الخصائص التالية على الموفر الخاص بك بالإضافة إلى تعيين كافة الخصائص IRowsetChangeImpl المذكورة سابقاً:

    • DBPROP_IRowsetUpdate.

    • DBPROP_OWNINSERT: يجب أن يكون READ_ONLY AND VARIANT_TRUE.

    • DBPROP_OWNUPDATEDELETE: يجب أن يكون READ_ONLY AND VARIANT_TRUE.

    • DBPROP_OTHERINSERT: يجب أن يكون READ_ONLY AND VARIANT_TRUE.

    • DBPROP_OTHERUPDATEDELETE: يجب أن يكون READ_ONLY AND VARIANT_TRUE.

    • DBPROP_REMOVEDELETED: يجب أن يكون READ_ONLY AND VARIANT_TRUE.

    • DBPROP_MAXPENDINGROWS.

      ملاحظة

      إذا كنت تدعم إعلامات ، من الممكن أن يكون لديك بعض الخصائص الأخرى أيضاً; راجع المقطع على IRowsetNotifyCP للحصول على هذه القائمة.

    لمثال عن كيفية تعيين الخصائص، راجع مخطط مجموعة الخصائص في CUpdateCommand (في Rowset.h) في UpdatePV .

الكتابة فى مصدر البيانات

للقراءة من مصدر البيانات، استدع الدالة Execute . للكتابة فى مصدر البيانات استدع الدالة FlushData . (فى المفهوم العام, المسح (flush) يعتنى بحفظ التعديلات التي تجريها على الجدول أو الفهرس فى القرص.)

FlushData(HROW, HACCESSOR);

وسائط مؤشر الصف ( HROW ) و مؤشر الموصل ( HACCESSOR ) تسمح لك بتحديد منطقة الكتابة. بشكل عام، تكتب حقل بيانات واحد في كل مرة.

يكتب الأسلوب FlushData البيانات بالتنسيق الذي تم تخزينها به فى الأصل . إذا كنت لا تتجاوز هذه الدالة, سوف يعمل الموفر الخاص بك بصورة صحيحة ولكن لن يتم مسح التغييرات إلى مخزن البيانات.

متى تمسح

تستدعى قوالب الموفر FlushData كلما تحتاج البيانات إلى كتابتها فى مخزن البيانات ; هذا عادةً (و ليس دائماً) يحدث نتيجة استدعاءات الدالات التالية:

  • IRowsetChange::DeleteRows

  • IRowsetChange::SetData

  • IRowsetChange::InsertRows (إذا كان هناك بيانات جديدة لإدراجها في الصف)

  • IRowsetUpdate::Update

كيفية عمله:

يقوم المستخدم باستدعاء يتطلب مسح (مثلUpdate) و هذا الاستدعاء يتم تمريره إلى الموفر الذي دوماً يقوم بما يلي:

  • يستدعي SetDBStatus كلما كان لديك قيمة حالة مربوطة (راجع، الفصل 6، مرجع مبرمجي OLE DB، أجزاء البيانات : الحالة).

  • يتحقق من علامات العمود.

  • يستدعي IsUpdateAllowed.

تساعد هذه الخطوات الثلاث على توفير الأمان. ثم يستدعي الموفر FlushData.

كيفية تنفيذ FlushData

لتطبيق FlushData ، تحتاج إلى أخذ العديد من المشكلات في الاعتبار :

  • التأكد من انه يمكن لمخزن البيانات معالجة التغييرات.

  • معالجة قيم NULL.

  • معالجة القيم الافتراضية.

لتنفيذ الأسلوب FlushData الخاص بك، تحتاج إلى:

  • الانتقال إلى فئة مجموعة الصفوف.

  • في فئة مجموعة الصفوف ضع التصريح الخاص بـ:

   HRESULT FlushData(HROW, HACCESSOR)
   {
       // Insert your implementation here and return an HRESULT.
   }
  • وفّر تطبيق FlushData.

التنفيذ الجيد لـ FlushData يخزن فقط الصفوف و الأعمدة المحدثة فعلياً. يمكنك استخدام المعلمات HROW و HACCESSOR لتحديد الصف و العمود المخزنين حالياً من أجل التحسين.

بشكل عام، التحدى الأكبر هو العمل مع مخزن البيانات الأصلى الخاص بك . إذا كان يمكن، حاول القيام بما يلى:

  • ليكن أسلوب الكتابة فى مخزن البيانات الخاص بك أبسط ما يمكن.

  • عالج القيم NULL (اختياري ولكن يُنصح به).

  • عالج القيم الافتراضية (اختياري ولكن يُنصح به).

أفضل شىء تقوم به هو الحصول على قيم معينة فعلية فى مخزن البيانات الخاص بك لـ NULL و القيم الافتراضية. الأفضل أن تستطيع استنتاج هذه البيانات. إذا لم يكن ممكناً، فمن الأفضل لك عدم السماح بالقيم NULLوالقيم الافتراضية.

يوضح المثال التالي كيفية تنفيذ FlushData في الفئة RUpdateRowset في نموذج UpdatePV (راجع Rowset.h في نموذج التعليمات البرمجية):

///////////////////////////////////////////////////////////////////////////
// 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;
}

معالجة التغييرات

بالنسبة للموفر الخاص بك لمعالجة التغييرات، تحتاج أولاً إلى التأكد أن مخزن البيانات الخاص بك (مثل الملف النصي أو ملف الفيديو) لديه التسهيلات التي تمكنك من إجراء تغييرات عليه. إذا لم يكن، يجب إنشاء تلك التعليمات البرمجية بشكل منفصل من مشروع الموفر.

معالجة بيانات NULL

يمكن أن يرسل المستخدم النهائي بيانات NULL. عند كتابة قيم NULL فى الحقول في مصدر البيانات ، يمكن أن يكون هناك مشاكل محتملة. تخيل تطبيق أخذ طلبات، يقبل قيم اسم المدينة و الرمز البريدي ; يمكنه قبول أي أو كل القيم ولكن لا يمكن ألا يقبل أيها، لأن في هذه الحالة التسليم يكون مستحيلاً. يكون عليك إذاً وضع قيود على تركيبات معينة من قيم NULL في الحقول ذات الأهمية للتطبيق الخاص بك.

كمطور للموفر ,عليك مراعاة كيف ستقوم بتخزين تلك البيانات، و كيف سيتم قراءة تلك البيانات من مخزن البيانات و كيف ستقوم بتعيينها للمستخدم. وبوجه خاص، يجب مراعاة كيفية تغيير حالة البيانات لبيانات مجموعة الصفوف في مصدر البيانات (على سبيل المثال، DataStatus = NULL ). تحدد أى قيمة يتم إرجاعها عندما يصل العميل إلى حقل يحتوي على القيمة NULL .

انظر إلي التعليمات البرمجية في نموذج UpdatePV ; فإنه يوضح كيفية معالجة الموفر للقيمة NULL . في UpdatePV ، يخزّن الموفر بياناتNULL بكتابة سلسلة "NULL" فى مخزن البيانات. عندما يقرأ بيانات NULL من مخزن البيانات, فانه يشاهد هذه السلسلة ثم يقوم بإفراغ المخزن المؤقت منشئاً سلسلة NULL. يكون عنده أيضاً تجاوز لدالة IRowsetImpl::GetDBStatus التي تقوم بإرجاع DBSTATUS_S_ISNULL إذا كانت قيمة هذه البيانات فارغة.

وضع علامة على أعمدة Nullable

إذا قمت أيضاً بتطبيق مخطط مجموعة سجلات (راجع IDBSchemaRowsetImpl) ، يجب تحديد التطبيق الخاص بك في مجموعة الصفوف DBSCHEMA_COLUMNS (عادة يتم تعليمها في الموفر الخاص بك بواسطة C xxx SchemaColSchemaRowset ) لتوضيح أن العمود قابل للاحتواء على قيمة فارغة (nullable).

كما تحتاج إلى تحديد كافة الأعمدة nullable التى تحتوي على القيمة DBCOLUMNFLAGS_ISNULLABLE في إصدار GetColumnInfo.

في تطبيق قوالب OLE DB، في حالة الفشل فى وضع علامة على أعمدة كـ nullable، الموفر يفترض أنه يجب أن تحتوي على قيمة وقد لا يسمح للمستخدم بإرسال القيم NULL.

يوضح المثال التالي كيفية تنفيذ دالة CommonGetColInfo في CUpdateCommand (راجع UpProvRS.cpp) في UpdatePV. لاحظ كيف يكون لدى هذه الأعمدة DBCOLUMNFLAGS_ISNULLABLE للأعمدة nullable.

/////////////////////////////////////////////////////////////////////////////
// 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، تقع عليك مسؤولية التعامل مع تغيير القيم الافتراضية.

الافتراضي لـ FlushData و Executeهو إرجاع S_OK. لذلك، إذا كنت لا تتجاوز هذه الدالة، تظهر التغييرات بأنها ناجحة (سيتم إرجاع S_OK) ، ولكن لن يتم نقله إلى مخزن البيانات.

في العينة UpdatePV (في Rowset.h) ، الأسلوب SetDBStatus يعالج القيم الافتراضية كما يلي:

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;
}

علامات العمود

إذا كنت تدعم القيم الافتراضية على الأعمدة الخاصة بك، ستحتاج إلى تعيينها باستخدام بيانات التعريف في الفئة **<**فئة موفر > SchemaRowset. عين m_bColumnHasDefault = VARIANT_TRUE.

تقع عليك أيضاً مسؤولية تعيين علامات العمود المحددة باستخدام نوع التعداد DBCOLUMNFLAGS. تصف علامات العمود الصفات المميزة.

على سبيل المثال، في الفئة CUpdateSessionColSchemaRowset في UpdatePV (في Session.h) ، يتم تعيين العمود الأول بهذه الطريقة:

// 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]);

هذه التعليمة البرمجية تعيّن، مع أشياء أخرى، أن العمود يعتمد قيمة افتراضية 0 يمكن الكتابة عليها, و أن كافة البيانات في العمود لها نفس الطول. إذا كنت ترغب أن تكون البيانات في العمود لها طول متغير, فلن تقوم بتعيين هذه العلامة.

راجع أيضًا:

المرجع

إنشاء موفر OLE DB