28 февраля 2011 г.

IsPostBack на страницах ASP.NET

У страниц ASP.NET есть полезное свойство IsPostBack, которое имеет тип bool. Оно показывает, является ли данная загрузка страницы "обратной отправкой" (PostBack)? Обратная отправка возникает, чаще всего, при нажатии какой-либо кнопки на странице.

Свойство IsPostBack удобно использовать при первичной установке каких-либо параметров страницы программным образом.

Допустим, у нас на странице есть элемент управления GridView с ID = gvEmployees, который содержит список работников. У этого контрола есть такое свойство как PageSize (размер страницы), т.е. количество строк на одной странице. Допустим, это свойство может изменять сам пользователь при помощи другого элемента управления, например, выпадающего списка DropDownList с ID = ddlPageSize. И пусть у этого выпадающего списка свойство AutoPostBack установлено в true, т.е. если пользователь выберет другой элемент в этом списке, это действие тут же вызовет обратную отправку страницы.

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

protected void Page_Load(object sender, EventArgs e)
{
        // если НЕ обратная отправка
if (!IsPostBack)
{
gvEmployees.PageSize = 20;
}
}

Если же это всё-таки обратная отправка, то в событии Page_Load относительно PageSize делать ничего не надо, т.к. данное свойство будет изменено обработчиком выпадающего списка.

protected void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e)
{
  gvEmployees.PageSize = (int)ddlPageSize.SelectedValue;
}

P.S. Хочу заметить, что у выпадающего списка свойство SelectedValue имеет тип string и должно быть преобразовано в int. Но лучше не делать преобразование так, как указано выше, а использовать для этого метод Int32.TryParse.


16 февраля 2011 г.

Объединение и разбиение строк: Join и Split

Тема довольно простая, но многие начинающие разработчики при решении задач объединения массива в строку и разбиения строки на части начинают изобретать велосипед.

Пример. Пусть откуда-то нам пришла строка, в которой слова разделены запятыми:

string Months = "январь, февраль, март, апрель, , май";

Стоит задача разбить эту строку на подстроки, выбрав в качестве разделителя запятую. Вот как она решается с помощью метода Split:

string[] MonthArray = Months.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);

Первый параметр метода представляет собой строковый массив разделителей, состоящий, как правило, из одного элемента (запятая с пробелом). Но если вы приглядитесь повнимательнее, то увидите две запятые, идущие подряд. И между ними ничего нет. Поэтому используется второй параметр, значение которого говорит о том, что, если при разделении будут пустые строки, то нужно их удалить.

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

Вот простое и элегантное решение с использованием стандартного метода Join:

string result = string.Join(", ", MonthArray);

Первый параметр - это строка разделителя (запятая с пробелом), второй - массив строк.

ASP.NET работа с профилем пользователя

Если в своём проекте на ASP.NET вы используете стандартную систему авторизации пользователей, то, возможно, вам требуется поддержка профилей для пользователей. В профиле можно указывать дополнительную информацию, например, номер ICQ или адрес сайта.

Получить доступ к значению любого свойства профиля можно по его имени:

ProfileBase pb = ProfileBase.Create(User.Identity.Name);
string FirstName = pb.GetPropertyValue("FirstName").ToString();

В первой строке создается объект профиля ProfileBase для текущего авторизованного пользователя. А во второй происходит доступ к значению этого свойства. Обратите внимание, что любое значение профиля имеет тип object, поэтому требуется явное приведение типа. Но в данном случае для получения строки можно воспользоваться методом ToString(), который предопределен для любого объекта по умолчанию.

Пример изменения значения одного из параметров профиля:

pb.SetPropertyValue("FirstName", FirstName);

Здесь, разумеется, никакого приведения не нужно, т.к. любой тип в .NET наследуется от object.

Теперь немного о конфигурировании. Для поддержки профилей в основном файле web.config должна быть следующая секция:

    <profile defaultProvider="AspNetSqlProfileProvider">
      <properties>
        <add name="Phone" />
        <add name="FirstName" />
        <add name="SubwayStation" />
      </properties>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/"/>
      </providers>
    </profile>

Сведения о полях, которые должен поддерживать профиль, содержатся внутри тэга <properties>. Чаще всего бывает достаточно указать только имя свойства. По умолчанию свойство сохраняется в БД в виде строки и имеет тип string.

Физически профили хранятся в БД в таблице aspnet_Profile. Все свойства профиля для одного пользователя хранятся в одной строке этой таблицы. Можно их хранить как в бинарном, так и в текстовом виде. Также большую часть объектов можно хранить в формате xml, например, тип DateTime.

Вот как следует конфигурировать свойство, чтобы хранить его в бинарном виде:

<add name="Phone" serializeAs="Binary" />

А вот как сохранить другой тип в формате xml:

<add name="BirthDate" type="DateTime" serializeAs="Xml" />

В таблице aspnet_Profile есть только два поля, которые непосредственно хранят значения свойств. Это PropertyValuesString и PropertyValuesBinary. В каждом из этих полей соответствующие данные профиля хранятся вместе. Для отделения одного значения от другого, а также указания имени свойства и в каком из двух полей оно хранится, используется поле PropertyNames. Значения в PropertyNames имеют следующий вид:

SubwayStation:S:0:20:FirstName:S:20:7:Phone:S:27:10:

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

Проверка на null в условии

Пара слов о проверке экземпляра класса на null. Типичный пример: пусть у нас в классе имеется коллекция, доступная всем методам внутри класса:

private List<string> Names;

Предполагается, что эта коллекция будет проинициализирована в конструкторе. Но что будет, если в конструкторе она не была проинициализирована, а в каком-нибудь методе будет проверяться какое-нибудь свойство этого объекта?

private bool CheckNames()
{
    // есть ли в коллекции хотя бы один элемент?
    if (Names.Count > 0)
        return true;
    return false;
}

Конечно же, произойдет исключение. Поэтому в том случае, если есть вероятность неинициализированной ссылки (а такая вероятность есть почти всегда!) - добавляйте в условие обязательно первым параметром проверку на null:

if (Names != null && Names.Count > 0)

Если будет обнаружена непроинициализированная ссылка, то остальные условия проверяться не будут. Таким образом, вы можете быть уверены, что в этом участке кода исключения NullReferenceExeption не возникнет.

TODO комментарии

Во время написания кода очень часто возникает ситуация, когда вы заметили какой-либо недочёт в коде, который не настолько критичен, чтобы править его прямо сейчас, но очень желательно исправить его в будущем. Чтобы потом о них не забыть, в таких местах можно оставлять комментарии, предваряя их токеном TODO с двоеточием:

// TODO: здесь требуется оптимизировать код

Тогда все комментарии, в начале которых стоит TODO, будут выводиться в одном окне Task List. Для отображения таких комментариев в выпадающем списке в этом окне выберите "Comments".

Комментарии из всех файлов проекта будут собираться в одном месте. Двойной клик на любой из строк в этом списке тут же переносит вас в соответствующее место в коде. Эдакий план разработки, если нет других более приоритетных задач.

Кроме TODO можно использовать и другие токены. Выберите в Visual Studio меню Tools - Options - Environment - Task List. Там вы увидите список всех доступных по умолчанию токенов, а также сможете добавить свои. Для каждого токена можно задать один из трёх приоритетов (имеет смысл при большом количестве комментариев): низкий (в списке будет отображаться как синяя стрелка вниз), средний (никакой пиктограммы не отображается) и высокий (красный восклицательный знак).

Хочу заметить, что подобный функционал поддерживает любая современная среда разработки, будь то Eclipse или IntelliJ Idea. Причём бывает даже не обязательно писать todo заглавными буквами и не требуется ставить двоеточие.

// todo исправить в ближайшее время

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

6 февраля 2011 г.

Отобразить целое число в двоичной форме

Если у нас имеется целое число, которое имеет значение не более 255, то его можно отобразить в двоичном (бинарном) виде при помощи класса BitArray:

BitArray ar = new BitArray(new byte[] { 31 });
foreach (bool b in ar)
{
   if (b)
      Console.Write("1");
   else
      Console.Write("0");
}

Массив бит представляет собой массив типа bool, поэтому если бит равен true, то отображаем 1. Ниже представлен результат выполнения:

11111000

Если же требуется отобразить целое число, состоящее из нескольких байт, то тут можно воспользоваться статическим классом BitConverter. Изменим первую строчку:

BitArray ar = new BitArray(BitConverter.GetBytes(uint.MaxValue));

В результате получим:

11111111111111111111111111111111

Преобразовать число в массив байт

В этом нам поможет статический класс BitConverter и его метод GetBytes(). Этот метод может принимать любой числовой тип: short, int, long, их беззнаковые аналоги, типы с плавающей запятой (float и double), а также логический тип.

Пусть у нас есть переменная одного из указанных выше типов с присвоенным ей значением. В качестве примера задается максимально возможное значение для данного типа:

int Number = int.MaxValue;

Преобразуем число в массив байт с помощью класса BitConverter:

byte[] bytes = BitConverter.GetBytes(Number);

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

string BytesInString = string.Join(" ", bytes);
Console.WriteLine(BytesInString);

На экране мы увидим, что массив состоит из четырёх элементов:


И это логично, если вспомнить, что тип int является 32-х битным, т.е. занимает в памяти 4 байта.

4 февраля 2011 г.

ASP.NET как узнать id текущего пользователя

Если в своём проекте на asp.net вы используете стандартную авторизацию, объекты БД для которой создаются утилитой aspnet_regsql, то получить id текущего пользователя можно так:

(Guid)Membership.GetUser().ProviderUserKey;

Обратите внимание, что если id в бд представлен типом uniqueidentifier и имеет вид "25E5E72F-07A1-4BD5-A1DF-F30887A9574F", то его следует явно приводить к типу Guid в коде на C#.

Как работать с таблицами БД из C#

Создайте таблицу в SQL Server. Откройте sql server management studio. В Object Explorer правой кнопкой на папке Tables - New Table. Откроется окно редактирования полей таблицы.

Если нет особых требований по типам данных, то для числовых используйте тип int, а для строковых nvarchar(N), где N - максимальное количество символов. Максимально N может быть равно 4000. Также можно указывать nvarchar(MAX).


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

У каждой таблицы хотя бы одно поле должно быть помечено как первичный ключ. Первичный ключ позволяет однозначно определить строку в таблице, поэтому его значения не могут повторяться. В качестве такого ключа выбирается отдельный столбец с типом int или с типом uniqueidentifier. Критерии выбора можно посмотреть в этой заметке. Первичный ключ отмечается пиктограммой в виде золотого ключа.

После указания первичного ключа сохраните структуру таблицы (Ctrl+S), здесь вам будет предложено ввести название для неё.

В Visual Studio добавьте в свой проект Add - new item - LINQ to SQL classes. Откроется пока ещё пустая диаграмма с расширением dbml. Если диаграмма dbml будет называться NordmineDatabase, то соответствующий ей класс автоматически получит название NordmineDatabaseDataContext - имейте это в виду, когда будете придумывать ей имя.


Откройте окно server explorer. В нём нужно добавить вашу таблицу. Выберите data connections - add connection. Там нажмите кнопку refresh и выберите из выпадающего списка имя компьютера, на котором установлен sql server. После нажатия на ОК в окне server explorer появится древовидная структура всех элементов вашей БД. Выберите нужную таблицу и просто перетащите её на диаграмму dbml. Нажмите F5 (запуск) или F6 (компиляция без запуска), чтобы последние изменения в диаграмме стали доступны.


Теперь вы можете работать с этой таблицей при помощи linq из любого метода. Пример:
NordmineDatabaseDataContext context = new NordmineDatabaseDataContext();
List<Sale> Sales = (from s in context.Sales select s).ToList();
Здесь из таблицы sales выбираются все строки. На выходе получаем коллекцию из элементов Sale. При этом объект context.Sales и класс Sale со всеми членами были созданы автоматически.

Здесь из таблицы sales выбираются все строки. На выходе получаем коллекцию из элементов Sale. При этом объект context.Sales и класс Sale со всеми членами были созданы автоматически.

Разница между int и uniqueidentifier в sql server

Эти два типа рекомендуется использовать в качестве первичных ключей в БД. Чем же они отличаются?

Тип int в sql server соответствует типу Int32 в платформе .NEТ. id новой записи генерируется автоматически путём увеличения на 1 id предыдущей записи, если для этого поля указано IDENTITY(1,1). Следовательно, id новой записи становится известно только после добавления её в таблицу.

Тип uniqueidentifier соответствует типу Guid в платформе .NET (пример: "C59498EC-5E38-41BF-9160-1B86449E02CA"). Особенностью этого типа данных является то, что статистически невозможно существование двух одинаковых id в любой информационной системе. В .NET генерируется методом Guid.NewGuid(). Отсюда следует, что id новой записи становится известным ещё перед сохранением в БД.

Выводы. Используйте uniqueidentifier, если не предполагается показывать id пользователю, т.к. он сложнее для восприятия, но удобнее при разработке. Если же нужно показывать пользователю id, например, в адресной строке браузера, то используйте целочисленный тип, но для того, чтобы узнать id новой записи, придётся выполнить запрос в БД.