16.08.2009

asp .net development сервер

Как сделать так, чтобы asp .net development сервер создал веб-приложение в папке / а не в папке /{имя проекта} ?

Наиболее простой способ:

Step 1: Select the “Tools->External Tools” menu option in VS or Visual Web Developer. This will allow you to configure and add new menu items to your Tools menu.

Step 2: Click the “Add” button to add a new external tool menu item. Name it “WebServer on Port 8080” (or anything else you want).

Step 3: For the “Command” textbox setting enter this value: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.EXE (note: this points to the web-server that VS usually automatically runs).

Step 4: For the “Arguments” textbox setting enter this value: /port:8080 /path:$(ProjectDir)

Step 5: Select the “Use Output Window” checkbox (this will prevent the command-shell window from popping up.


Перевод:

Выбираем в меню visual studio "tools" - "external toools"

Создаем новый элемент "add", выбираем любое имя

Command: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.EXE (это путь к серверу в .net framework 2.0, для четвертого фреймворка он будет другой)

Arguments: /port:{выбранный нами порт} /path:{путь к каталогу с сайтом}

Выбрать use output window

Все, теперь можно выбирать в меню tools - наше выбранное имя

04.02.2009

Context.RewritePath и атрибут action тега form

Как известно для того чтобы использовать friendly-urls нужно в обработчике события BeginRequest производить вызов Context.RewritePath и соответственно переписывать url.
Например /help/default.aspx можно заменить на default.aspx?action=help
Но при этом возникает неприятность. При рендеринге формы в атрибут action будет записан уже новый url ( default.aspx?action=help в нашем случае ) Таким образом, при нажатии на любую кнопку пользователю будет возвращена страница уже без дружественного url

Существует множество способов изменить такое поведение.
Наиболее простой способ - перекрывать метод Render и в нем сначала переписывать значение action а потом вызывать метод базового класса.


protected override void Render(HtmlTextWriter writer)
{
Page.Form.Action = Request.RawUrl;
base.Render(writer);
}



Однако этот метод не очень гибкий, поскольку нужно либо перекрывать метод Render на каждой странице либо создавать наследника класса Page и использовать его.
Еще один подводный камень - свойство Action класса HtmlForm доступно только в .NET Framework 2.0 SP1. Без сервиспака оно недоступно.

Поэтому лучше использовать технологию Adapters.
Для этого нужно создать папку App_Browsers и в ней файл name.browser со следующим содержимым

<browsers>
<browser refid="Default">
<controladapters>
<adapter controltype="System.Web.UI.HtmlControls.HtmlForm" adaptertype="HtmlFormAdapter">
</adapter>
</controladapters>
</browser>
</browsers>

Остается написать класс HtmlFormAdapter


public class HtmlFormAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
base.Render(new HtmlFormWriter(writer));
}
private class HtmlFormWriter : HtmlTextWriter
{
public HtmlFormWriter(HtmlTextWriter writer)
: base(writer)
{
InnerWriter = writer.InnerWriter;
}
public HtmlFormWriter(TextWriter writer)
: base(writer)
{
InnerWriter = writer;
}
public override void WriteAttribute(string key, string value, bool fEncode)
{
if ( string.Equals(key, "action", StringComparison.CurrentCultureIgnoreCase) )
{
value = HttpContext.Current.Request.RawUrl;
}
base.WriteAttribute(key, value, fEncode);
}
}
}


Перекрываем метод WriteAttribute и подменяем аттрибут action

P.S. существуют и другие методы, о них возможно напишу позже

14.01.2009

Пейджинг в GridView с использованием get параметров

Пейджинг в GridView, как известно, производится с помощью параметров, сохраняемых во ViewState, таким образом при каждом обновлении делается POST-запрос.

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

Естественное решение проблемы - использование GET-параметров. В них можно передавать номер страницы и количество элементов на странице.

Создаем класс-наследник GridView


public class ContentList : GridView
{
}


Сделаем 2 публичных свойства чтоб манипулировать GET-параметрами (чтоб избежать возможных конфликтов)


public string PageParamName
{
get { return pageParamName; }
set
{
if ( string.Equals(pageSizeParamName, value,
StringComparison.CurrentCultureIgnoreCase) )
throw new ArgumentException("parameter names are equal");
pageParamName = value;
}
}
public string PageSizeParamName
{
get { return pageSizeParamName; }
set
{
if (string.Equals(pageParamName, value,
StringComparison.CurrentCultureIgnoreCase))
throw new ArgumentException("parameter names are equal");
pageSizeParamName = value;
}
}

private string pageParamName = "page"; //default values
private string pageSizeParamName = "pagesize";


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

Теперь перекрываем метод OnInit, в котором устанавливаем значение размера страницы и номера страницы. Здесь нужно учесть что в C# нумерация массивов начинается с нуля а нам желательно начинать нумерацию с единицы.

В итоге получим


protected override void OnInit(EventArgs e)
{
base.OnInit(e);

if (! string.IsNullOrEmpty(Context.Request.QueryString[PageSizeParamName]))
{
PageSize = Int32.Parse(Context.Request.QueryString[PageSizeParamName]);
}

if (!string.IsNullOrEmpty(Context.Request.QueryString[PageParamName]))
PageIndex = Int32.Parse(Context.Request.QueryString[PageParamName]) - 1;
}


Таким образом данные берутся из GET-параметров. При желании можно проверять, является ли параметр числом и, если не является, игнорировать его. Сделать это несложно.

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


private string createPagedPath(int page, int pagesize)
{
string param = string.Empty;

foreach ( string s in Context.Request.QueryString.AllKeys )
{
if (string.Equals(s, PageParamName,
StringComparison.CurrentCultureIgnoreCase) )
continue;
if (string.Equals(s, PageSizeParamName,
StringComparison.CurrentCultureIgnoreCase))
continue;
param += string.Format("&{0}={1}", s, Context.Request.QueryString[s]);
}

return string.Format("{0}?{1}={2}&{3}={4}{5}",
Context.Request.Path, PageParamName, page, PageSizeParamName, pagesize, param);
}

Как видно из кода, проверяем все GET-параметры и копируем из них только те, которые не относятся к нашим параметрам с именами PageParamName и PageSizeParamName. Затем берем Path запроса, добавляем к нему параметры пейджинга и исходные параметры, если таковые имеются в запросе.

P.S. Стоит обратить внимание на изменение свойств PageParamName и PageSizeParamName, желательно прописывать их прямо на странице .aspx и не изменять в коде, поскольку это может привести к труднолокализуемым ошибкам (либо вообще не менять, а оставить значения по умолчанию).

09.01.2009

sql management studio 2008

Установил себе наконец sql server 2008 (пока что express with advanced services).
Обнаружил, что наконец-то появился более-менее приличный intellisence в редактировании хранимых процедур.
Как и следовало в общем ожидать, радость продлилась недолго. Например, в нем не поддерживаются временные таблицы.
То есть код

INSERT * FROM table INTO #temp

и далее

SELECT * FROM #temp

вызывает ошибку unknown identifier #temp. Естественно, процедура компилируется. Так что хорошее начало но работать им видимо придется еще.