(DotNet) NET (2012 год)

Змінні Nullable та як обробляти DBNull з бази за допомогою Extension-функції.

Visual Basic має величезну кількість типів даних (ось тут є повна документація по VB), але якщо підсумувати все дуже коротко, то є єлементарні типи даних (Boolean, Byte, Char, Date, Decimal, Double, Integer, Long, SByte, Short, Single, UInteger, ULong, UShort), е такі самі прості, але з деякими особливістями - Constant, Literal, Enum, String, а є комплексні, що скаладаються з простих типів, наприклад Array (декількох різних типів), Structure, Object, Collection, XML. Самому можно побудувати не тільки складні типи даних, але й єлементарні, унаслідовавши їх від ValueType, як це зроблено у NET 4.7 де побудований новий тип даних Tuples. Це лише кріхітка всього, що існує в Бейсіці щодо навіть єлекментарних типів даних, наприклад окремі класи єкспортуть власні типи єлекнтарних данних, тобто наприклад існують тіпи SqlTypes як SqlFileStream, SqlXml, SqlMoney, SqlBinary, SqlGuid та багато інших.

Складні типи даних утворюються у бейсіці вкрай запоморочено. Наприклад існує цілий всесвіт Дженеріків та Lambda Expression, де є методи, які вміють працювати з будь-якими класами, є інтерфейси, контрейнси між ними, взагалі одні складні типи даних (такі як класи та інтерфейси) будуються на базі інших. Ось тут, наприклад, це у 2002-му році, коли замість VB6 тільки-тільки з'явився VB.NET, я намагався розібратися, як наслідуються одни класи від інших (картинка знизу сторінки) - але тоді таких речей як Generic, Lambda Expression та Extension function взагалі ще не існувало. Окремі типи (разновідністі класів) обробляються вкрай спеціфічно, наприклад такі як Attributes, Delegate, Event, Exception, Nothing, AddressOf (а деякі особливі сущності такі як Namespace, Module взагалі не є обьектами-типами, хоча нагадують класи по сінтаксісу). Спеціфічні типи даних мають складні залежності між собою, наприклад будь-які Event's дуже просто замінити на Delegate, але наприклад щоб замінити AddressOF на Lamda Expression, треба спочатку добряче поломати голову. Існує також окреме питанні, яким чином можливо працювати у декількох потоках без зруйнування.

Деякі типи даних Visual Basic відповідають загальним типам даних .NET Framework (і мають єквівалент у C#), а деякі конструкції, такі як Module, або спеціфічні засоби наслідування (такі ік Shadow) або типи даних спеціфічні саме для Бейсіка (наприклад Nullable (of UInit32)) - неможливо запросто відтворити на інших мовах .NET (ось тут у мене зберігається повний опис спеціфіки Бейсіка щодо більш простих мов, таких як C# - VB-Csharp-Difference).

Все це непогано було б звести в одну багатомірну табличку, де по першій осі був би відкладений тип даних з повного набору, що взагалі існують у бейсіці, а по кожнієї інший осі була б одна характеристика типів даних - наприклад чи ці дані є єлементарними, чи побудовані як комбінація єлементарних, чи ці дані зберігаються по лінку (тобто змінюючи дані в одному місці, вона змінюється усюди), скільки пам'яті займає той чи інший тип даних, як можливо з'ясувати чи той чи інший тип даних конкретне значення, чи значення присвоюється при завантаженні програми з діску у пам'ять, чи пізніше, коли утворюєтсья єкземпляр класу, як можно конвертувати одні дані у інші, як з'ясувати тип ціх даних, яким чином можна порівнювати дані у межах того ж самого типу даних, та у з іншими типами даних. От тільки як цю табличку спроєктувати на двомірний папір чи єкран?

Таким чином ми розуміємо, що дані, з якими вміють працювати програми на Бейсіці - це цілий всесвіт, який неможливо запросто навіть огорнути зором. Тому пишутся лише окремі нотатки з якогось крихітного питання. У цьому топіку я опишу малесеньку, але дуже важливу крапочку цього всесвіту - а саме, як працювати з данними наприклад типа Nullable (of integer) та як їх пов'язати з Nothing та DbNull, які повертаються з SQL-бази.

Поперше, якщо ми бажаємо прийняти з SQL такі дані у свою програму, то є найспрстіший шаблон, як це можливо зробити з самими звичайними змінними взагалі без будь-яких складнощів.

Але у цьому засобі є велике питання. Наприклад неінеціалізована ціла змінна у пам'яті має значення НОЛЬ, якщо в базі NULL, а як ви розумієте, 'NULL' і '0' у SQL-базі - це далеко не теж саме. Щоб почути і обробити у програмі різницю між 'NULL' та '0' ми повинні десь зробити якусь об'яву з Nullable (Of xxx). Така змінна дозволить надати їй знчення Nothing або 0, і обробити в програмі це по різному.

Якщо ми використовуємо Linq-to-SQL, то таку обьяву зробить цей фреймворк самостійно, але, наприклад, якщо ми використовуємо більш старі засоби роботи з базою, наприклад DataSet/DataAdapter, то нам доведеться зробити таку об'яву у явному вігляді.

Але нажаль Nothing теж не однй й те ж саме, що NULL у базі (це залежить від фреймворка, за допомогою якого ви звертаєтесь до бази), до того ж писати ось так у кожній стрічці дуже незручно.

Тому хочеться щось зробити таке, що автоматично б обробляло NULL у базі, та у змінній залишався би Nothing якщо у цьому полі бази є NULL.

І тут раптово ми згадаємо про Extension-фунції, які дозволяють нам своїм кодом поширювати будь який класс існуючого фреймворку,головна вимога до порюванного класу лише одна - поширбвати можна лише статичну секцію класу, тобто додавати SHARED-методи, які працюють без акземплярів класу, а лише з тим статічним шаблоном класу, що спочатку роботи програми завантажується з діска.

Фраза дуже важка для порозуміння, але подивиться на скрін нище, оцей метод GetNullable я зробив сам і додав його до існуючого класу Data.DataRow.

Щоб порозумітися у цьому, і зробити таку Extension-функцію, потрібно твердо порозуміти такі речі.

Таким чином, нище ви можете побачити найпростішу з можливих, але дуже корисну Extension-функцію.


   1:  Imports Microsoft.VisualBasic
   2:   
   3:  Public Module Extension6
   4:   
   5:      <Runtime.CompilerServices.Extension()> _
   6:      Public Function GetNullable(Of T)(DataRow As Data.DataRow, FieldName As Object) As T
   7:          If Convert.IsDBNull(DataRow(FieldName)) Then
   8:              Return Nothing
   9:          Else
  10:              Return CType(DataRow(FieldName), T)
  11:          End If
  12:      End Function
  13:   
  14:  End Module

Це найпростіша з можливих Extension-функцій, згодом я став їх робити все більше й більше.


Цікаво, що не існує можливості зробити Extension-функцію до простих типів даних, тобто мені завжди є бажання додати до цього набора якись свої функції, але нажаль це повністю неможливо.

Зробити можливо лише ось так, тобто спочатку потрібно мати єкземпляр класу String, а його вже можливо поширювати своїми власними функціями.

У даному випадку це ось така моя функція.


   1:  Imports Microsoft.VisualBasic
   2:   
   3:  Public Module Extension5
   4:      <Runtime.CompilerServices.Extension()> _
   5:      Public Function GetRandomString(X As String, Len As Integer) As String
   6:          Dim RND As Random = New Random()
   7:          Const ARR1 As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
   8:          Return New String(Enumerable.Repeat(ARR1, Len).Select(Function(s) s(RND.Next(s.Length))).ToArray())
   9:      End Function
  10:  End Module




Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17
Link to this page: http://www.vb-net.com/Nullable-and-DBNull/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <MAIL ME>  <ABOUT ME>  < THANKS ME>