Thursday, August 25, 2005

"Integration with Visual Studio Develop php applications from within Visual Studio.Net 2003. This product lets you build, edit and deploy php applications using the familiar interface of the most popular IDE in the market. Contrary to other PHP IDE products, VS.Php leverages the familiar interface of Microsoft Visual Studio.Net 2003 allowing PHP developers leverage all the features Visual Studio.Net 2003 provides. No need to learn a new IDE interface or tools.
Working with php projects has never been easier, VS.Php provides rich project management features similar to ASP.Net projects. "

Подробнее читать здесь (+ там же есть ссылка для скачивания):

http://netz.ru/more.php?id=3939_0_1_20_M

posted @ 2:09 PM | Feedback (0)

Tuesday, August 09, 2005

Обращаясь к предыдущему посту (http://relib.com/blogs/rulez/archive/2005/07/29/2550.aspx) - недавно открыл еще одно удобное применение. Можно легко разделить логику обнаружения пути к файлу (в примере - к доп. файлу конфигурации). В IApplicationContext добавляется метод, а в его имплементации различные реализации (либо просто возвращает имя файла, либо Server.MapPath(имя файла) ). Вот так-то ;)

 

posted @ 4:40 PM | Feedback (0)

Friday, July 29, 2005

Аутентификация в веб-приложении или свой ApplicationContext

Когда пишешь веб-приложение, стандартная проблема, которая возникает - это как хранить и доставать свойства текущего зарегистрированного пользователя в различных уровнях приложения (см. мой пост http://relib.com/blogs/rulez/archive/2005/07/28/2517.aspx).

Так как для веб-приложений характерно хранить подобные данные в сессии, а для windows или тестов - в статичной переменной, то получается, что необходимо каким-либо образом разделить логику получения этих данных. Тут на помощь приходит механизм контекстов. В посте http://relib.com/blogs/rulez/archive/2005/07/28/2517.aspx были описаны различные уровни приложения. В этом посте будет описано, как организовать Application service layer.

Итак, нам понадобится: ASP.NET-проект (назовем его WebUI), проекты Class library - BusinessObjects, DAO, BusinessServices, ApplicationServices.

 

1 шаг - определим класс UserCredentials в BusinessObjects:

 

Public Class UserCredentials

 

    Private _userId As String

    Private _password As String

 

    Public Property UserId() As String

        Get

            Return _userId

        End Get

        Set(ByVal Value As String)

            _userId = Value

        End Set

    End Property

 

    Public Property Password() As String

        Get

            Return _password

        End Get

        Set(ByVal Value As String)

            _password = Value

        End Set

    End Property

End Class

 

Класс UserCredentials будет использоваться для хранения информации о пользователе. В нашем случае это просто логин и пароль, хотя сюда можно добавить и ФИО, адрес, e-mail и любую другую информацию.

 

2 шагв DAO нам понадобится класс UserDAO

 

Public Class UserDAO

 

    Public Shared Function GetUser(ByVal userId As String) As UserCredentials

        If userId = "Vitaly" Then

            Dim user As New UserCredentials

            user.UserId = "Vitaly"

            user.Password = "1"

 

            Return user

        End If

    End Function

 

    Public Shared Function CheckUser(ByVal userId As String, ByVal password As String) As Boolean

        If userId = "Vitaly" And password = "1" Then

            Return True

        Else

            Return False

        End If

    End Function

End Class

 

Вообще говоря, метод GetUser должен брать данные из базы, а CheckUser проверять логин и пароль по базе.

 

3 шаг – в Application Service нам понадобятся классы:

 

Public Interface IApplicationContext

 

    Function GetCurrentUser() As UserCredentials

 

    Function GetCurrentIdentity() As IIdentity

End Interface

Public Class ApplicationContextFactory

 

    Private Shared _context As IApplicationContext

 

    Private Sub New()

    End Sub

 

    Public Shared Property Context() As IApplicationContext

        Get

            Return _context

        End Get

        Set(ByVal Value As IApplicationContext)

            _context = Value

        End Set

    End Property

End Class

 

Это и есть классы, необходимые для контекста. Теперь пишем реализацию контекста в Web приложении:

 

Public Class WebApplicationContext

    Implements IApplicationContext

 

    Private _application As HttpApplication

 

    Public Property Application() As HttpApplication

        Get

            Return _application

        End Get

        Set(ByVal Value As HttpApplication)

            _application = Value

        End Set

    End Property

 

    Public Function GetCurrentIdentity() As IIdentity Implements IApplicationContext.GetCurrentIdentity

        Return _application.Context.User.Identity

    End Function

 

    Public Function GetCurrentUser() As UserCredentials Implements IApplicationContext.GetCurrentUser

        If Not (TypeOf _application.Context.Session("UserCredentials") Is UserCredentials) Then

            If _application.Context.User.Identity.Name <> String.Empty Then

                'set user to session

                _application.Context.Session("UserCredentials") = _

                    UserDAO.GetUser(_application.Context.User.Identity.Name)

            End If

        End If

 

        Return _application.Context.Session("UserCredentials")

    End Function

End Class

 

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

 

Public Class TestApplicationContext

    Implements IApplicationContext

 

    Public Function GetCurrentIdentity() As IIdentity Implements IApplicationContext.GetCurrentIdentity

        Return System.Security.Principal.WindowsIdentity.GetCurrent

    End Function

 

    Public Function GetCurrentUser() As UserCredentials Implements IApplicationContext.GetCurrentUser

        Dim user As New UserCredentials

        user.UserId = "TestUser"

        user.Password = "TestPassword"

 

        Return user

    End Function

End Class

 

Далее, чтобы установить контекст пишем свой модуль для веб-приложения и подключаем его в web.config:

<httpModules>

<add name="WebApplicationModule" type="AspNetCredentialsSampleUI.WebApplicationModule, WebUI " />

</httpModules>

 

Сам модуль:

 

Public Class WebApplicationModule

    Implements IHttpModule

 

    Private WithEvents _context As HttpApplication

 

    Public Sub Dispose() Implements IHttpModule.Dispose

        _context.Dispose()

    End Sub

 

    Public Sub Init(ByVal context As HttpApplication) Implements IHttpModule.Init

        _context = context

    End Sub

 

    Private Sub OnAcquireRequestState(ByVal sender As Object, ByVal e As EventArgs) Handles _context.AcquireRequestState

        Dim appContext As New WebApplicationContext

        appContext.Application = _context

 

        ApplicationContextFactory.Context = appContext

 

        _context.Context.Session("UserCredentials") = _

            UserDAO.GetUser(_context.Context.User.Identity.Name)

    End Sub

 

End Class

posted @ 1:27 PM | Feedback (1)

Thursday, July 28, 2005

Обычно, архитектура бизнес-приложения состоит из следующих слоев (tiers или уровней, layers):

1) Business objects (бизнес-объекты)

Они могут называться по-разному, джависты бы их назвали value objects, а другие бы сказали domain model. Вообще говоря, это какой-то набор взаимосвязанных классов (обычно на уровне parent\childs collection), каждый из которых состоит из свойств (set'теров и get'теров). По сути, это базовые объекты, которые будут использоваться в нашем приложении.

Например, это может быть класс UserInfo со свойствами LastName, FirstName, Age, UserId, Password и т.п.

Иногда, если в DAO (см. ниже) используется ORM-система типа NHibernate, к бизнес-объектам прилагаются еще файлики с описанием какое свойство бизнес-объекта какому полю в базе данных соответствует. Здесь, кстати (немного оффтопика), есть очень полезная утилита в Wilson OR Mapper - не сам маппер, а утилита к нему прилагающаяся, которая строит код для классов и XML-файлы.

2) DAO (DAL) - Уровень доступа к данным, Data Access Layer

Уровень данных может быть организован абсолютно по-разному.

Возможен вариант использования ORM (object relation mapping) систем типа NHibernate, который заметно облегчит написание кода для CQRUD (Creare, Query, Retrieve, Update, Delete) операций. ORM систем много, возможно в дальнейшем напишу какие-либо интересные их особенности.

Для упрощения доступа к базе данных можно использовать Microsoft Data Access Application Block. По сути, это набор классов-оберток для стандартных классов и их методов из .net framework, облегчающий написание кода для соединения с БД, построения запросов и получения данных. Код получается более легким и компактным.

В DAL обычно используются паттерны Factory (чтобы разделить логику доступа к различным базам данным в различные классы), а также Strategy (чтобы разделить логику для разных бизнес-объектов, например - восстановление бизнес-объекта из DataSet).

3) Services layer - уровень сервисов

Обычно состоит из 2-х частей - это Business Services и Application Services

a) Business servicess - уровни бизнес-логики

Оборачивает запросы к DAL, может использоваться как для обработки ошибок (выдавать удобоваримое Exception), так и подключать транзакции для нескольких запросов к DAL - например, при обновлении данных. К этому уровню обращается UI (user interface)

b) Application Service - уровень логики для приложения

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

Исправления, дополнения и замечания приветствуются!

posted @ 6:22 PM | Feedback (0)

Wednesday, July 20, 2005

В проекте возникла проблема: имеется куча бизнес-объектов, которые необходимо как-то отображать/редактировать/обновлять/добавлять. Возник вопрос как лучше всего организовать web-приложение в этом случае, чтобы писать по минимуму рутинного кода для привязки объекта к контролам.

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

 

Наш тестовый бизнес-объект:

 

Public Class TestBusinessObject

Private _id As String

Private _name As String

Private _city As String

Private _hasSomething As Boolean

 

Public Property Id() As String

Get

Return _id

End Get

Set(ByVal Value As String)

_id = Value

End Set

End Property

 

Public Property Name() As String

Get

Return _name

End Get

Set(ByVal Value As String)

_name = Value

End Set

End Property

 

Public Property City() As String

Get

Return _city

End Get

Set(ByVal Value As String)

_city = Value

End Set

End Property

 

Public Property HasSomething() As Boolean

Get

Return _hasSomething

End Get

Set(ByVal Value As Boolean)

_hasSomething = Value

End Set

End Property

End Class

 

Наш класс-помощник в деле bindinga (привязки данных):

 

Imports System.Reflection

 

Public Class BindingHelper

 

#Region "Binding object to controls"

 

    '''

    ''' This will bind object properties to control values

    '''

    ''' Object

    ''' Container (page usually)

    ''' Prefix of control name (this will be used if you need to bind several controls to the same page)

    Public Shared Sub BindObjectToControls(ByVal obj As Object, ByVal container As Control, ByVal propertyNamePrefix As String)

        'check object for nothing

        If obj Is Nothing Then

            Return

        End If

 

        'loop all obj properties

        For Each objProperty As PropertyInfo In obj.GetType.GetProperties()

            'look for control with specified property name

            Dim control As control = container.FindControl(propertyNamePrefix & objProperty.Name)

 

            If Not (control Is Nothing) Then

                SetPropertyValueToControl(control, objProperty.GetValue(obj, Nothing))

            End If

        Next

    End Sub

 

    '''

    ''' This will bind object properties to control values

    '''

    ''' Object

    ''' Container (page usually)

    Public Shared Sub BindObjectToControls(ByVal obj As Object, ByVal container As Control)

        BindObjectToControls(obj, container, String.Empty)

    End Sub

 

    'Bind property value to control

    Private Shared Sub SetPropertyValueToControl(ByVal control As Control, ByVal value As Object)

        If TypeOf control Is TextBox Then

            'process text box

            CType(control, TextBox).Text = value

 

        ElseIf TypeOf control Is DropDownList Then

            'process DropDownList

            Dim ddList As DropDownList = CType(control, DropDownList)

            Dim item As ListItem = ddList.Items.FindByValue(value)

 

            If item Is Nothing Then

                ddList.SelectedIndex = 0

            Else

                ddList.SelectedIndex = ddList.Items.IndexOf(item)

            End If

 

        ElseIf TypeOf control Is CheckBox Then

            'process CheckBox

            CType(control, CheckBox).Checked = value

 

        ElseIf TypeOf control Is HtmlInputHidden Then

            'process HtmlInputHidden

            CType(control, HtmlInputHidden).Value = value

 

        End If

    End Sub

 

#End Region

 

#Region "Binding controls to object"

 

    '''

    ''' This will bind control values to object properties

    '''

    ''' Object

    ''' Container (page usually)

    ''' Prefix of control name (this will be used if you need to bind several controls to the same page)

    Public Shared Sub BindControlsToObject(ByVal obj As Object, ByVal container As Control, ByVal propertyNamePrefix As String)

        'check object for nothing

        If obj Is Nothing Then

            Return

        End If

 

        'loop all obj properties

        For Each objProperty As PropertyInfo In obj.GetType.GetProperties()

            'look for control with specified property name

            Dim control As control = container.FindControl(propertyNamePrefix & objProperty.Name)

 

            If Not (control Is Nothing) Then

                objProperty.SetValue(obj, GetControlValue(control), Nothing)

            End If

        Next

    End Sub

 

    '''

    ''' This will bind control values to object properties

    '''

    ''' Object

    ''' Container (page usually)

    Public Shared Sub BindControlsToObject(ByVal obj As Object, ByVal container As Control)

        BindControlsToObject(obj, container, String.Empty)

    End Sub

 

    'Bind property value to control

    Private Shared Function GetControlValue(ByVal control As Control) As Object

        If TypeOf control Is TextBox Then

            'process text box

            Return CType(control, TextBox).Text

 

        ElseIf TypeOf control Is DropDownList Then

            'process DropDownList

            Return CType(control, DropDownList).SelectedValue

 

        ElseIf TypeOf control Is CheckBox Then

            'process CheckBox

            Return CType(control, CheckBox).Checked

 

        ElseIf TypeOf control Is HtmlInputHidden Then

            'process HtmlInputHidden

            Return CType(control, HtmlInputHidden).Value

 

        End If

    End Function

 

#End Region

 

End Class

 

Привязка в одну сторону (из объекта в контролы):

 

Dim testObject As New TestBusinessObject

testObject.Id = Guid.NewGuid().ToString

testObject.Name = "name"

testObject.City = "NY"

testObject.HasSomething = True

 

BindingHelper.BindObjectToControls(testObject, Page, "obj1_")

 

Привязка в другую сторону (из контрола в объект):

 

Dim testObject As New TestBusinessObject

BindingHelper.BindControlsToObject(testObject, Page, "obj1_")

 

Данная реализация имеет два ограничения:

1) названия контролов (их идентификаторы) должны совпадать с названиями свойств. Либо если мы используем префикс, то должны быть в формате ПРЕФИКСИМЯСВОЙСТВА, например obj1_Id;

2) сейчас обрабатываются только контролы типа TextBox, DropDownList, CheckBox и HtmlInputHidden, но это легко расширяется вами (возможно, вам понадобится поддержка Calendar или чего-то в этом духе).

 

Удобства: мы избегаем рутинной работы по заполнению соответствий между свойствами объекта и контролами в обе стороны - когда хотим отобразить объект или после PostBack.

posted @ 6:00 PM | Feedback (4)
 

На просторах интернета случайно наткнулся на интересное расширение для NUnit: http://nunitasp.sourceforge.net/

Смысл его в том, чтобы проверять ASP.NET страницы на правильное заполнение и т.п. Все предельно просто, так что рекомендую воспользоваться!

posted @ 5:55 PM | Feedback (0)

Tuesday, July 12, 2005

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

И тут же столкнулся с тем, что большинство приходящих не знает каких-то самых простых вещей. Кто-то не скажет что такое XSLT, кто-то не знает как в .NET объявить свой value type, кто-то не очень понимает разницу между INNER JOIN и OUTER JOIN, а кто-то и просто совсем ничего не знает. В последнем случае приходится выяснять чем же человек все-таки занимался на предыдущей работе, а заодно попутно выяснять понимание архитектуры приложения.

Смотрю как кандидат написал тест, если неправильно написаны какие-то простые вопросы пытаюсь выяснить: понял ли их кандидат, может быть знает?! Бывало и такое. В общем, максимум объективности.

И все равно, из 10 человек один более-менее устраивает меня по знаниям как моего будущего коллегу. А после меня в бой вступает американская сторона, логика которых вынесена за пределы рационального (сколько раз я с ними аргументированно пытался спорить как правильнее писать приложения, а им мои аргументы были до фени; в конечном счете, с 90% моих предложений все равно им приходится соглашаться, но обычно только через 2-3 недели после того как я им об этом сказал).

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

posted @ 4:46 PM | Feedback (7)

Wednesday, April 06, 2005

Проходил тест для американской компании. Там был вопрос - что это такое и в чем разница между доменами и потоками. А как бы вы ответили на этот вопрос?
posted @ 3:04 PM | Feedback (7)

Monday, March 21, 2005

Интересно было бы обсудить плюсы и минусы различных реализаций многоуровневого приложения. Пишите в комменты!
posted @ 5:35 PM | Feedback (2)
 
Сегодня успешно сдал экзамен по разработке распределенных приложений, получил сертификат MCAD.NET. Желаю и вам всего самого наилучшего :)
posted @ 5:33 PM | Feedback (2)