Ласкаво просимо до dev.net.ua Увійти | Приєднатися | Допомога | Увійти Live ID
в Пошук

usarskyy

  • String vs FileInfo як аргумент методу

    Натрапив сьогодні на збірку лінків про всякі цікаві штуки (хочу сказати, автор справді знайшов цікаві теми). Одна з обговорюваних тем (прямий лінк) - як краще передавати шлях до файлу в метод: через FileInfo чи через string?

    Для тих хто не хоче перечитувати всі коментарі в темі на StackOverflow, постараюсь стисло передати всю їх суть:

    1. як "золоту середину" було запропоновано рішення з 2 методами один з яких приймає string, а інший FileInfo
    2. особливо оптимістично налаштовані вирішили, що використання Stream має вирішити  всі проблеми
    3. тільки один з учасників читає MSDN :) саме він і запостив, на мою думку, найбільш інформативний коментар ("The difference is primarily that there is a little bit of checking going on; the FileInfo constructor does some checking for a null or clearly invalid parameter...")

    Тепер декілька слів по кожному з пунктів:

    1. Створювати два методи (які будуть практично ідентичні!) тільки для того щоб всі залишились задоволеними - це якось тупо... і "попахує" добре відомою американською політкоректністю :)
    2. у випадку передачі Stream як аргумента без гострої на те необхідності - на кодревю зразу ж потрібно перевіряти справку від психіатра
    3. а от почитати MSDN + подивитись Reflector-ом всередину конструктора FileInfo і аргументувати його (не)використання - це досить-таки непогана ідея!

    І так, ось що можна побачити всередині конструктора:

    [
    code language="C#"]
            public FileInfo(string fileName)
            {
                if (fileName == null)
                {
                    throw new ArgumentNullException("fileName");
                }
                base.OriginalPath = fileName;
                string fullPathInternal = Path.GetFullPathInternal(fileName);
                new FileIOPermission(FileIOPermissionAccess.Read, new string[] {fullPathInternal}, false, false).Demand();
                this._name = Path.GetFileName(fileName);
                base.FullPath = fullPathInternal;
            }
    [/
    code]

    Коротке пояснення коду:

    • спочатку йде перевірка на null (до речі, в когось є ідеї чому "fileInfo" не перевіряється на пусту строку?) - це ясно
    • всередині "Path.GetFullPathInternal(fileName)" проходить валідація шляху до файлу + отримання повного шляху в разі, якщо "fileName" - це відносний шлях  (ага! а от перевірка на пусту строку :) )
    • перевірка прав на читання - на мою думку, одна з осносних причин чому варто використовувати FileInfo
    • виклик "Path.GetFileName(fileName)" просто перевіряє шлях до файлу на invalid-символи

    Як бачимо, нічого надзвичайного :)
    Переваги в порівнянні з методом який використовує
    string-аргумент очевидні:
    - "халявна" перевірка шляху на валідність
    - перевірка прав перед читанням (про це постійно забувають), а значить можна спростити код обробки помилок в процесі самого читання

    Недоліки:
    - виклик методу з
    kernel32 для отрмання повного шляху і перевірка прав доступу займає якийсь час, хоча не думаю що це критично

    FYI:
    Атрибути і вся інша інформація підгрузиться тільки в момент коли це справді необхідно, тому про додаткові витрати ресурсів хвилюватись не варто.


    Як на мене, я бачу більше "плюсів" при використанні
    FileInfo - додаткові халявні перевірки (в межах розумного) ніколи не бувають лишніми :)

    The end

    P.S.: Якщо хтось бажає додати "за/проти" кожного з методів, розказати байку про їх використання або просто висказати своє "+1" - прошу в коменти

  • Win7 for .NET developer

    Напевне, ні для кого не стане новиною те що МС вирішило роздати на халяву 2.5 млн копій бета версії Win7. І щось мені підказує, що багато з учасників dev.net.ua скористались можливістю скачати цю "халяву" :) Скачати - не проблема, а от чи варто її інсталювати..?
    "Гугл - наш друг" і по запиту "Windows7" видає "тонну" оглядів, які, всі як один, розписують яка ж хороша нова ОС! І ні один (принаймні, я так і не зміг знайти) не розказує якими граблями може получити по чолі чесний .NET девелопер :(
    Чесно кажучи, брак інформації по даному питанню мене трохи насторожив, але так як моя XPюша уже давно мріяла відправитись в "кращий світ", я все ж таки вирішив ризикнути... і от уже рівно тиждень мій лептоп працює з новою операційкою :)

    Не буду більше витрачати букви впусту і перейду до суті справи :)
    Позитивні сторони:
    1) швидкість + мінімальні потреби для роботи
    2) VS 2008 + SP1, IIS, TypeMock.NET, ReSharper 4.1 поставились без жодних проблем
    3) а чого ще треба для повного щастя? :)

    Негативні сторони:
    1) SQL Server 2005 SE проінсталювати не вдалось :(
    Прийшлось скачувати SQL Server 2008 with Tools (поставився без жодних проблем). А чому не SQL Server 2008 with Advanced Services запитаєте ви..? А тому що воно видає якусь незрозумілу помилку і навідріз відмовляється інсталюватись :(
    2) нажаль про SVN 1.5.* теж прийдеться забути. Прийдеться інсталювати щось з вітки 1.4.*
    3) трохи нервує нова політика безпеки і новий інтерфейс (думаю, ті хто довго працювали з вістою мали б бути звичні до цього)
    4) постійні апдейти! хоча, чого ж чекати від бета-версії..? :)

    І так, висновки :)
    Поки що враження в більшості позитивні і якщо хтось думає над тим, щоб теж проінсталювати Win7 - інсталюйте! (особливо я б рекомендував це для девелоперів, які працюють над десктопними рішеннями). Але памятайте, що термін бета-версії закінчується 1 серпня!

    UPDATE: VMWare Player заінсталювався без проблем. Gtk# 2.12.* - викидує помилку на початку інсталяції
    UPDATE 2 (10.02.09): хочеться додати ще декілька слів про Win7: 1) баг з msi-інсталлерами можна виправити так як мені підказали в коментах; 2) в процесі роботи було знайдено ще декілька дивних проблем :(
    Перша проблема - інколи при роботі курсор різко змінює своє положення без видимих причин. Звичайно, це не критично, але трохи діє на нерви...
    Друга - FF 3.0.6 часто починає "відгризати" 15-25% процесорного часу (хоча на WinXP SP2/Win2003 все працює чудово!).
  • Architects in Ukraine - code review

    Думаю уже всі встигли встигли прочитати новину про новий айтішний портал і протестити його функціонал, тому "копіпастити" в свій блог цю інформацію не буду. Якщо ще не встигли - "лінки вам в руки" :)

    Особисто від себе додам: все сподобалось! Як ідея, так і реалізація. Розробникам бажаю продовжувати розвивати ресурс з тим же ентузіазмом!

    Тепер коротко про те для чого я почав писати цей пост :)
    Коли прочитав "Файловое хранилище SkyDrive" аж в голові закрутилось :) Памятаю, читав що команда SkyDriv-a хотіла випустити API, але новин про реліз так і не було... а тут цілий ПОРТАЛ зберігає файли на майкрософтських серверах! ХАЛЯВНО! 5 Гігабайт!
    На жаль, після біглого перегляду коду вияснилось, що ніякого API портал не використовує... Натомість девелопери (Константин Косинский і Алена Косинская) цілком успішно використали уже наявні можливості (дивимось VideoListControl.ascx):
    </asp:XmlDataSource ID="fileListDataSource" runat="server" DataFile="<%$ AppSettings:FileList %>"
        XPath="/rss/channel/item" EnableCaching="true">

    Ключ "FileList" веде сюди.

    Залишилось придумати якби реалізувати автоматичне додавання файлів і можна запускати власний rapidshare :)
  • Kaspersky Antivirus SDK - міф чи реальність?

    На днях менеджерам моєї компанії захотілось чогось "великого і прекрасного", ну як завжди Big Smile Коротка суть "великої" ідеї: юзер аплоадить файл на сервер, який потім треба певним чином обробляти. Ніби нічого незвичайного... окрім того, що всі юзери "по дефолту" - погані і можуть загрузити якусь "бяку" Sad Під останнім розуміються дві речі: "небажаний контент" (порнографічні/расистські малюнки, ...) і віруси.
    На жаль, з першим можна боротись тільки людськими ресурсами... а от другий треба фільтрувати автоматично на етапі аплоаду.

    Не знаю хто як звик відловлювати віруси у себе на компі, але я довіряю свій дорогоцінний лептоп антивірусу AVG :) Майже так само думає і керівництво моєї компанії і для того щоб гарантувати "тотал секюріті" придбало N ліцензії Касперського (з таким вибором я не зовсім згідний, але моєї думки ніхто не питався).

    Думаю, всім зрозуміло що сканувати кожен файл що був завантажений на сервер вручну - це повний ідіотизм (тим більше, якщо в словниковому запасі є такі страшні слова як "Касперський" і "API" Big Smile)
    Після недовгих пошуків в Гуглі я знаходжу сторінку з описом Kaspersky Antivirus SDK... і НЕ знаходжу лінка на скачування цього SDK! Перше що подумалось - сьогодні не мій день! Але не все так погано і завжди можна мило поспілкуватись з саппортами :) Дзвоню... спілкуюсь...
    І що цікавого я дізнався? Перше, музика "на очікуванні" у них досить непогана, хоча постійно повторюється Tongue Tied (я це перевірив за ті 4 рази як дзвонив, в сумі "провисів" на ліній чуть більше пів години). Друге, саппорт уявлення не має що таке SDK і головне що існує Kaspersky Antivirus SDK (я не став їх шокувати історіями про інші SDK, які описані у них на сайті :) )! Третє, на російському сайті касперського про SDK нічого не згадується! (в кого є думки з цього приводу - прошу прокоментувати).
    В результаті, все що я отримав - це мейл саппорту SDK. (в цьому місці мій настрій суттєво покращився і я навіть вирішив, що сьогодні мені щастить, але...)

    Пишу лист в SDK-саппорт: доброго-дня-бла_бла_бла-треба-SDK-підкажіть-де-скачати. На моє щастя відповіли мені досить швидко, десь за 3-3.5 години, але замість довгоочікуваного лінка ввічливо поцікавились для чого він мені здався. Ну що ж, в скороченій формі пояснюю хто я, в якій компанії працюю і для чого мені потрібен SDK. Пройшло ще 3 години... Отримую ще одну відповідь... скажу чесно, я очікував будь що: від "а SDK можна скачувати тільки на Новий Рік і Піздво" Big Smile до "ми забули видалити наш першоквітневий жарт", але аж ніяк не "Я передам ваші контактні дані нашим менеджерам і вони з вами звяжуться"! При цьому варто зауважити, що спілкувався я не з простим девелопером, а з одним із керівників (принаймні в підписі мейлів у нього значиться такий статус)!

    За своє життя мені довелось скачати немало SDK-шок, але ще ні разу для цього не треба було витрачати стільки часу на спілкування з саппортами і тим більше розмовляти з менеджерами (в принципі, це мене чекає в найближчому майбутньому). Я взагалі мовчу про логічне питання "а для чого це все?". Таке враження, що керівництво компанії всіма силами старається запобігти інтеграції своїх продуктів з іншими системами (хоча в цьому випадку можна було б взагалі не згадувати про SDK). А відповідальні керівники бояться приймати хоча б якісь відповідальні(?) рішення... Повний "ахтунг"!

    І для чого я це все написав..? Головна мета посту - надати першу інформацію тим нещасним, хто захоче повторити мій шлях. Ну і запитати в шановних колег якщо хтось уже мав незабутній досвід спілкування з менеджерами/девелоперами Касперського - відпишіться, пліз, в коментах! Буду дуже вдячний за будь-яку інформацію Wink

    UPDATE 13.08.2008
    На наступний день після написання цього поста мені написав sales-менеджер Касперського (тут можна поставити 5+ за оперативність!). Він переслав документ який треба було підписати і відіслати назад, коротка суть документу - моя компанія не має права копіювати/продавати і т.д. Kaspersky AntiVirus SDK ну і т.д. Як результат, після узгодження всіх формальностей, я мав би отримати демо версію SDK.
    Так як пересилання мейлів/факсів ніколи не було моєю сильною стороною, я просто подзвонив sales-менеджеру і поцікавився скільки коштує повна версія AntiVirus SDK. На мій подив, він не зміг дати мені навіть приблизну відповідь (wtf?!). Основним аргументом неможливості сказати ціну було те, що вони не знають які зміни в SDK мають бути зроблені спеціально на моє замовлення (я так і не зрозумів чи вони для всіх "підганяють" пакет, чи це тільки я попав на спец. акцію :) ). Хоча я і був трохи шокований таким розвитком подій, але все-таки поцікавився скільки ж буде коштувати "стандартний" пакет... на що мене ввічливо запитали "а скільки користувачів буде перевіряти ваша система?". Скажу чесно, таке питання повністю відправило мене в нокдаун! Можна було б зрозуміти питання "а скільки серверів з нашим антивірусом ви маєте і на скільки з них буде залучено у вашому проекті?", але кількість користувачів - це повний "ахтунг"!
    Закінчивши розмову, я переслав лист з документом проджект менеджеру і попросив його розібратись в ситуації.
    До цього часу, ніяких новин від мого ПМ-а на рахунок Kaspersly SDK не надходило (я так підозрюю що і не надійде).


    P.S.: може хто знає, чи існує ще вітчизняний антивірус UNA? памятаю, колись про нього досить непогано відзивались і я навіть хотів купити ліцензуйну версію, але так і не зміг її знайти :(
  • Are you already Meshed?

    Не знаю хто як переклав "Meshed" і про що подумав, але мова зараз піде про платформу LiveMesh Wink

    Про LiveMesh я встиг начитатись всяких маркетингових "охів-ахів" і навіть зареєструвався в черзі на тестування... але 2 дні тому фортуна показала свою начищену до блиску "блендаметову" посмішу і завдяки добрим людям у мою Гмейловську скриньку "впало" запрошення на тестування "LiveMesh tech preview" :)
    Скажу чесно, після залогінювання на порталі я спочатку було витягнув свій губозакатувальний механізм і почав шукати якусь лінку з текстом "Download LiveMesh SDK" (що цілкому природньо для девелопера Geeked )... але круто обламався Tongue Tied Простим смертним треба чекати осіннього публічного релізу (тоді як деякі словочі програмери уже використовують SDK "на повну" і успішно викладають скрінкасти на цю тему!).

    Як тільки стало зрозуміло, що SDK мені не знайти, я почав освоюватись в користувацькому веб-інтерфейсі... Що тут можна сказати? Виглядає досить симпатично :) Звичний віндовський інтерфейс стилізований під Vist-у:

    Але це тільки мала частина всіх можливостей! Щоб відчути що таке LiveMesh насправді, потрібно додати свій ПК в "девайси" і визначити папки які будуть синхронізуватись:


    При додаванні ци самих девайсів не обійшлось без "ложки дьогтю" Sad Мій робочий ПК працює під ОС WinServer 2003, яка не входить у список ОС що підтримуються клієнтом LiveMesh (він є частиною процесу додавання нового девайсу і він же потім синхронізує папки/файли з сервером), тому він вперто показував ось таке симпатичне вікно:

    Після недовгих пошуків виявилось, що проблему вирішує параметр "-Force" при запуску інсталяційного пакету Smile Чому було просто не додати на форму кнопку "Install anyway" для мене так і залишилось загадкою.
    Після інсталяції і додавання свого робочого ПК в список девайсів клієнт працював стабільно, ніяких проблем помічено не було... Дивно, правда? Big Smile

    І так, синхронізувати файли між ПК і сервером - це круто... але якось тупо Confused Процес дуже скидається на банальний бекап. Тому було вирішено принести в жертву домашній лептоп і встановити LiveMesh клієнт ще й на нього.
    І тут [цензура] знову "лажа"!!! Клієнт навідріз відмовився інсталюватись на Win XP SP2 :( Знову до болю знайоме "-Force"... знову все працює ОК Smile

    Наступним пунктом в списку тестування стояло "розшарювання" папки... На цьому етапі не було ніяких сюрпризів, все пройшло без проблем.

    Тепер коротке резюме: у мене є 2 ПК з встановленим LiveMesh клієнтом, розшарена папка і велике бажання щось поламати :))
    Останнє вилилось у тестування функції "Live Remote Desktop" (яка, як сказано в маркетингових матеріалах, має працювати стабільно незважаючи на фаєрволи, NAT та інші страшні слова :) ).
    Для тестування "живого" RD я спершу законектився з робочого ПК на домашній лептоп. В загальному - враження позитивні, хоча відчувались невеликі затримки, яких ніколи не було при використанні звичного Remote Desktop.
    Окрім цього, попри всі нові корисні "features", які мають гарантувати ще більшу безпеку зєднання, я був радий побачити що копіювання файлів між 2 ПК через Remote Desktop працює і надалі :) Але кому воно потрібне, якщо у нас є папка з автоматичним синхронізуванням.?!

    Зізнатись чесно, до питання синхронізації файлів я підійшов з особливим ентузіазмом! Скопіювати 1-2 фільми було б найпростіше (в мому тестовому аккаунті доступно 5 Гб), але користі від них мало... тому я скопіював "trunk" SVN-a проекту CC.NET :) Процес синхронізації "лептоп -> LiveMesh Server -> робочий ПК" зайняв біля 5-10 хв (70 Мб).
    Результат доволі непоганий, АЛЕ як завжди не все так добре як здається. На даний момент у LiveMesh не реалізовано функції "diff", тому при зміні файлу клієнт синхронізує його ПОВНІСТЮ, що доволі неекономно з точки зору трафіку (для прикладу, в цьому проекті завдяки diff-у все працює набагато швидше). На додаток, при синхронізації кудись пропали всі "hidden" папки (у мому випадку це були всі ".svn"-папки).

    Після випробування функції "Live Remote Desktop" з робочого ПК підійшла черга до лептопа. У цьому місці треба зробити невеликий відступ і сказати, що робочі ПК в моїй компанії "заховані" за 2 чи 3 фаєрволами і ще якоюсь фігньою, яку я не зовсім розумію... Тому для забезпечення конекту своїх працівників до робочого місця компанія використовує софт третьої сторони, що створює тимчасовий VPN канал між робочим і домашнім ПК. На даний момент це єдиний спосіб доступу ззовні.
    Знову звичний інтерфейс клієнтської програми...

    Connect to device... УРА! я знову можу читати свою робочу пошту :) Хоча останнє трохи проблематично робити і ось чому саме: на роботі у мене два 21-дюймових монітори (Dual Monitor), в кожного з них "Screen resolution" більший ніж на мому лептопі... і тепер уявіть собі картину, коли цей Extended Desktop пробує втиснутись на маленькому (15.4 дюйми) лептопному дисплеї... уявили? Картина явно не для людей з слабкою психікою!
    Раніше, при використанні звичайного Remote Desktop-а, все працювало нормально: Screen resolution на робочому ПК змінювався, вікна з додаткового монітору "зїжали" на основний, що давало можливість нормально працювати... А зараз "хотіли зробити як краще, а получилось як завжди" :( Сподіваюсь, у фінальній версії все буде працювати як і має працювати :)

    Summary. Що тут можна сказати?
    1) Як для "tech preview" версії все працює досить непогано, будемо надіятись, що надалі все буде тільки краще :)
    2) В процесі роботи я все-таки побачив одне вікно помилки... нажаль все що там було написано це "Виникла помилка (а я і не помітив :) ). Номер помилки такий_то". Не знаю як хто, а я б хотів побачити більш інформативний текст :) Хоча це ще можна списати на версію продукту...
    3) Також хочеться думати, що в майбутньому Майкрософт перестане дразнитись скрінкастами на тему "як буде добре жити коли ми випустимо SDK" без надання можливості попробувати це SDK!

    Особисто від себе хочу додати: ЗОДОВБАЛО вікно допомоги! Я так і не знайшов спосіб, щоб його відключити :(


    Всім дякую за увагу :) Також буду радий почути про ваш досвід роботи з LiveMesh!
  • І знову про StyleCop

    З самого початку думав почати цей пост з слів типу "який я хороший, добрий, мудрий і т.д."... але прийдеться визнати, що я ППЦ який лінивий :))

    Як усім уже, мабудь, відомо Microsoft 23 травня цього року виклала в публічний доступ свій Source Analyzer (StyleCop). І все було б просто супер, якщо б не той факт, що всі правила перевірки коду - абсолютно статичні :( Щоб остаточно переконатись в цьому, в день релізу я трохи "погуглив" і натикнувся на цікавий пост, де один з кодерів майкрософта розказував які вони щасливі з своїм Source Analyzer-ом :) Також він писав, що правила у ньому статичні не через "криві руки "... а тому що тільки ці правила можуть принести абсолютне щастя девелоперам які будуть ними керуватись! Одним словом, логіка в його словах проста - "ми щасливі, і вам прийдеться бути щасливими" :)
    Так як характер у мене поганий, я погано сприймаю той факт що комусь роблять добре. Тому з допомогою Reflector-а я дизасемблював Source Analyzer і хотів створити свої правила. Проте, все що було після дизасемблювання йшло туго і постійно відкладувалось "на завтра" :( Як результат - фінішна пряма позаду, але перше місце дісталось іншому.

    Цього "іншого" і його статтю можна знайти тут. Що я можу сказати..? В пості добре описаний шлях створення custom rules з прикладами, всі хто хотів додати в StyleCop щось своє тепер мають таку можливість :) Думаю, в найближчому майбутньому можна сподіватись на появу Add-In-а який дозволить писати власні правила в конфігураційних файлах і створювати локальне (проте власне!) щастя :)

    P.S.: Також раджу зайти сюди. Автор цього поста пробує схрестити StyleCop з ReSharper-ом! І навіть пообіцяв викласти сорси в публічний доступ!
    Надіслане Wednesday, June 04, 2008 8:51 PM від usarskyy | 6 коментарів
    Помічено як: , ,
  • Перевірка параметрів у TypeMock.NET

    Не так давно Mike Chaliy опублікував пост про перевірку параметрів за допомогою Rhino.Mocks. Так як мені довелось трохи попрацювати з TypeMock.NET, то я поцікавився у автора чи не знає він як реалізувати подібну штуку там. Нажаль Mike Chaliy дав негативну відповідь :( тому прийшлось розбиратись самотужки.

    В результаті виявилось, що у TypeMock.NET це реалізовується набагато простіше:

        public class SomeClass
        {
            public int TestMethod(int x, string y)
            {
                throw new NotImplementedException("TestMethod(int, string) is not implemented");
            }
        }
        [TestFixture]
        [ClearMocks]
        public class Tests
        {
            [Test]
            [VerifyMocks]
            public void FirstTest()
            {
                SomeClass s = new SomeClass();
                using (RecordExpectations recorder = RecorderManager.StartRecording())
                {
                    recorder.ExpectAndReturn(s.TestMethod(1, "2"), 10).RepeatAlways();
                    recorder.CheckArguments(Check.Not(-1, "'x'-parameter can't be less than 'zero'"), Check.NotNull("'y'-parameter can't be null"));
                }
                Assert.AreEqual(10, s.TestMethod(1, "2"));
                Assert.AreEqual(10, s.TestMethod(-1, null));
               
            }
        }


    Все ніби і класно, але щось підказує, що має бути "ложка дьогтю"... і імя цій ложці - "ліцензія"! :( Іншими словами, код приведений вище буде працювати тільки після покупки Professional або Enterprise ліцензії. А "бідним" студентам прийдеться користуватись "стандартним" набором можливостей:

        [TestFixture]
        public class Tests
        {
            [Test]
            public void SecondTest()
            {
                Mock sc = MockManager.MockAll(typeof(SomeClass));
       
            sc.AlwaysReturn("TestMethod", 10).Args(Check.Not(-1,
    "'x'-parameter can't be less than 'zero'"),
    Check.NotNull("'y'-parameter can't be null"));
                SomeClass s = new SomeClass();
                Assert.AreEqual(10, s.TestMethod(1, "2"));
                Assert.AreEqual(10, s.TestMethod(-1, null));
            }
        }



    >> UPDATE

    Тут мене справедливо "ткнули носом" в той факт, що я пропустив важливе слово "Callback" в кінці теми минулого поста від Mike Chaliy. Зараз я постараюсь виправитись і для повноти картини додам ще один код тесту:

        [Test]
        public void ThirdTest()
        {
            Mock sc = MockManager.MockAll(typeof(SomeClass));
            SomeClass s = new SomeClass();
            sc.AlwaysReturn("TestMethod", 10).Args(
                                        new ParameterCheckerEx(delegate (ParameterCheckerEventArgs e)
                                        {
                                            if(((int)e.ArgumentValue) < 0)
                                            {
                                                e.FailureMessage = "First param can't be less then zero";
                                                return false;
                                            }
                                            return true;
                                        }),
                                        new ParameterCheckerEx(delegate (ParameterCheckerEventArgs e)
                                        {
                                            if (e.ArgumentValue == null)
                                            {
                                                e.FailureMessage = "Second param can't be null";
                                                return false;
                                            }
                                            return true;
                                        }));
           
            Assert.AreEqual(10, s.TestMethod(1, "2"));
            Assert.AreEqual(10, s.TestMethod(-1, null));
        }


    От тепер справді можна порівнювати :)
    Надіслане Thursday, May 22, 2008 3:08 PM від usarskyy | 3 коментарів
    Помічено як: ,
  • Тріал період у ... 0 днів!

    Всім алоха!

    Warning! Якщо ваші релігійні чи ще_якісь_там погляди не дозволяють користуватись такою чудовою тулзою як Reflector, прохання далі не читати! Так як в основі даного поста буде лежати саме його використання.

    Думаю, цей пост буде цікавий насамперед новачкам у .NET, тому прохання до "фулів" і "суперіорів" не писати коменти в стилі "ми таке в дитсадку робили".


    Частина перша: передісторія
    Недавно я вирішив додати до однієї з власних програмок трохи гламуру (під "власною" я розумію тулзу, яку використовую виключно я і тільки я). Конкретніше, мені знадобився красивий ListView Smile Гугл видав декілька результатів, але мій вибір впав саме на цей контрол.
    І хоча виробник не давав ліцензії "for non-commercial use", я все-одно вирішив викачати бібліотеку і протестувати її тріальну версію Smile

    Скачав... проінсталював... переніс контрол на форму... тут мені запропонували або купити контрол, або задовільнитись 30-денним тріальним періодом, я вибрав тріал (у нашому колгоспі поки не дають грошей на такі гламурні штуки Crying )... додав декілька колонок у ListView... натиснув F5... і тут мені повідомляють:



    Не знаю як хто, але я розраховував на трохи більший тріал-період Big Smile
    Повторний запуск не дав жодних позитивних результатів Tongue Tied


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

    Я більш ніж упевнений, що люди, які зробили скачаний мною контрол - професіонали своєї справи. І як професіонали вони вирішили скористатись послугами таких же професіоналів з захисту програмних продуктів.
    Часи коректування ключів реєстру з назвами IsRegistered давно минули, тому мені мало б світити повне фіаско у справі "крякання"... але, як кажуть, "проба грошей не коштує" Cool

    Отож, що ми маємо на цей момент?
    1) GUI-бібліотека, яка відмовляється працювати гарантовані 30 днів - 1шт.
    2) Велике бажання спробувати цю бібліотеку в дії - 1 ВЕЛИКА шт.
    3) Автор цього поста, юзверь який прочитав статтю "Reflector for dummiesGeeked - 1 шт.

    А що нам необхідно?
    1) Все вище перелічене (хоча, третій пункт прийдеться замінити Wink )
    2) Reflector і один чудовий Add-in до нього


    Зовсім випадково весь необхідний набір виявився у мене на вінчестері Yes Тому я переходжу до наступної частини!


    Частина друга: темні справи
    Відкриваємо папку, куди був встановлений наш GUI-компонент і дивимось які там є бібліотеки. У моєму випадку їх 3:
    1) LidorSystems.IntegralUI.DLL
    2) LidorSystems.IntegralUI.Lists.DLL
    3) LidorSystems.IntegralUI.Lists.ListView.dll

    Судячи з назв, декомпілювати їх прийдеться у перерахованому вище порядку...

    Відкриваємо Reflector, додаємо потрібні нам ліби і зберігаємо їхні "сорси" на хард-диск Smile Як зберегти сорси показано на малюнку нижче (не забудьте встановити скачаний Add-in!):



    В результаті маємо отримати 3 незалежні проекти.


    Наступний крок: створюємо WinForms-проект (для тестування нашого контрола) і додаємо до нього проекти декомпільованих бібліотек. Після цього потрібно не забути поправити референси в кожному з декомпільованих проектів один на одного, так як на даний момент всі референси вказують на бінарники, а нам потрібно щоб вони були "проектними" (project reference).

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

    Тому спробуємо додати ListView програмно (хоча достатньо буде просто створити екземпляр його класу):

            private void button1_Click(object sender, EventArgs e)
            {
                ListView testControl = new ListView();
                Controls.Add(testControl);
            }


    Запускаємо WinForms-проект і натискаємо на button1... На моніторі має зявитись вікно аналогічне першому малюнку. Ввічливо відмовляємось від купівлі компоненти і отримуємо exception в методі Init() класу LidorSystems.IntegralUI.Lists.ListView. Код, що викликав exception виглядає так:

    if (!base.DesignOnly && !this.Intialize(this))
                {
                    throw new Exception("Your evaluation has expired");
                }


    Думаю, логічно буде закоментувати цю частину коду Wink Після цього компонент можна вільно тестувати!
    А щоб все було взагалі ідеально, видаліть атрибут LicenseProvider(typeof(EncryptedLicenseProvider)) класу ListView.


    Заключна частина
    Як висновок можу сказати одне, платити гроші за всякі "хитрі" LicenseProvider-и для компонентів не має змісту. Всі вони легко обходяться і не становлять якусь надзвичайну міру захисту :( В найгіршому випадку, прийдеться витратити на пів години більше часу на видалення перевірки ліцензії Smile


    Памятайте, що використання "крякнутих" пограмних продуктів категорично заборонене! За таке можна "отримати по вухах" :)
    Надіслане Thursday, March 06, 2008 12:23 AM від usarskyy | 7 коментарів
    Помічено як: , ,
  • Програвання музичних файлів

    Арігато!

    Не так давно я зіткнувся з необхідністю програвання музичних файлів у своїй програмі. Пошук в Гуглі видав досить багато результатів, але всі вони зводились до написання врапера над DirectSound (або використання стандартних засобів .NET Framework, які і є тим самим врапером).

    Мені було абсолютно всерівно над чим там треба писати врапер (головне, щоб це працювало!), тому я швидко скопіпейстив текст класу у свії проект... і круто обломався! Динаміки навіть не пікнули Crying, а головне помилок при виконанні коду також не виникло. Тобто, все ніби працює, але в той же момент нічого не працює Tongue Tied

    Подальші пошуки проблеми дали нульовий результат, у всіх все було "тіп-топ". А якщо і не працювало, то тільки тому що хтось погано скопіпейстив код :( Метод "тику" також не приніс потрібного результату.
    Єдиним позитивним моментом в цій ситуації було те, що це потрібно для моєї власної тулзи, яку ніхто окрім мене не буде використовувати :)


    Пройшов приблизно місяць і сьогодні моя "звукова" проблема дістала продовження Big Smile Зовсім випадково я натикнувся на бібліотеку, що могла відтворювати більшість популярних аудіоформатів (принаймні, автори так написали).
    Спортивний інтерес взяв гору і я таки вирішив перевірити чи вона спрацює на мому лептопі. Як виявилось - "не спрацює" :( Але ж хіба я з індуського роду, щоб так швидко здаватись? Тому одразу ж вирішив скачати ще кілька ліб... які в результаті так само вперто відмовлялись працювати!
    Я вже думав було завершити свої блукання в "павутині", коли натрапив ось на цю сторінку. Чесно кажучи, я був налаштований досить скептично щодо цього продукту, так як він використовував той самий DirectSound :( Але "проба грошей не коштує" і можна було спробувати "покулупатись" ще кілька хвилин :)
    І, О ЧУДО! все запрацювало! без жодних "танців з бубном"!

    Якщо у когось виникали такі ж проблеми і ви знаєте ЧОМУ САМЕ вони виникали - відпишіться в коментах!
  • Маленька препарація SqlDataReader

    Якось давніше пощастило прочитати короткий мануал по правильному поводженню з SqlDataReader. Лінк зараз не згадаю, тому прийдеться кортко переказати зміст мануалу.

    1. Старайтесь якнайменше використовувати ось такий вид запису:
            using (IDataReader reader = SPs.TestSelect().GetReader())
        {
            for (int j = 0; reader.Read(); j++)
            {
                toReturn[j] = new TestItem();


                toReturn[j].Age      = (int)reader["Age"];
                toReturn[j].Gender   = reader["Gender"] as string;
                toReturn[j].ID       = (int)reader["ID"];
                toReturn[j].Login    = reader["LoginName"] as string;
                toReturn[j].Name     = reader["Name"] as string;
            }
        }


    2. Якщо настільки "приперло" написати код схожий на поданий вище, то модифікуйте його таким чином:
        using (IDataReader reader = SPs.TestSelect().GetReader())
        {
            int ageIndex      = reader.GetOrdinal("Age");
            int genderIndex   = reader.GetOrdinal("Gender");
            int idIndex       = reader.GetOrdinal("ID");
            int loginIndex    = reader.GetOrdinal("LoginName");
            int nameIndex     = reader.GetOrdinal("Name");

            for (int j = 0; reader.Read(); j++)
            {
                toReturn[j] = new TestItem();

                toReturn[j].Age      = (int)reader[ageIndex];
                toReturn[j].Gender   = reader[genderIndex] as string;
                toReturn[j].ID       = (int)reader[idIndex];
                toReturn[j].Login    = reader[loginIndex] as string;
                toReturn[j].Name     = reader[nameIndex] as string;
            }
        }

    3. А в ідеалі код має бути схожим на:
        using (IDataReader reader = SPs.TestSelect().GetReader())
        {
            int ageIndex      = reader.GetOrdinal("Age");
            int genderIndex   = reader.GetOrdinal("Gender");
            int idIndex       = reader.GetOrdinal("ID");
            int loginIndex    = reader.GetOrdinal("LoginName");
            int nameIndex     = reader.GetOrdinal("Name");

            for (int j = 0; reader.Read(); j++)
            {
                toReturn[j] = new TestItem();

                toReturn[j].Age      = reader.GetInt32(ageIndex);
                toReturn[j].Gender   = reader.GetString(genderIndex);
                toReturn[j].ID       = reader.GetInt32(idIndex);
                toReturn[j].Login    = reader.GetString(loginIndex);
                toReturn[j].Name     = reader.GetString(nameIndex);
            }
        }

    В прикладах toReturn – це «TestItem[] toReturn» + ви якимось чином дізнались правильний розмір масиву :) Результатом роботи методу є цей самий «TestItem[] toReturn»

     

    Перше що кидається в очі, чому 3-й варіант кращий за перші два – це unboxing при присвоєнні значень елементам масиву toReturn.
    А чим другий кращий за перший? Як саме третій варіант відрізняється від перших двох? Про це в мануалі не написали, тому прийшлось розбиратись самотужки…

    Звичайно, можна було б написати в саппорт майкрософта і запитати у них, але я вирішив, що з Reflector-ом я справлюсь швидше :)

    Хто до читав до цього місця і вирішив, що дана тема йому малоцікава, може далі не читати. Так як далі буде тільки дизасемблювання стандартних ліб…


    Отож, пройдемось по питаннях по порядку…
    1. Чим другий варіант кращий за перший?

    Відкриваємо у рефлекторі клас SqlDataReader і дивимось на тексти перегруженого оператора «[]».

    public override object this[string name]
    {

        get

        {

            return this.GetValue(this.GetOrdinal(name));

        }
    }

    та

    	public override object this[int i]
    {
    get
    {
    return this.GetValue(i);
    }

    }

     

    В першому лістінгу  бачимо, що перед викликом методу «GetValue» (який очевидно повертає потрібне нам значення :) ), йде виклик «GetOrdinal»… Відмінність ніби і не значна, але здоровий глузд підказує, що виконувати «GetOrdinal» лишні 200-300 тис разів не варто :) Тому від цього моменту конструкція типу reader["SomeKeyName"] перестає для нас існувати.


    2. Як саме третій варіант відрізняється від перших двох?

    Те що там відсутнє явне приведення типів – це ми бачимо, але що заховано всередині..? Для прикладу препаруємо метод «GetInt32»

    public override int GetInt32(int i)
    {

        this.ReadColumn(i);

        return this._dataIdea.Int32;
    }

    Звідси ясно, що нічого не ясно :) Треба копати глибше! Виклик методу «ReadColumn» я опущу і перейду до «return this._dataIdea.Int32». Цікаво, а що ж за поле  «_data»?

        private SqlBuffer[] _data;

    Хм… Все одно нічого не ясно… Дивимось що таке «SqlBuffer» (весь код постити не буду, просто покажу те що нам потрібне):

        internal sealed class SqlBuffer
        {

            // Fields

            private bool _isNull;
    private object _object;
    private StorageType _type;
    private
    Storage _value;
            // Methods
            // Properties
            internal object Value
    {
    get
    {
    if (this.IsNull)
    {
    return DBNull.Value;
    }
                           	switch (this._type)
    {
    case StorageType.Empty:
    return DBNull.Value;

    case StorageType.Boolean:
    return this.Boolean;

    case
    StorageType.Byte:
    return this.Byte;

    case StorageType.DateTime:
    return this.DateTime;

    case StorageType.Decimal:
    return this.Decimal;

    case StorageType.Double:
    return this.Double;

    case StorageType.Int16:
    return this.Int16;

    case StorageType.Int32:
    return this.Int32;

    case StorageType.Int64:
    return this.Int64;

    case StorageType.Money:
    return this.Decimal;

    case StorageType.Single:
    return this.Single;

    case
    StorageType.String:
    return this.String;

    case StorageType.SqlBinary:
    return this.ByteArray;

    case
    StorageType.SqlCachedBuffer:
    return ((SqlCachedBuffer) this._object).ToString();
     				case StorageType.SqlGuid:
    return this.Guid;

    case StorageType.SqlXml:
    {
    SqlXml xml = (SqlXml) this._object;
    return xml.Value;
    }
    }
    return null;
    }
    }
    	internal int Int32
    {
    get
    {
    this.ThrowIfNull();

    if (StorageType.Int32 == this._type)
    {
    return this._value._int32;
    }
    			return (int) this.Value;
    }

    set
    {
    this._value._int32 = value;
    this._type = StorageType.Int32;
    this._isNull = false;
    }
    }

     //Other properties
               

    	[StructLayout(LayoutKind.Explicit)]
    internal struct Storage
    {
    // Fields
    [FieldOffset(0)]
    internal bool _boolean;

    [FieldOffset(0)]
    internal byte _byte;
            	[FieldOffset(0)]
    internal SqlBuffer.DateTimeInfo _dateTimeInfo;
            	[FieldOffset(0)]
    internal double _double;
            	[FieldOffset(0)]
    internal short _int16;
            	[FieldOffset(0)]
    internal int _int32;
            	[FieldOffset(0)]
    internal long _int64;
            	[FieldOffset(0)]
    internal SqlBuffer.NumericInfo _numericInfo;
            	[FieldOffset(0)]
    internal float _single;
    }
        	internal enum StorageType
    {
    Empty,
    Boolean,
    Byte,
    DateTime,
    Decimal,
    Double,
    Int16,
    Int32,
    Int64,
    Money,
    Single,
    String,
    SqlBinary,
    SqlCachedBuffer,
    SqlGuid,
    SqlXml
    }

    }

    З приведеного вище коду видно, що значення потрібної нам табличної ячейки зберігається в одному елементі SqlBuffer. Для кожного типу даних SqlBuffer має окреме поле (див клас Storage). Отже, якщо кожен тип має своє поле, то ніякого приведення типів з object не відбувається!

    Натомість при виклику методу «GetValue» (див текст пергруженого оператора «[]») ми витягуємо значення з проперті «Value», її значення проходить boxing в object, а потім unboxing з object в потрібний нам тип. Про перфоменс в такому випадку говорити не приходиться…

    Варто зауважити, що клас Storage зберігає не усі типи в окремих змінних... Для прикладу, Storage не має поля для зберігання String-ових значень. Отримання потрібного значення там виглядає ось так:

    internal string String
    {
    get
    {
    this.ThrowIfNull();
    if (StorageType.String == this._type)
    {
    return (string) this._object;
    }
    if (StorageType.SqlCachedBuffer == this._type)
    {
    return ((SqlCachedBuffer) this._object).ToString();
    }
    return (string) this.Value;
    }
    }

    Тому, при витягуванні string-значення через GetValue()-метод ми отримуємо звязку з_object_в_string -> з_string_в_object -> знову_з_object_в_string! Повний ахтунг! І хоча реально платформа передає референси на обєкт (String - це ж все-таки референс тип), я вважаю, що 2 надлишкові операції - це 100% зайве!

    Законним є питання, а як SqlBuffer дізнається який тип даних він зберігає (і якому полю присвоїти прочитані з БД дані)? Це все відбувається на початку передачі даних з БД, коли клас отримує метадані у вигляді потоку байтів. Розписувати тут весь процес я не буду, так як це вимагає місця та часу.

     

    P.S.: Ніякого відкриття я звичайно ж не зробив… просто хотілось прояснити для себе які механізми ховаються за звичайними методами. Надіюсь, що комусь це також згодиться :)

    P.P.S.: прошу вибачення за форматування коду... у мене ніяк не виходить подружитись з вбудованим редактором :(

    Надіслане Sunday, December 02, 2007 4:55 PM від usarskyy | 2 коментарів
    Помічено як:
  • Трохи гумору...

    А ви кажете "паттерни", "методології", "інші_страшні_слова"... Тут пацани рІально рішають рІальні напряги: http://www.esnips.com/doc/ef658e05-212c-4819-987e-87dd2564d19b/SoftServe

    АХТУНГ! Контент важить 24 "метри" і містить ненормативну лексику!
  • Сторінковий перегляд даних

    Алоха!

    Мабудь у всіх поставала проблема реалізації почастинного перегляду даних, які зберігаються в БД. Стандартний DataGrid робить це настільки криво (моє ІМХО!), що використовувати цю функцію у ньому - майже смертельний гріх! Тому приходиться реалізовувати такий функціонал своїми руками :(

    І так, перша ідея, яка приходить в голову (і як показує практика, переходить в реалізацію) буде описана такими пунктами:
    1. Вибрати з потрібної нам таблиці PageNum*PageSize записів в якусь тимчасову таблицю
    2. Видалити з тимчасової таблиці перші (PageNum - 1)*PageSize записів
    3. Те що залишиться, видати як результат сторінки з номером PageNum

    Примітка: PageNum - номер сторінки, яку треба показати (нумерація сторінок починається з "1"); PageSize - кількість записів на сторінці

    В принципі, це все прекрасно працює... але на невеликих обємах даних! (до речі, 99% тестерів проводять тестування пейджингу на обємах таблиць у 1000-2000 записів, що дає прекрасні часові результати!)

    Тепер уявимо собі, що в таблиці є 500 000 записів і нам потрібно вибрати записи з 399 990 по 400 000... Результатом вибірки буде перевищення усіх можливих таймаутів :( Що ж нам робити?! Читати далі...

    Реалізація нового алгоритму описується наступними пунктами:
    1. Подивитись в Books Online що таке курсори
    2. Реалізувати механізм вибірки на їх основі :)

    Для тих хто не зрозумів (або кому не хочеться читати мануали) приводжу спрощений приклад:

        DECLARE temp_cursor SCROLL CURSOR FOR    
            select [ID]   
            from [Profile]
            where  [SomeCriteria] = 1
        declare @counter int
        declare @id   int
        OPEN temp_cursor         
        FETCH NEXT FROM temp_cursor    
        into @id           
        set @counter = 0      
        WHILE @@FETCH_STATUS = 0 AND @counter < 10
        BEGIN     
            insert into @temp ( [id] ) values ( @id )          
            set @counter = @counter + 1             
           
            FETCH NEXT FROM temp_cursor     
            into @id    
        END       
        CLOSE temp_cursor   
        DEALLOCATE temp_cursor     

        select    mp.[ID],     
                    mp.[LoginName]
        from [Profile] as mp    
        where mp.[ID] in ( select [id] from @temp )


    Для тих хто до цього часу вірить в те, що перший варіант реалізації покаже співрозмірні часові результати з реалізацією на курсорах - перевірте на 1 млн записів)))
    Для ти хто думає, що вибірка 10-20 записів з 1 млн ніколи не буде потрібна скажу, що такі системи існують і прекрасно функціонують)))


    UPDATE (27.11.2007)

    2 kosinsky:

    1. До цього часу ніяких проблем з курсорами не виникало... і суттєве зниження швидкодії теж не спостерігалось (можливо, через специфіку БД) + кращого рішення я поки що не бачив (можливо, тому що я не "DB guy" :) ).
    2. ДУЖЕ дякую за приведений приклад! Я знав, що MS має щось на зразок LIMIT в MySQL, але не знав як воно називається (або як грамотно реалізовується) :(
    3. А Ви впевнені що синтаксис правильний для MS SQL Server 2005? Я так і не зміг заставити його працювати (я дуже старався! чесно! :) )
    4. Нажаль, з третім дотнетом розібратись зможу не скоро... як завжди бракує часу. Проте як тільки випаде можливість - обовязково зверну увагу на LinqDataSource і DataPager

    2 Mike Chaliy:

    1. Я не помилився коли сказав DataGrid
    2. Подивіться що SqlDataSource має "віддати" DataGrid-у, щоб пейджинг запрацював
    3. Сказати "гівно" - це просто, а Ви спробуйте запропонувати свій варіант рішення проблеми :) як це зробив kosinsky

    Тепер результати тестування.

    Умови: 1) MS SQL Server Express Edition 2) 319854 строка в таблиці з якої робиться вибірка 3) результатом where-умови було 317851 строка 4) вибирались строки з 100 000 по 100 020 5) активність роботи з базою на час проведення експерименту == ~"0" 6) для справедливості експерименту запити запускались по 15 разів :)

    Так як заставити спрацювати конструкцію товариша kosinsk-ого я не зміг, то прийшлось її модифікувати: внутрішній селект робився в тимчасову таблицю в памяті, а зовнішній робився з цієї таблиці.
    Структура тимчасової таблиці: int, nvarchar(128), nvarchar(128), int

    Результатом роботи алгоритму на курсорах стали 3860 мілісекунд, для row_number() - 27640 :(

    По аналогії з LIMIT в MySQL я очікував побачити кардинально іншу картину, але факт залишається фактом!

    На закінчення, я б попросив kosinsky провести експеримент на власній базі :) І якщо це можливо, виконати це все на MS SQL Server 2008

    P.S.: В принципі, я припускаю, що виконання запиту на основі row_number() в такому вигляді як він був поданий в коментарі kosinsky може дати набагато кращі результати...


    Надіслане Monday, November 26, 2007 2:01 AM від usarskyy | 11 коментарів
    Помічено як:
  • SubSonic 2.0.3

    Недавно перетягнув один проект з версії сабсоніка 2.0.1 на 2.0.3 . Підтримка Nullable-типів - це те чого там справді не вистачало! (до тепер не розумію, чому цього не зробили раніше). Плюс покращена кодогенерація..! Одним словом, раджу всім переходити як найшвидше.

    Проте не обійшлося без ложки дьогтю :( Код, який працював до цього нормально, тепер викидував дивні помилки і кричав, що пул конекшинів вичерпано! Особливо це вдарило по роботі з MySQL (на проекті гібридна система зберігання даних, MS SQL + MySQL).

    Що змінилось у бібліотеці, я не досліджував, тому розказувати "як так можна було проїхати!" не буду :) Просто приведу код вирішення проблеми.

    І так, код, який працював в SubSonic 2.0.1:

            IDataReader reader = SPs.SomeMethod().GetReader();

    Перетворюється на:

                using (IDataReader reader = SPs.SomeMethod().GetReader())
                {
                    ...
                }


    В принципі, логічно було б робити юзінг зразу... але чомусь усе працювало гуд і ніколи не викидало жодної помилки, тому "навороти" опустились (за що в результаті і поплатився).


    Надіслане Thursday, November 22, 2007 7:34 PM від usarskyy | 3 коментарів
    Помічено як: ,