Про кэширование

Недавно писал про оптимизацию сайта для всяких спидтестов. Но все они бесполезны, если сам сайт работает медленно, а чтобы его ускорить нужно не только встроенное кэширование на уровне страниц - @OutputCache, но и кэширование на уровне объектов модели. Кэширование на уровне базы, всякие memcache актуально лишь когда есть куча FrontEnd'ов и они тоже генерят немало запросов несмотря на свое кэширование. Так вот, для кэширования объектов в asp.net делается простой класс, например:Далее...

Метки:   Категории:Code


asp.net optimization

В сети появился доклад Mad'a на конференции Mix11 в ЛасАнжелесе, смотреть тут. В нем рассказывается прежде всего как с помощью настройки asp.net сайта добиться показателей на тестах скорости YSLOW и PageSpeed близких в 100. Как нетрудно догадаться в результате получается сайт, когда браузер вообще не делает запрос к серверу, так как всюду проставлен Expired. Единственная сложность кроме всяких сжатий, была в уменьшении числа запросов к серверу. И когда эта проблема была решена было верно замечено, что если бы использовался CDN для статики, то такой проблемы бы и не возникло.

Метки:   Категории:Code


Эстонское сообщество русских разработчиков

Открыл для себя очень дружное сообщество эстонских русскоязычных разработчиков. Вот парочка просто замечательных докладов, которые всем рекомендую.

WTF Code.


БОТы и Автоматизация процессов - Александр Ястремский.


Метки:   Категории:Code


Microsoft.Office.Interop

Давно не выкладывал технических постов, а они, как показывает статистика, приносят наибольшее число просмотров через поисковики. Поэтому сегодня опишу общно и без детальных картинок проблематику взаимодействия с документами MS Office 2007 из веб-сайта при помощи поставляемых в комплекте библиотек-прослоек Office.Interop. Эта история интересна тем, что ее решение содержит кучу проблем, но нигде я не видел целостного описания их преодоления.
  • Во-первых, вам понадобится установить на сервер офис. Берем пиратскую энтерпрайс. Так как некоторые версии могут отказаться ставится на сервере через терминал (RDP).
  • После того как в программе сделали Reference, написали нужны код, скопировали на сервер у вас появится: Retrieving the COM class factory for component with CLSID {00024500-0000-0000-C000-000000000046} failed due to the following error: 80070005 Решение - запускаем DCOMCNFG, выставляем пользователю NetworkServices права на запуск компонентов MicrosoftOffice(Word/Excel) или для того пользователя от лица которого работает ApplicationPool.
  • Запускаем повторно, вылезает (0x800A03EC): Microsoft Office cannot open or save any more documents because there is not enough available memory or disk Решение - Создать системные папки
    C:\Windows\SysWOW64\config\systemprofile\Desktop (для x64) C:\Windows\System32\config\systemprofile\Desktop (для x86)
    и выставить права для NetworkServices на папку - systemprofile.
  • На этом квест не заканчивает. При повторном запуске программа выдаст (0x80028018): Old format or invalid type library. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD)) http://support.microsoft.com/kb/320369 Что говорит о разнице в локализации вашего кода и компонента. Решается переключением локализации вызывающего потока перед вызовов функций COM объекта
    System.Globalization.CultureInfo oldCI = System.Threading.Thread.CurrentThread.CurrentCulture; System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); //вызовы Office.Interop

Вот пожалуй все, однако, на win2003x64 IIS6 EN даже вышеописанное не помогло.

Метки:   Категории:Code


OpenSource stories

Последние месяцы было несколько громких событий с популярными проектами с открытым исходным кодом. Вероятно это связано с тем, что все начали подводить итоги года и остались не очень довольны:
1) Lucene.Net - в ноябре обратилась ко всему сообществу с заголовком needs your help (or it will die) и It got forked. Напомню, что это .Net реализация самой популярной поисковой библиотеки, которая также используется в твиттере. Суть проблемы, что Apache Software Foundation решили прибить проект, из-за того, что неактивно развивается.
2) OpenSocial - в декабре объявило, что им нужен .Net разработчик, а также открыто место в наблюдательном совете для всех желающих. Опять же из-за недостаточной активности сообщества.
3) GoogleWave - социальная сеть от гугла, ранее выложенная в опенсорс, не смогло собрать вокруг себя достаточного числа разработчиков, поэтому решала разместиться внутри еще живущего Apache Foundation сообщества. Теперь проект смело можно называть Apache Wave.

В догонку - количество пользователей ICQ упало на треть, сервера этой системы скоро перевезут в Москву, так как только тут остались пользователи.

Метки:   Категории:Analytics | Code


MSVS2010 + Resharper 5 + Hotkeys

Достаточно долго уже работаю в Visual Studio 2010, как уже писал - все нравится. Это нравится включает Resharper 5 с кряком(пароль на архив - yandex, так как глючный dr.web считает его вирусом) и список всех горячих клавиш . Пользуйтесь.

Метки:   Категории:Code


json for .net

Всегда не любил выдачу JSON за то, что она подразумевает обработку в JS на стороне клиента, а значит много гемора для программиста. Но оказывается существует уже и полноценное решение делающее JSON не сложнее обычной XML сериализации. Речь про newtonjson.dll, написанную на .Net и неплохо документированную. Отдельно порадовала возможность конвертации конечного листа дерева в произвольный тип путем простого вызова типа cursor["leafname"].Value<long>();. Вот бы все значения ячеек для датагридов тоже имели такой шаблонизатор, чтобы не прописывать каждый раз приведение типа.

Пример JSON сериализатора в .Net для результатов выдаваемых гуглом при поисковых запросах через AJAX API - можно найти в последних изменениях blogsapi

PS: отдельно отмечу, что готов взять программиста на частичную занятость для поддержания моих хобби проектов и развития некоторых идей.

Метки:   Категории:BlogsAPI | Csharp | Code


Twitter OAuth 2.0 by login-password

Сегодня расскажу как реализовывается работа приложения через OAuth, если пользователь передает просто логин и пароль. Прежде всего потребуется класс OAuthUtility, код которого есть в blogsapi. На первом этапе мы проверяем производили ли мы аутентификацию пользователя ранее. Если нет, то обращаемся к твиттеру с логином и паролем, соответствующему нашему приложению (tokens.ConsumerKey,tokens.ConsumerSecret). Получаем назад ключ(response.Token), по которому мы можем построить запрос (OAuthUtility.BuildAuthorizationUri(response.Token)) на получение уникальных логина и пароля для пользователя нашего приложения. Назад возвращается страница, на которой пользователю предлагается ввести логин и пароль, но так как мы не хотим тревожить пользователя, то вводим их за него, незабывая извлечь со страницы authenticity_token. Если логин и пароль правильны, то нам возвращается уникальный PIN, который пользователь должен потом передать нашему приложению, в качестве единоразового пароля для получения постоянного пароля. Опять же парсим страницу вручную, чтобы не тревожить пользователя. И в завершение делаем запрос OAuthUtility.GetAccessToken, чтобы по разовому паролю наше приложение получило постоянный пароль. И сохраняем результат, чтобы больше не повторять этих действий. В итоге наше приложение, чтобы сделать запрос от лица пользователя передает не логин и пароль, а аж 4 строчки, соответствующие 2м логинам и паролям от них. Основная выгода - безопастность, и то, что эти строчки намного сложнее чем первоначальные логин и пароль пользователя, что гарантирует сложность их подбора. Но с другой стороны, они не имеют срока давности, поэтому зная их с легкостью можно выполнить действия от лица пользователя-приложения, в отличии от куков или сессий для авторизованных клиентов сайтов. И общая схема, демонстрирующая весь ужас того, во что превращается простая ранее операция по выполнению действий при помощи логина и пароля: Код самой функции "залогинивания" в твиттер. Далее...

Метки:   Категории:Twitter | BlogsAPI | Code


Barrier .Net 4

Хорошая статья по новым фишкам в .Net 4 в области синхронизации потоков.

Метки:   Категории:Code


IIS7 perfomance

Результаты независимого сравнения производительности IIS6 и 7-ки в блоге у Мэда. От себя добавлю, что тоже заметил улучшения в производительности 7ки по сравнению с 6й. Единственный найденный минус у iis7 - это то, что нельзя просто пользоваться url_rewrite модулем, приходится доставлять специальную компоненту, встраиваемую в систему управления сервером.
Кроме того, где-то месяц назад я писал письмо в мастерхост с вопросом, когда же там наконец появится win2008, да iis7, типа я готов платить и все дела. Они правда ответили, что 7ка у них даже в планах не значится. Вот и получается, что мастерхост при их плюсах использует 64-битные машины, которые больше жрут памяти и старую iis6, которая тоже потребляет больше ресурсов. С этой точки зрения им действительно нет смысла что-то менять.
И сегодня четверг, яндекс опять в 11 утра перестал выдавать новые данные, так что не удивляйтесь качеству топа. Письмо им я уже отправил.

Метки:   Категории:topbot | Code


Facebook continue

То, что популярнейшая западная соц.сеть в наглую плюет на блогеров, оказывается кроме меня заметило еще несколько сотен человек, в связи с чем давно заведен топик. Но мы как всегда пойдем своим путем - ниже идет код, который логинится на Facebook и 10 раз нажимает кнопку обновить RSS, пока социальная сеть не сдастся и не импортирует новые записи из блога на вашу страничку. Вызываемой функции нужно лишь передать аккаунт и номер присвоенные вашему RSS, который можно получить из кода страницы. (Этот класс также будет включен в открытый BlogsAPI) Далее...

Метки:   Категории:Blogs | F*Connect | Code


deep-water.ru

Решением хостера сайт deep-water.ru остановлен. Причиной стало вот что, а также не желание с этим разбираться. Сайт конечно был скорее мертв, чем жив. Теперь весь функционал переезжает на topbloger.ru потихоньку, уверен хостинг от Агавы уже не позволит такого произвола.

Метки:   Категории:Livejournal | Code


Serialization.XmlChoiceIdentifierAttribute

Иногда появляется необходимость создать сервис, который в SOAP сериализации имеет на одном и том же месте то один тег, то другой в зависимости от состояния объекта. Однако, если сделать все как написано в документации, код примера идет ниже, то можно получить совсем не то, что хотелось бы. Класс для WCF, реализованный в виде: Далее...

Метки:   Категории:Code


.Net Copy Protection

Проблема быстрого роста торрентов не только в том, что защитить программное обеспечение от копирования невозможно, но еще и в том, что многие программисты даже не задумываются о защите, особенно, если пишешь небольшую программу, предназначенную для ограниченного числа пользователей. Многие думают, что защита копирования - это сложно, либо заранее считают, что их приложение не станет сверх-популярным, а многие даже и не задумываются, что могут заработать на создании простенького приложения. Удивительный факт в мире программистов заключается в том, что для того, чтобы создать популярное приложение, достаточно сделать более простой аналог со сложного коммерческого приложения, решая для пользователя хоть и маленькую, зато частую проблему.

Итак, в этом посте я расскажу про лучшую защиту для .Net программ, в том плане, что просто настроить, - про популярный DeployLX. Открыв их сайт вы обнаружите другую неприятную новость, что экспресс версия этого продукта стоит порядка 200у.е.,а стандарт так вообще под 1000у.е. Лично я бы не поскупился на 200у.е. за стандарт, и вы вероятно тоже хотите стандарт, так как только в нем есть удаленный мониторинг запусков программы. Но платить 1000у.е. - это для нас через чур. Поэтому вот полная бесплатная версия, правда устаревшая. Это выражается тем, что генератор ключей не пойдет на Vista, w7, w2k8, поэтому у меня установлена одна виртуальная машина с winXP. А также генератор работает только в 32х битной среде, но и это легко решается. Та библиотека, что должна идти вместе с вашим ПО и обеспечивать проверку лицензий можно взять после установки в папке redistributable и идет как Any CPU.

Ниже идет пример реализации компонента, от которого нужно наследовать контролы. При помещении их на страницу будет проверена лицензия и если вы в генераторе ключей задали удаленное отслеживание, то и обращение к вашему серверу лицензий для проверки валидности использования программы.
Copy Source | Copy HTML
  1. // Adding the following attribute enables Visual Studio.NET designer license embedding support
  2.     [LicenseProvider(typeof(SecureLicenseManager))]
  3.     public class ProtectedUserControl : UserControl
  4.     {
  5.         ///Компонент защиты
  6.         private SecureLicense _license;
  7.  
  8.         /// <summary>
  9.         /// Базовая функция, нужна ли защита?
  10.         /// Если не наш сервер, то нужна!
  11.         /// </summary>
  12.         protected virtual Boolean NeedProtection
  13.         {
  14.             get
  15.             {
  16.                 return !Request.Url.Host.Contains("localhost");
  17.             }
  18.         }
  19.  
  20.         /// <summary>
  21.         /// Поехала инициализация
  22.         /// </summary>
  23.         /// <param name="e"></param>
  24.         protected override void OnInit(EventArgs e)
  25.         {
  26.             base.OnInit(e);
  27.             if (NeedProtection)
  28.             {
  29.                 try
  30.                 {
  31.                     string keys_dir = Global.LicensePath;
  32.                     if(String.IsNullOrEmpty(keys_dir))
  33.                     {
  34.                         keys_dir = @"C:\Keys";
  35.                     }
  36.                     Exception e0 = null;
  37.                     foreach(string filename in Directory.GetFiles(keys_dir, "*.lic", SearchOption.TopDirectoryOnly))
  38.                     {
  39.                         LicenseFile file = new LicenseFile();
  40.                         file.Load(filename);
  41.                         if(file.Id != null)
  42.                         {
  43.                             LicenseValidationRequestInfo info = new LicenseValidationRequestInfo
  44.                                                                     {
  45.                                                                         LicenseFile = file,
  46.                                                                         SerialNumbers =
  47.                                                                             new[] {"DLXLENT-3TX1-2UQ8-87YR-647Y-16WS"}
  48.                                                                     };
  49.                             try
  50.                             {
  51.                                 _license = SecureLicenseManager.Validate(this, typeof (Components_Campaign), info);
  52.                                 //нашли рабочую лицензию
  53.                                 e0 = null;
  54.                                 break;
  55.                             }catch(NoLicenseException nle)
  56.                             {
  57.                                 //если соединились, но что-то на стороне лицензиара пошло не так, то плюем на это дело
  58.                                 //if(nle.Message.Contains("Cannot validate license at server."))
  59.                                 if(nle.Message.Contains("The license is still encrypted and the internal properties cannot be accessed"))
  60.                                 {
  61.                                     break;
  62.                                 }
  63.                                 e0 = nle;
  64.                             }
  65.                             catch(Exception e1)
  66.                             {
  67.                                 e0 = e1;
  68.                             }
  69.                         }
  70.                     }
  71.                     if(e0!=null)
  72.                     {
  73.                         throw e0;
  74.                     }
  75.                 }
  76.                 catch (Exception e1)
  77.                 {
  78.                     Global.Logger.Error("licence", e1);
  79.                     State.GlobalError = e1.Message;
  80.                     Response.Redirect("~/PurchaseLicense.aspx");
  81.                 }
  82.             }
  83.         }
  84.  
  85.         public override void Dispose()
  86.         {
  87.             if (_license != null)
  88.                 _license.Dispose();
  89.  
  90.             base.Dispose();
  91.         }
  92.  
  93.     }
И конечно же нужно привести возможный код сервера лицензий, работающий с Mysql, так как из документации вы не сможете получить его автоматически для mysql базы:
Copy Source | Copy HTML
  1. using System;
  2. using System.Data;
  3. using MySql.Data.MySqlClient;
  4. using System.Collections;
  5. using System.Web.Services;
  6. using DeployLX.Licensing.v3;
  7. using DeployLX.Licensing.Management.v3;
  8.  
  9. [WebService(
  10.     // WARNING: Do not change the Namespace
  11.     Namespace="http://www.xheo.com/licensing/v3_0",
  12.     Description="Implements a license server for the DeployLX\u00AE Licensing copy protection system. Additional info at http://www.deploylx.com.",
  13.     Name="DeployLX|Licensing\u00AE Server" )]
  14. public class MyLicenseServer : LicenseServer
  15. {
  16.     public MyLicenseServer( )
  17.     {
  18.         KeyFolder = Server.MapPath("~/Keys");
  19.         DeployLxSerialNumbers = new string[] { "DLXLENT-3TX1-2UQ8-87YR-647Y-16WS" };
  20.         Global.Logger.Info("License for " + Context.Request.ServerVariables["REMOTE_ADDR"]);
  21.     }
  22.  
  23.     private const string LicenseTableName = "License";
  24.     private const string ActivationTableName = "Activation";
  25.  
  26.     private string ConnectionString
  27.     {
  28.         get
  29.         {
  30.             return @"Server=servername;Database=license;User=username;Password=password;";
  31.         }
  32.     }
  33.  
  34.  #region Validate...
  35.     protected override bool Validate( ServerRequestContext context, bool autoValidate )
  36.     {
  37.         if( ! base.Validate( context, autoValidate ) )
  38.             return false;
  39.  
  40.  
  41.         // TODO: Add any custom validation logic
  42.  
  43.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  44.         {
  45.             conn.Open();
  46.             using (MySqlCommand cmd = new MySqlCommand(string.Concat("SELECT IsValid FROM ", LicenseTableName, " WHERE SerialNumber = @SerialNumber"), conn))
  47.             {
  48.                 string sn = context.SerialNumber;
  49.                 cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", sn) );
  50.  
  51.                 object obj = cmd.ExecuteScalar();
  52.                 if(int.Parse(obj.ToString())== 0)
  53.                     return false;
  54.             }
  55.         }
  56.             return true;
  57.     }
  58.     #endregion
  59.  
  60.  
  61.     #region Activation Support
  62.  
  63.     #region CanActivate...
  64.  
  65.     protected override bool CanActivate( ServerRequestContext context, ActivationLimit limit, ref ActivationProfile suggestedProfile )
  66.     {
  67.         bool result = false;
  68.  
  69.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  70.         {
  71.             conn.Open();
  72.             using (MySqlCommand cmd = new MySqlCommand(string.Concat("SELECT * FROM ", ActivationTableName, " WHERE SerialNumber = @SerialNumber"), conn))
  73.             {
  74.                 cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  75.  
  76.                 using( MySqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleResult ) )
  77.                 {
  78.                     if( reader == null || !reader.Read() )
  79.                     {
  80.                         result = true;
  81.                     }
  82.                     else
  83.                     {
  84.                         ArrayList results = new ArrayList();
  85.                         int matchingIdIndex = -1;
  86.  
  87.                         do
  88.                         {
  89.                             object[] values = new object[ 5 ];
  90.                             values[  0 ] = reader[ "SerialNumber" ];
  91.                             values[ 1 ] = reader[ "ReferenceId" ];
  92.                             values[ 2 ] = reader[ "ProfileHash" ];
  93.                             values[ 3 ] = reader[ "DateActivated" ];
  94.                             values[ 4 ] = reader[ "AllowNewMachine" ];
  95.                             if( (int)values[ 1 ] == suggestedProfile.ReferenceId )
  96.                                 matchingIdIndex = results.Count;
  97.                             results.Add( values );
  98.                         }
  99.                         while( reader.Read() );
  100.  
  101.                         if( matchingIdIndex != -1 )
  102.                         {
  103.                             object[] values = results[ matchingIdIndex ] as object[];
  104.                             result = ( values[ 4 ] != DBNull.Value && (bool)values[ 4 ] ) || CheckProfile( context, limit, values[ 2 ] as string, (DateTime)values[ 3 ], (int)values[ 1 ], out suggestedProfile );
  105.                         }
  106.                         else
  107.                         {
  108.                             result = true;
  109.                             return result;
  110.                         }
  111.  
  112.                         if( !result )
  113.                         {
  114.                             for( int ix =  0; ix < results.Count; ix++ )
  115.                             {
  116.                                 if( ix != matchingIdIndex )
  117.                                 {
  118.                                     object[] values = results[ ix ] as object[];
  119.                                     result = ( values[ 4 ] != DBNull.Value && (bool)values[ 4 ] ) || CheckProfile( context, limit, values[ 2 ] as string, (DateTime)values[ 3 ], (int)values[ 1 ], out suggestedProfile );
  120.                                     if( result )
  121.                                         break;
  122.                                 }
  123.                             }
  124.                         }
  125.  
  126.                     }
  127.                 }
  128.             }
  129.         }
  130.  
  131.         return result;
  132.     }
  133.     #endregion
  134.  
  135.     #region RecordActivation...
  136.     protected override void RecordActivation( ServerRequestContext context, ActivationLimit limit, ActivationProfile profile )
  137.     {
  138.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  139.         {
  140.             conn.Open();
  141.             using( MySqlTransaction transaction = conn.BeginTransaction() )
  142.             {
  143.                 using( MySqlCommand cmd = new MySqlCommand( String.Format(
  144. @"IF EXISTS ( SELECT * FROM {0} WHERE SerialNumber = @SerialNumber AND ReferenceId = @ReferenceId )
        UPDATE {0} SET ProfileHash = @ProfileHash, DateActivated = @DateActivated, AllowNewMachine = 0 WHERE SerialNumber = @SerialNumber AND ReferenceId = @ReferenceId
    ELSE
        INSERT INTO {0} ( SerialNumber, DateActivated, ProfileHash, ReferenceId, AllowNewMachine ) VALUES ( @SerialNumber, @DateActivated, @ProfileHash, @ReferenceId, 0 )", ActivationTableName), conn, transaction))
  145.                 {
  146.  
  147.                     cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  148.                     cmd.Parameters.Add( new MySqlParameter( "@ReferenceId", profile.ReferenceId ) );
  149.                     cmd.Parameters.Add( new MySqlParameter( "@ProfileHash", profile.Hash ) );
  150.                     cmd.Parameters.Add( new MySqlParameter( "@DateActivated", DateTime.UtcNow ) );
  151.                     // Additional ordered parameters since not all MySql providers support named parameters
  152.                     cmd.ExecuteNonQuery();
  153.  
  154.                 }
  155.  
  156.                 transaction.Commit();
  157.             }
  158.         }
  159.     }
  160.     #endregion
  161.  
  162.     #region CanDeactivate...
  163.     protected override bool CanDeactivate( ServerRequestContext context, ActivationLimit limit, DeactivationPhase phase, ref ActivationProfile suggestedProfile )
  164.     {
  165.         // Only allow deactivation if the license was previously activated and has not already
  166.         // been deactivated.
  167.         bool result = false;
  168.  
  169.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  170.         {
  171.             conn.Open();
  172.             using (MySqlCommand cmd = new MySqlCommand(string.Concat("SELECT * FROM ", ActivationTableName, " WHERE SerialNumber = @SerialNumber"), conn))
  173.             {
  174.                 cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  175.  
  176.                 using( MySqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleResult ) )
  177.                 {
  178.                     if( reader == null || !reader.Read() )
  179.                     {
  180.                         // Never activated so it cannot be deactivated
  181.                     }
  182.                     else
  183.                     {
  184.                         ArrayList results = new ArrayList();
  185.                         int matchingIdIndex = -1;
  186.  
  187.                         do
  188.                         {
  189.                             object[] values = new object[ 5 ];
  190.                             values[  0 ] = reader[ "SerialNumber" ];
  191.                             values[ 1 ] = reader[ "ReferenceId" ];
  192.                             values[ 2 ] = reader[ "ProfileHash" ];
  193.                             values[ 3 ] = reader[ "DateActivated" ];
  194.                             values[ 4 ] = reader[ "AllowNewMachine" ];
  195.                             if( (int)values[ 1 ] == suggestedProfile.ReferenceId )
  196.                                 matchingIdIndex = results.Count;
  197.                             results.Add( values );
  198.                         }
  199.                         while( reader.Read() );
  200.  
  201.                         if( matchingIdIndex != -1 )
  202.                         {
  203.                             object[] values = results[ matchingIdIndex ] as object[];
  204.                             result = (bool)values[ 4 ] == false;
  205.                         }
  206.                     }
  207.                 }
  208.             }
  209.         }
  210.  
  211.         return result;
  212.     }
  213.     #endregion
  214.  
  215.     #region RecordDeactivation...
  216.     protected override void RecordDeactivation( ServerRequestContext context, ActivationLimit limit, ActivationProfile profile )
  217.     {
  218.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  219.         {
  220.             conn.Open();
  221.             using( MySqlTransaction transaction = conn.BeginTransaction() )
  222.             {
  223.                 using( MySqlCommand cmd = new MySqlCommand( String.Format(
  224. @"UPDATE {0} SET AllowNewMachine = 1 WHERE SerialNumber = @SerialNumber AND ReferenceId = @ReferenceId", ActivationTableName), conn, transaction))
  225.                 {
  226.  
  227.                     cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  228.                     cmd.Parameters.Add( new MySqlParameter( "@ReferenceId", profile.ReferenceId ) );
  229.  
  230.                     cmd.ExecuteNonQuery();
  231.  
  232.                 }
  233.  
  234.                 transaction.Commit();
  235.             }
  236.         }
  237.     }
  238.     #endregion
  239.  
  240.     #endregion
  241.  
  242.     #region Extension & Subscription Support
  243.  
  244.     #region CanExtend...
  245.     protected override bool CanExtend( ServerRequestContext context, IExtendableLimit limit, out int extensionAmount )
  246.     {
  247.         // TODO: Determine if the limit can be extended.
  248.         extensionAmount = -1;
  249.         return false;
  250.     }
  251.     #endregion
  252.  
  253.     #region RecordExtension...
  254.     protected override void RecordExtension( ServerRequestContext context, IExtendableLimit limit )
  255.     {
  256.         // TODO: Record the extension
  257.     }
  258.     #endregion
  259.  
  260.     #endregion
  261.  
  262.  
  263.  
  264.     #region Register...
  265.     protected override bool Register( ServerRequestContext context, LicenseValuesDictionary registrationInfo, bool autoRegister )
  266.     {
  267.         if( ! base.Register( context, registrationInfo, autoRegister ) )
  268.             return false;
  269.             // TODO: Add any custom validation logic
  270.             using (MySqlConnection conn = new MySqlConnection(ConnectionString))
  271.             {
  272.                 conn.Open();
  273.                 using (MySqlTransaction transaction = conn.BeginTransaction())
  274.                 {
  275.                     string sn = context.SerialNumber;
  276.                     string xml = context.License.ToXmlString();
  277.                     if (autoRegister)
  278.                     {
  279.                         using (MySqlCommand cmd = new MySqlCommand(
  280.         String.Format(@"INSERT INTO {0} ( SerialNumber, LicenseXml, IsValid ) VALUES ( @SerialNumber, @LicenseXml, 1 ) ON DUPLICATE KEY UPDATE LicenseXml=@LicenseXml", LicenseTableName), conn, transaction))
  281.                         {
  282.                             cmd.Parameters.Add(new MySqlParameter("@SerialNumber", sn));
  283.                             cmd.Parameters.Add(new MySqlParameter("@LicenseXml", xml));
  284.  
  285.                             cmd.ExecuteNonQuery();
  286.                         }
  287.                     }
  288.                     else
  289.                     {
  290.                         using (MySqlCommand cmd = new MySqlCommand(
  291.                                     String.Format(@"INSERT INTO {0} ( SerialNumber, LicenseXml, IsValid ) VALUES ( @SerialNumber, @LicenseXml, 1 ) ON DUPLICATE KEY UPDATE LicenseXml=@LicenseXml", LicenseTableName), conn, transaction))
  292.                         {
  293.                             // Repeat parameters since not all MySql providers support named parameters
  294.                             cmd.Parameters.Add(new MySqlParameter("@SerialNumber", sn));
  295.                             cmd.Parameters.Add(new MySqlParameter("@LicenseXml", xml));
  296.  
  297.  
  298.                             cmd.ExecuteNonQuery();
  299.                         }
  300.                     }
  301.  
  302.                     transaction.Commit();
  303.                 }
  304.         }
  305.  
  306.         return true;
  307.     }
  308.     #endregion
  309.  
  310.     #region ProcessFailureReport...
  311.     protected override void ProcessFailureReport( string report, string machineProfileHash, Version reportFormat, LicenseValuesDictionary properties )
  312.     {
  313.         // TODO: Log the error
  314.         Global.Logger.Error(report);
  315.     }
  316.     #endregion
  317.  
  318. }

Метки:   Категории:Code


Tr.im RIP

Скончался один из первопроходцев сокращения ссылок в интернете - Tr.im. О его скорой смерти было известно давно, еще когда твиттер переключился по умолчанию с него на Bit.ly, но только на днях сервис окончательно перестал функционировать и опубликовал список некоторых других популярных сокращателей. По этому случаю обновил библиотеку BlogsAPI, добавив следующие классы сокращения:

Copy Source | Copy HTML
  1. [TestFixture]
  2. public class Shorteners
  3. {
  4.     private void RunTest(Shortener oServer)
  5.     {
  6.         String sRet = oServer.ConvertDataTo("http://s-c.me", ItemType.FullUrl, ItemType.ShortUrl);
  7.         Assert.AreEqual(false,String.IsNullOrEmpty(sRet));
  8.         Debug.WriteLine("Returns: " + sRet);
  9.         sRet = oServer.ConvertDataTo(sRet, ItemType.ShortUrl, ItemType.FullUrl);
  10.         if(!String.IsNullOrEmpty(sRet))
  11.         {
  12.             Debug.WriteLine("BackTrace: "+sRet);
  13.         }
  14.     }
  15.  
  16.     [Test]
  17.     public void Bitly()
  18.     {
  19.         RunTest(new BitLy());
  20.     }
  21.  
  22.     [Test]
  23.     public void Cligs()
  24.     {
  25.         RunTest(new Cligs());
  26.     }
  27.  
  28.  
  29.     [Test]
  30.     public void Isgd()
  31.     {
  32.         RunTest(new Isgd());
  33.     }
  34. }

Метки:   Категории:BlogsAPI | Code


icom - part 2

На вчерашней конференции I-community2010.ru читал доклад. Презентацию Доклада с моими же комментариями можно скачать и почитать. Тема: "Концепция единого программного интерфейса по взаимодействию с русскоязычными блог-платформами" была призвана рассказать всем, что на проблему программного взаимодействия сервисов в интернете по разному смотрят сами создатели соц.сети, пользователи и разработчики. Также я предлагаю объединить усилия разработчиков над открытым кодом, чтобы упростить друг другу жизнь и заставить создателей соц.сетей делать нормальные интерфейсы для базового функционала, который я описал.
К сожалению, глубину моей мысли оценили не многие, зато мой доклад сильно отличался от других "представителей компаний", которые пришли на конференцию только ради рекламы себя и своих продуктов. Особо вставляла центральная секция, где у докладчиков не было докладов и все время на экране крутилась реклама партнеров.

Из всего обсуждения про твиттер можно было вынести реплики #ded 'ka, который от лица всех слушателей задавал ведущим каверзные вопросы, в то время как остальным было лень пытаться сказать ведущим, что то, что они рассказывают уже не интересно и было озвучено ранее. Из полезного, это замечание, что число занесения в листы на твиттере более говорит о читаемости человека нежели его количество френдов. Да, это сейчас так, и норма в среднем 600 занесений в листы на 10 тыс. последователей. Но и это число при желании можно легко накрутить, поэтому очевидно, что самый главный показатель - число переходов со ссылки.

В заключение, на секции про правовые вопросы, было отмечено, что по российскому законодательству за сайт несет ответственность тот, кто хостит сайт, то есть каждый, кто ведет блог на стандалоне, обязан модерировать свои комментарии, иначе его и автора комментария вполне могут привлечь за какую-нить ерунду типа разжигание. Смешной прозвучала мысль устроить в рунете монархию по принципу, что весь контент принадлежит по умолчанию президенту, а он может делегировать права на него разным людям.

Метки:   Категории:Blogs | Code


CSS HighLight at s-c.me

Добавил фишку, которую у меня когда-то просили - подсветку файлов стилей CSS в виде html для вставки в посты. Подсветка кода как всегда находится на сайте SourceCode for Me. Ниже приведу пример подсвеченного файла стилей.Далее...

Метки:   Категории:S-c.me | Code


ie6 - never die - вопрос html верстки

Всегда знал, что из меня фиговый верстальщик, но никогда не думал, что удастся сделать сайт, который будет корректно отображаться лишь в самом лучшем браузере всех времен и народов - Internet Explorer'e 6. Речь идет о верстке страницы, где неправильно отображаются уголки у разноцветных кнопок. Если глянуть код, то все должно быть так как показывает ie6:


Но на практике современные браузеры показывают следующее:


Может кому не лень подсказать как исправить такое недоразумение?

Метки:   Категории:bugs | Code


Отлов утечки памяти

На днях долго и упорно занимался отловом утечки памяти в постоянно работающем win-сервисе. В помощь себе я установил профайлер памяти процессов от микрософта - CRL Profiler. Он рисует красивые картинки, но иногда и просто вылетает. В общем помощи от него не удалось получить. Далее из гугла начал вытаскивать другие профайлеры и остановился на memprofiler, который триальный только на 14 дней, а дальше придется платить. понравилась возможность делать снимки объектов в памяти в разное время и сравнивать их количество и объемы. Поиск кряка или лицензии не привели к успеху, а очень бы хотелось может у кого есть?

В целом ни один из профайлеров памяти мне не помог. Проблему локализировал только благодаря тому, что за время мониторинга вручную просмотрел код работы со всеми статическими классами программы.

Метки:   Категории:Code | life


Source Code Highlight

Очередная статья на хабре, опубликованная неизвестным программистом, про подсветку кода вынудила меня внести некоторые улучшения в мой проект s-c.me - по подсветке кода. Изменение касательно подсветки строковых констант. Эта, как оказывается, весьма непростая задача теперь решается еще более сложным регулярным выражением:
Copy Source | Copy HTML
get
            {
                return @"@(?:"".*?"")+|"".*?(?<![^\\](?:\\\\)*?\\)""|''|'.*?(?<![^\\](?:\\\\)*\\)'|\b\d+?(?=\b|$)%?|‘’|‘.*?(?!\\).’";
            }

Убедиться в том, что все нормально подсвечивается, можно на подготовленном тестовом коде по ссылке. Конечно улучшать можно и дальше, но пока и так сойдет.
ЗЫ: надо не забыть продлить регистрацию домена... блин, 800р за год в зоне .me - какой ужас...

Метки:   Категории:S-c.me | Code


Кто я?

Программист. Я слежу за блогосферой и знаю, как будет развиваться интернет. Когда у меня есть время я даже прилагаю для этого усилия. Подробнее

Последние комментарии

Не отображать

Topbot at FeedsBurner

Мои Твиты

Twitter февраля 19, 06:57
Диоксин угрожает планете! http://dlvr.it/QGwPKJ https://twitter.com/f1ashr/status/965480472095555584/photo/1

Twitter февраля 11, 18:36
@typographera @lustforcookies Но Москва на 70 лет моложе и была основана при Кутузове, когда начали железную дорогу прокладывать.

Twitter февраля 11, 17:24
@lustforcookies @typographera А ничего, что Коломна основана от силы 270 лет назад?

Twitter февраля 11, 14:07
Украина готовит на Евровидение песню с быками http://dlvr.it/QFw6Kb

Twitter февраля 9, 14:07
Чем белые отличаются от черных? http://dlvr.it/QFh4j8 https://twitter.com/f1ashr/status/961964675469426690/photo/1

Twitter февраля 6, 09:57
Победа свободы над здравым смыслом http://dlvr.it/QFD2S4 https://twitter.com/f1ashr/status/960814600802377731/photo/1

Twitter февраля 4, 15:32
В Твиттере начался обвал фолловеров http://dlvr.it/QDzJLg

Twitter февраля 3, 08:34
Суммарная капитализация криптовалют http://dlvr.it/QDqJyj

Twitter февраля 2, 10:52
Музыка про прилет Американцев на Марс http://dlvr.it/QDhYq7 https://twitter.com/f1ashr/status/959378893017726977/photo/1

Twitter января 28, 17:04
Разбор трилогии Матрицы (The Matrix Trilogy) http://dlvr.it/QD3h9W https://twitter.com/f1ashr/status/957660569674199040/photo/1

Twitter января 24, 13:20
Google Lunar X Prize - за 10 лет никто не смог запуститься на Луну http://dlvr.it/QCYSRW https://twitter.com/f1ashr/status/956154641753911298/photo/1

Twitter января 24, 04:03
Разбор сериала Туман или 4 всадника Апокалипсиса http://dlvr.it/QCVWXz https://twitter.com/f1ashr/status/956014586666287104/photo/1

Twitter января 23, 19:50
Пасха в Москве 2 сентября 2017 года http://dlvr.it/QCS7cD https://twitter.com/f1ashr/status/955890398899007488/photo/1

Twitter января 23, 19:50
Пользователи умудрились поломать гео-локацию в Инстаграм http://dlvr.it/QCS7hp

Twitter января 23, 16:29
В бюджете Америки появились лишние 2 трлн. долларов http://dlvr.it/QCQpPr

Twitter января 22, 14:45
Добродел обновился http://dlvr.it/QCGHwl https://twitter.com/f1ashr/status/955451259439407105/photo/1

Twitter января 22, 13:32
Рейтинг каналов и ботов Телеграмм http://dlvr.it/QCFt9v https://twitter.com/f1ashr/status/955432885657395200/photo/1

Twitter января 21, 16:15
Битва за Мосул в 360 с вертолета http://dlvr.it/QC84Xk https://twitter.com/f1ashr/status/955111520337248256/photo/1

Twitter января 21, 16:15
3 февраля стартует год огненного Петуха http://dlvr.it/QC84XQ

Twitter января 21, 16:15
Независимая Подмосковия http://dlvr.it/QC84X3

Мой твиттер

Копирайт

Все мысли, высказанные в блоге, являются моим мнением и за это мнение меня никто не забанит! Кроме того, никто не имеет право копировать материалы блога без использования ctrl+C/V!

© Copyright 2008