среда, 30 сентября 2015 г.

Про using или знаете ли вы что...

Наверняка, вы знаете что в C# есть такая конструкция, как using. И, скорее всего, в курсе, что использовать его можно и нужно только с объектами, реализующими интерфейс IDisposable (и, соответственно, скорее всего, что-то делающими с неуправляемыми ресурсами). На всякий случай, напомню, что конструкция вида:

 
using (someType obj = new someType())
{
         //тут что-то происходит
}
разворачивается компилятором вот в такую:

 
someType obj = new someType();
try
{
        //тут что-то происходит
}
finally
{
        //если объект значимого типа (value-type)
        ((IDisposable)obj).Dispose();

        //или, если объект ссылочного типа (reference-type)
        if (obj!=null)
           ((IDisposable)obj).Dispose();
}
а где же блок catch, спросите вы? Почему его нет? И почему не предусмотрен вариант using, где его можно задать?


среда, 25 марта 2015 г.

Как в .Net получить действительно случайные числа

Наверняка, всем известно о существовании в .Net класса System.Random, позволяющего получать якобы случайные числа практически без особых как умственных, так и временных затрат. Представьте что у нас есть такой вот метод, который чисто ради эксперимента мы вызовем в цикле несколько раз:

        
        static string GetRandomNum(int minValue, int maxValue)
        {
            Random rnd = new Random();
            return rnd.Next(minValue, maxValue).ToString();
        }

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(GetRandomNum(1,11));
        }


В данном случае вы получите совершенно одинаковые все "случайные" цифры.

Происходит так потому, что при инициализации объекта Random с использованием конструктора по умолчанию в качестве числа, использующегося для вычисления случайных чисел используется Environment.TickCount (количество миллисекунд, прошедших со времени старта системы), который обновляется раз в 15.6 миллисекунд.


четверг, 12 марта 2015 г.

Используем сессию в обработчиках (HTTP handlers) ASP.NET

Представьте себе гипотетическую ситуацию, когда вам нужно отдавать пользователю файлы, с, допустим, какими-то индивидуальными для каждого пользователя данными, причем именно отдавать уже готовые файлы, а не генерировать на лету. Самым простым и очевидным вариантом было бы создать обработчик, который будет вызываться для запросов вида "*_report.pdf", но для этого нам нужно понимать, можно ли отдавать пользователю файл, который он запрашивает или он принадлежит другому пользователю.
Передавать идентификатор в явном виде не только не правильно идеологически, но и просто не красиво, да и сохраненный файл, полученный по ссылке вида 10102_u323467_report.pdf?sessId=05446DA8F736B29A будет по умолчанию сохранен с таким же "кривым" именем.

Значительно проще положить этот идентификатор в сессию и получать его в обработчике уже оттуда. Для того, чтобы объект Session был доступен, нам нужно, чтобы обработчик наследовал не только от интерфейса IHttpHandler, но од одного из интерфейсов IRequiresSessionState или IReadOnlySessionState в зависимости от того нужен ли нам доступ только на чтение (IReadOnlySessionState) или на чтение и запись (IRequiresSessionState). На самом деле, записать в объект сессии вы сможете что угодно в любом случае, единственное отличие, что при использовании IReadOnlySessionState в конце обработки запроса данные сессии не сохраняются.


среда, 11 марта 2015 г.

Как сделать поля ввода с "подсказками" с помощью jQuery и CSS

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


В принципе, все современные браузеры, поддерживают стандарт HTML5 и, соответственно, атрибут placeholder, которой для этой цели и был придуман. Проблема в том, если вам необходима поддержка старых браузеры или чуть менее старые версии Internet Explorer (который начал поддерживать этот атрибут только в 10 версии), то подсказку созданную с помощью него они не увидят.
Если вы используете на своем сайте jQuery, то есть простое и быстрое решение для создания таких полей буквально несколькими строчками кода.  Прежде всего нужно создать стиль отображения "подсказок":

<style type="text/css">
    .helpField { background:white; }
    .blankHelpFieldText { color: #a1a1a1; font-style: italic; }
</style>


При этом, первый стиль несет в себе единственное практическое предназначение - быть "свойством" к которому будет привязываться скрипт отображения текста подсказки. Так что, вместо background можете написать что вам больше подходит, например width.


четверг, 5 февраля 2015 г.

Как победить ошибку "The SDDL string contains an invalid sid..." при установке SharePoint 2013

Я уже два раза при установке SharePoint Server 2013 сталкивался c возникновение странной ошибки "The SDDL string contains an invalid sid or a sid that cannot be translated" или "Строка SDDL содержит недопустимый sid либо sid, который не может быть преобразован." для русскоязычной версии. Выглядит момент появления ошибки вот так:


Как это часто периодически бывает с продуктами Microsoft текст ошибки никак не намекает ни то что на решение проблемы, но даже на то в какую сторону надо смотреть.


воскресенье, 11 января 2015 г.

Делаем локализацию сайта на ASP.NET MVC

Представьте, что вы решили делать сайт с использованием ASP.NET MVC. И, помимо всего прочего, вам на этом сайте вам нужна возможность отображения его на нескольких языках. Задача, хоть и кажущаяся с первого взгляда не самой тривиальной, на самом деле, достаточно проста. И я сейчас расскажу что для этого нужно сделать.


Во-первых, необходимо как-то передавать идентификатор языка. Можно, конечно, придумать какое-нибудь извращение, типа хранения текущего языка в сессии, но лучше всего просто передавать его в URL. Как минимум, в это случае, локализованными ссылками на ваш сайт можно будет без проблем делиться, хоть в соцсеятх, хоть пересылать их по e-mail. 
Для этого в вашем проекте открываем файл RouteConfig.cs, находящийся в папке App_Start и добавляем туда путь для локализованного  контента.
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Localization",
                url: "{lang}/{controller}/{action}/{id}",
                defaults: new { lang = "ru", controller = "Home", action = "Index", id = UrlParameter.Optional },
                //ограничение необходимо, чтобы отличить параметр языка от параметра контроллера. 
                //В данном случае, в качестве параметра lang подходят все двухсимвольные комбинации из букв латинского алфавита, например "en" или "fr"
                constraints: new { lang = @"[a-z]{2}" }
            );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }           
            );
        }

Теперь, если в URL содержится идентификатор языка, то он будет приходить нам в коллекции RequestContext.RouteData, и получить его проще всего в методе в методе Initialize контроллера. Давайте для простоты сделаем локализованную страничку авторизации, которая будет отображаться сразу же при входе на сайт, то есть по пути /.


понедельник, 15 декабря 2014 г.

О передаче параметров в C#

Давайте сразу начнем с кода. У нас есть вот такой код, давайте разберемся почему он выводит то, что выводит:

 
    class Program
    {
        static void Main(string[] args)
        {
            int a = 1;
            ProcessValue(a);
            Console.WriteLine(a.ToString());
            //здесь будет выведено 1

            TestClass test = new TestClass();
            test.val = 2;
            ProcessRef(test);
            Console.WriteLine(test.val.ToString());
            //здесь будет выведено 10

            TestClass test = new TestClass();
            test.val = 3;
            Process(test);
            Console.WriteLine(test.val.ToString());
            //а здесь будет выведено 3
        }

        static void ProcessValue(int par)
        {
            par = 3;
        }

        static void ProcessRef(TestClass par)
        {
            par.val = 10;
        }

        static void Process(TestClass par)
        {
            par = new TestClass();
            par.val = 100;
        }
    }
 

    public class TestClass
    {
        public int val;
    }


Вы, наверняка, знаете, что в .Net есть значимые (value) и ссылочные (reference) типы. Первые - размещаются полностью в стеке, а вторые размещаются в управляемой куче, а в стеке размещается только указатель на них.


воскресенье, 7 декабря 2014 г.

Обработка исключений при работе с WCF-сервисами

Наверняка вы сталкивались с ситуацией, когда в случае ошибки в WCF-сервисе нужно передать клиенту какие-либо данные либо, например, подробное описание что случилось. Очень часто встречаются реализации WCF- сервисов, использующие возвращаемые клиенту объекты с дополнительными, используемыми только в случае ошибки, полями, даже если реализация метода не требует возврата какого-либо результата клиенту. Так делают потому, что если просто выбросить исключение, то клиент не получит практически никакой информации о том, что случилось. Представьте, что у нас есть WCF сервис вот с таким простым контрактом и реализацией.


  
    //контракт
    [ServiceContract]
    public interface IDataService
    {        
        [OperationContract]        
        void TestError();
    }

    //реализация
    public void TestError()
    {
       throw new Exception("Error!");
    }


В если реализовать сервис именно так, то на клиент придет исключение, которое не дает никаких сведений о случившемся:

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


суббота, 6 декабря 2014 г.

Несколько мыслей о получении IP адреса клиента в ASP.NET

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

Итак, самый широко известный в мире ASP.NET способ получения IP-адреса клиента - это использование HttpContext.Current.Request.UserHostAddress, который, по сути является оберткой над HTTP заголовком REMOTE_ADDR, в которую записывается адрес хоста от которого серверу пришел запрос. А этот заголовок используется практически во всех языках программирования, для целей определения IP клиента. И везде не утихают споры правильно ли использовать именно его. Почему? Потому, что REMOTE_ADDR содержит адрес компьютера, установившего соединение с сервером. Во времена становления интернет и появления протокола HTTP этик компьютером почти наверняка являлся клиент, так что все было хорошо. Сейчас же, в большинстве случаев, в REMOTE_ADDR будет адрес прокси сервера внутренней сети (корпоративной, или домашней - смысла это не меняет) через который прошел запрос клиента.
И еще один пример, с которым, особенно часто сталкиваются Linux программисты, в результате криво настроенного nginx. ;)
Представьте себе ситуацию, что у вас есть криво настроенный балансировщик нагрузки. В таком случае в REMOTE_ADDR будет 10.10.0.2, что, естественно совершенно не клиентский адрес.



четверг, 27 ноября 2014 г.

Как получить IP-адрес клиента в WCF-сервисе.

Если вам нужно узнать с какого адреса приходит обращение к вашему WCF-сервису, то сделать это вы можете несколькими способами в зависимости от версии .Net Framework. Давайте по порядку.
В версии 3.0 (то есть, самая первая версия .Net, в которой стала доступна WCF) по заверениям Microsoft не существует гарантированного способа это сделать.  В принципе, если сервис хостится на IIS, то можно попробовать пару извращений, типа получения IP-адреса клиента из логов сервера (хотя встает вопрос как определять какая из записей лога соответствует текущему запросу). В общем, предлагаю придерживаться позиции Microsoft по этому вопросу ;)
В версии .Net 3.5 в WCF появляется класс  System.ServiceModel.OperationContext, так что подключаем сборку  System.ServiceModel и используем вот такой код, который и вернет на IP клиента: