RIA Services at fullsix.pt
Monday, July 27th, 2009Autor: goncalo.chaves
Intro
Com o lançamento da versão 3 do Silverlight, as novidades e funcionalidades quem têm sido desenvolvidas neste produto, aproximam-se cada vez mais de uma melhor experiência de utilização (UX) para o comportamento da aplicação, mas não se esquece a camada de negócio e a camada de serviços disponibilizados para dar “alimento” às novas “RIAs” (Rich Internet Applications) que vão aparecendo, cada vez mais na web.
Uma aplicação RIA tem como arquitectura a separação entre a lógica da apresentação, e a lógica dos serviços, como demonstra a seguinte imagem:
É neste contexto que a Microsoft nos apresenta a nova “Framework” .net RIA Services. E resumidamente podemos dizer que se tratam de um conjunto de bibliotecas adicionais a uma qualquer aplicação desenvolvida com base na .net Framework para a Web, neste caso o Silverlight.
Assim é complementado sobre o conjunto de ofertas da Microsoft, para o desenvolvimento das aplicações Web.
Em .net clients, aparecem o Silverlight e o WPF, para que o Silverlight possa disponibilizar o modelo RIA acima, é então disponibilizada a .net Ria Services Framework. Com um roadmap bastante curto, está neste momento em versão de CTP (community technology preview), versão disponível para as comunidades, onde se obtém feedback sobre as funcionalidades actuais e sobre as desejadas, July 2009 preview CTP é a versão actual. Está previsto o lançamento da versão beta durante a conferência anual PDC, e a sua versão final será conjunta com a versão 4 da .NET Framework.
O nosso objectivo principal, é demonstrar as capacidades desta Framework, aplicadas a um contexto real de uma aplicação. Para tal vamos abordar alguns dos tópicos mais importantes, aplicados a uma demo disponível em: http://labs.fullsix.pt/f6riademo
Instalação
Para utilizar a .net Ria Services Framework versão: July 2009 CTP é necessário seguir os seguintes passos:
1) Remover instalações prévias caso existam, sejam elas versões anteriores a:
Silverlight 3 SDK RTW
Silverlight 3 Plugin RTW
Silverlight 3 toos para o Visual Studio 2008 RTW
2) Instalar a .NET framework 3.5 SP1 e seguidamente o SP1 para o Visual Studio 2008
http://msdn.microsoft.com/en-us/vstudio/cc533448.aspx
3) A partir do site www.silverlight.net instalar:
a. Silverlight 3 RTW
b. Silverlight 3 SDK RTW
c. Silverlight 3 tools para o Visual Studio 2008 SP1
4) Instalar a .NET Ria Services: , July 2009 preview CTP
Para verificar que tudo correu como esperado, no Visual Studio 2008 no menu de novo projecto deverá estar como na seguinte imagem:
Silverlight Application – Aplicação normal em Silverlight, tal como na versão anterior.
Silverlight Navigation Application – Aplicação Silverlight, com a utilização “out of the box” das funcionalidades de navegação integradas com o browser, acima descritas.
Silverlight Class Library – Criação de bibliotecas usuais para aplicações silverlight, também semelhante á versão anterior.
Silverlight Business Application – criação de aplicações Silverlight sobre a .NET Ria Services Framework, é este o template que gera a estrutura ideal para a utilização das funcionalidades de negócio que a Framework Ria services nos dá. Acima já descrita.
Utilizando a framework
Acrescentando à nossa aplicação esta Framework, disponibiliza imediatamente e totalmente “out of the box” serviços como:
- Acesso a dados facilitando por ligação directa entre a Entidade de dados e o controlo na aplicação. Exemplo: ligação de uma tabela em SQL com uma dataGrid na nossa aplicação.
<riaControls:DomainDataSource x:Name="ContactListDataSource" LoadSize="20" QueryName="GetPLURAL_Contacts" AutoLoad="True" >
<riaControls:DomainDataSource.DomainContext>
<ds:ContactListContext/>
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<data:DataGrid Height="Auto" MinHeight="100" IsReadOnly="True" ItemsSource="{Binding Data, ElementName=ContactListDataSource}"
x:Name="dataGrid1" />
Acima, é definido em XAML, sem recorrer a qualquer linha de código, um controlo de DomainDataSource, que tem uma Query como propriedade, esta query está definida no serviço da camada de acesso aos dados. Seguidamente a definição de uma DataGrid, que tem como fonte de dados o controlo anteriormente definido. Utilizando apenas estes dois controlos a ligação é feita entre os dados e a nossa aplicação. Mais à frente está a explicação de como é definida a entidade de ligação aos dados.
- Autenticação, sim! Lembram-se do Membership em asp.net? Porquê mexer, se o que temos funciona lindamente? Nem mais, membership “out of the box” para a nossa aplicação. Em tudo semelhante na sua arquitectura, e com utilização diferente mas simples. Vejamos o seguinte exemplo:
Para uma chamada a um serviço de inserção de dados, informamos a nossa aplicação que: é necessário que o utilizador esteja devidamente autenticado.
[RequiresAuthentication()]
public void InsertPLURAL_CastingRegistrations(PLURAL_CastingRegistrations pLURAL_CastingRegistrations)
{
pLURAL_CastingRegistrations.RegGuid = Guid.NewGuid();
pLURAL_CastingRegistrations.ts = DateTime.Today;
this.Context.AddToPLURAL_CastingRegistrations(pLURAL_CastingRegistrations);
}
Como se pode ver acima, o método InsertPlural_CastingRegistrations tem uma anotação de “RequiresAutentication()”. Desta simples forma, seja qual for a entidade que invoque este método, só é executado cado existe um utilizador autenticado em sessão.
- Navegação contextualizada, para quem já experimentou desenvolver ou a utilizar mais intensivamente aplicações em silverlight, deparou-se certamente com o problema de navegação dentro da aplicação, e que esta esteja devidamente integrada com o browser. Certamente que recorrer às funções de retroceder ou avançar do browser não têm o comportamento esperado com a aplicação em silverlight. Apesar de já existirem algumas técnicas para “resolver” este problema em Silverlight 2, tal como a utilização de JQuery, ou de ASP.NET AJAX, para guardar o histórico da navegação e interagir com os evento gerados pelo browser dos comandos acima utilizados. A navegation framework, vem resolver este problema de uma forma simples e bastante eficaz, ora se não vejamos o exemplo:
Na página principal colocamos o controlo “navigation”:
<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}"
Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed" Background="{x:Null}">
<navigation:Frame.UriMapper>
<uriMapper:UriMapper>
<uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
</uriMapper:UriMapper>
</navigation:Frame.UriMapper>
</navigation:Frame>
Como podemos ver, basicamente é definino uma forma de como é mapeado e interpretado os endereços URLs, que são colocados no browser. É conhecido como o método de URL re-writting. E neste caso podemos ver que é definido um “contentor” que vai permitir o carregamento de outras páginas em XAML que estejam na directoria de Views/pagina.xaml. Portanto qualquer página que esteja na directoria definida permite que seja referenciada e carregada na vista actual. Assim é guardado o históricos pelas “vistas” que vamos fazendo sobre a nossa aplicação.
Para navegar entre as páginas basta definir um link:
<HyperlinkButton x:Name="Link4" Style="{StaticResource LinkStyle}"
NavigateUri="/ContactList" TargetName="ContentFrame" Content="contact list"/>
Apenas definimos o contentor para onde a página estará a ser carregada, o texto do nosso link, neste caso “contact list”, e o nome da “vista”, página correspondente, com o código XAML e se necessário o code behind.
Estruturação de camadas: Serviços e Apresentação, utilizando esta framework, a aplicação fica devidamente estruturada tanto a nível da camada dos serviços, como a nível de apresentação. Conjuga assim os padrões MVVM (Model View-View Model) e MVC (Model View Control), quer permitem dar à aplicação a máxima abstracção da camada dos serviços que a “alimentam”, mas também a versatilidade da criação de vistas independentes, que são os vários “ecrãs” da aplicação.
A imagem abaixo, demostra esta estruturação dado ao projecto, utilizando o “Silverlight Business Application” template para o Visual Studio 2008.
Como se pode ver, a nossa solução é constituída por dois projectos, a aplicação em Silverlight, e a aplicação Web que fará o hosting da aplicação em si. Do lado da aplicação em Silverlight temos:
Assets, para todos os recursos da aplicação que sejam partilháveis, ex: mídia
Libs, para as dependências externas, ex: AjaxControl Toolkit, ActivityControl, Windows.Controls, etc.
Views, comtém os vários “ecrãs” da nossa aplicação. Ex: about.xaml, clientRegistration.xaml, etc
O ficheiro MainPage.xaml, já contém as referências necessárias para a utilização das várias vistas e da integração com o histórico do browser.
Do lado da aplicação web temos:
ClientBin, por defeito o directório com o ficheiro xap complidado da aplicação Silverlight.
Services, como o nome sugere, todos os ficheiros que representam as interfaces aos serviços da nossa aplicação. São nos dados os seguintes:
AuthenticationService.cs – Responsável pela activação do serviço de existir uma “autenticação” da nossa aplicação. Neste caso em membership
UserInformation.cs – Expressões regulares para a validação dos campos de registo de um utilizador.
UserRegistrationService.cs – Este é a interface do serviço de membership, portanto contém os vários métodos normais para este cenário. Criação de utilizador, Obter Utilizador, etc.
É assim que a nossa aplicação fica estruturada para a utilização da framework .net ria services.
fullsix .NET RIA Services Demo
Para a nossa demo sobre esta Framework, utilizámos o template de Business application, pois facilmente estrutura o nosso projecto sobre os modelos de MVVM e MVC, aplica os serviços de navegação integrada com o browser, faz a integração com o serviço de membership, e importa as várias namespaces que compõem a Framework.
NOTA: a demo, utiliza dados, e nomes, totalmente fictícios cujo único objectivo é de demonstração das funcionalidades dadas pela tecnologia utilizada.
O cenário de utilização
Pensámos num cenário real, onde esta aplicação poderia ser utilizada, mas como aplicação primária para uma determinada função e não apenas uma aplicação de acessório a um cenário já existente.
Neste caso pensámos como um backoffice de um dado site… perfeito!! Ora vejamos:
1) Adicionar dados ao site em produção. OK
2) Disponível através do browser em qualquer lugar. OK
3) Devidamente protegida para acessos indevidos. OK
4) Facilidade de utilização e com um UX e design atraentes. OK (bom aqui não só usámos os novos behaviours que o silverlight 3 nos dá, mas também é necessário ter em conta o desenvolvimento de algum design para a aplicação.)
Passando estes pontos, parece-nos que é um cenário ideal e real para ser utilizada.
Página inicial
Então na página principal aproveitamos os vários controlos que o Silverlight 3 Toolkit nos oferece, e disponibilizamos um gráfico com os acessos ao site principal, para qual este backoffice está a ser utilizado.
Podemos observar no gráfico o número de page hits para o mês de Maio do site em questão.
Mas aproveitando os behaviours do Silverlight 3, aplicou-se o “drag and drop” a todos os elementos e permite assim ao utilizador definir a forma de como deseja ver os elementos na página principal, fazendo clique e arraste como habitualmente. Assim os objectos são “livres” dentro da página inicial:
Navegação entre páginas (vistas)
Utilizando partido da navigation Framework, acrescentou-se facilmente na página principal da aplicação, dois links adicionais: “casting list” e “contact list”.
Basicamente são listagens de dados possíveis do site. Neste caso uma lista de utilizadores que se subscreveram a um casting, e uma lista de contactos feitos pelo site principal.
Até a este ponto, criámos mais duas “vistas”, dois ficheiros xaml: castinglist.xaml e contactlist.xaml.
E na página inicial, junto do componente de navegação que se vê no cabeçalho da aplicação, acrescentámos os dois links:
<Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>
<HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/Casting" TargetName="ContentFrame" Content="casting list"/>
<Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
<HyperlinkButton x:Name="Link4" Style="{StaticResource LinkStyle}"
NavigateUri="/ContactList" TargetName="ContentFrame" Content="contact list"/>
O elemento Rectangle, é apenas o separador visual na barra de navegação.
E avançámos para os serviços da camada de dados…
Acesso a dados e serviços
Uma vez que já temos a nossa aplicação preparada para os novos “ecrãs” que vão servir para mostrar os dados, é necessário agora criar o serviço que fará a ligação com os dados e a nossa vista.
Para esta fase nada melhor que o uso da Entity Framework, que em conjunto com a .net Ria services Framework, facilita muito o processo! E porquê? Além de permitir a selecção das tabelas, ou vistas sobre a base de dados, através de “drag and drop”, gera automaticamente os ficheiros de serviço, com os métodos CRUDE (inserção, selecção, actualização, etc..).
Aqui o LinQ tem um papel fundamental, é através desta linguagem que são criadas as queries necessárias para suportar as operações anteriormente anunciadas. Apesar de serem geradas as queries standards, é-nos dada a liberdade de as alterar e personalizar às nossas necessidades. Para este caso, uma listagem simples, nada que um SELECT * não resolva… e já agora um update, insert, delete, etc… sim, tudo automático por favor!
Adicionamos então uma ADO.NET Entity Data Model, com o nome de PluralCasting, onde escolhemos a tabela apropriada
E foi gerada a classe PluralCastingService.cs que contêm os métodos todos prontinhos a serem utilizados… J.
Recorde-se que o elemento ADO.NET entity, foi mapeado para uma tabela da base de dados, mas este também é possível sobre uma Stored Procedure ou outro qualquer elemento relacional de dados.
Neste momento apenas nos falta fazer os elementos UI para dar suporte a esta listagem. Então no ficheiro de castinglist.xaml acrescentamos a nossa DataGrid:
<data:DataGrid Height="Auto" MinHeight="100" IsReadOnly="True" ItemsSource="{Binding Data, ElementName=CastingDataSource}"
x:Name="dataGrid1" >
<i:Interaction.Behaviors>
<il:FluidMoveBehavior Duration="00:00:04">
<il:FluidMoveBehavior.EaseX>
<BackEase EasingMode="EaseIn"/>
</il:FluidMoveBehavior.EaseX>
<il:FluidMoveBehavior.EaseY>
<BackEase EasingMode="EaseOut" Amplitude="2"/>
</il:FluidMoveBehavior.EaseY>
</il:FluidMoveBehavior>
</i:Interaction.Behaviors>
</data:DataGrid>
(já agora com os movimentos fluidos… um pouco de animação…)
E o nosso Ria data source que contém os dados em si:
<riaControls:DomainDataSource x:Name="CastingDataSource" LoadSize="20" QueryName="GetPLURAL_CastingRegistrations" AutoLoad="True" >
<riaControls:DomainDataSource.DomainContext>
<ds:PluralCastingContext/>
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.SortDescriptors>
<riaData:SortDescriptor PropertyPath="age" Direction="Ascending"/>
<riaData:SortDescriptor PropertyPath="gender" Direction="Ascending"/>
</riaControls:DomainDataSource.SortDescriptors>
<riaControls:DomainDataSource.FilterDescriptors>
<riaData:FilterDescriptorCollection>
<riaData:FilterDescriptor PropertyPath="age" Operator="IsGreaterThanOrEqualTo">
<riaData:ControlParameter ControlName="ageText" PropertyName="Text" RefreshEventName="TextChanged" />
</riaData:FilterDescriptor>
</riaData:FilterDescriptorCollection>
</riaControls:DomainDataSource.FilterDescriptors>
</riaControls:DomainDataSource>
Utilizando como query todos os registos da nossa tabela… e já agora posso ter um filtro? Sim, vamos então filtrar por idade… e a idade é o utilizador que coloca numa caixa de texto, e assim os dados são filtrados no instante.
Hum… interessante, e posso agora ter paginação sobre estes milhares de registos da tabela? Sim, basta utilizar o datapager:
<data:DataPager PageSize="5" Source="{Binding Data, ElementName=CastingDataSource}" Margin="0,-1,0,0"></data:DataPager>
Simples… não?
Eis o resultado:
Agora, gostaríamos de dar a possibilidade ao utilizador de alterar ou adicionar um novo registo a esta listagem. E para que seja possível, será necessário que os nossos serviços criados anteriormente, suportem as devidas operações de inserção e actualização. Mas uma vez utilizado a Entity para esta tabela, já existem estas operações definidas no serviços, portanto só nos falta mesmo a interface.
Para que tenhamos o formulário para edição ou adição de um novo registo, utilizamos também mais um controlo da Framework Ria Services: o DataForm, da seguinte forma:
<dataForm:DataForm x:Name="dataForm1" Header="Casting Candidate Information" AutoGenerateFields="False" AutoEdit="False" AutoCommit="False" CurrentItem="{Binding SelectedItem, ElementName=dataGrid1}" Margin="0,12,0,0">
<dataForm:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<dataForm:DataField Label="User ID">
<TextBox Text="{Binding id, Mode=TwoWay}" />
</dataForm:DataField>
<dataForm:DataField Label="Name">
<TextBox Text="{Binding name, Mode=TwoWay}" />
</dataForm:DataField>
< … >
<Button x:Name="submitButton" Width="75" Height="23" Content="Submit" Margin="4,0,0,0" Click="submitButton_Click"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</dataForm:DataForm.EditTemplate>
</dataForm:DataForm>
Em muito semelhante ao controlo FormView utilizado para ASP.NET. Com a utilização dos templates, Edit Template, Insert Template e ItemTemplate. Utilizamos estes templates para definir o comportamento do nosso formulário consoante o tipo de operação que pretendemos fazer. Neste caso estamos a ver o Edit template, que tem uma StackPanel, com todos os campos DataField que sejam eles ou label ou textbox, fazem binding com o campo da nossa tabela que se encontra devidamente mapeada no nosso Entity model do serviço.
Podemos também observar que a ligação deste formulário com a nossa DataGrid criada anteriormente é feita através da propriedade de CurrentItem. Assim, o elemento actual que estiver seleccionado na nossa dataGrid, é “passado” ao nosso DataForm, para que possa ser editado.
Mas antes de qualquer operação sobre estes dados, autenticação! Se faz favor… ok, não há problema. Uma vez que utilizamos a metatag de RequiredAuthentication() acima sobre as operações no serviço, a .net Ria Services Framework, uma vez que implementa “out of the box” o serviço de autenticação, gera os automatismos necessário, para em caso de pedido de autenticação, desenha sobre o ecrã actual a janela de autenticação, como se pode ver na imagem:
Introduzimos as nossas credenciais:
Username: gchaves
Password: 1234567/
E voilá, já podemos agora utilizar o nosso formulário sobre os dados disponíveis pela dataGrid. Aproveitamos também os novos controlos de validação de campos dados também “out of the Box” pela Framework. Assim para o campo “Gender”, só faz sentido que seja introduzido algo como “m” ou “f”, definimos no código da seguinte forma:
<dataForm:DataField Label="gender">
<TextBox Text="{Binding gender,
Mode=TwoWay,NotifyOnValidationError=True,
ValidatesOnExceptions=True }" />
</dataForm:DataField>
Com o NotifyValidationError e o Validates onExecptions definidos automaticamente pela geração do furmulário, os tipos de restrições sobre os campos são já conhecidos pelo nosso objecto de Entity, que contém toda a estrutura e restrições definidas na base de dados. Assim facilita-nos imenso o processo de validação e verificação… certo? Sim o campo “gender” tem como restrição “tamanho=1” portanto, já nos é dada alguma validação:
OK!
Podemos então observar, que para edição e visualização de dados, a .net Ria Services framework, dá-nos efectivamente uma colecção de controlos e serviços muito úteis e fáceis de personalisar para que a nossa aplicação responda às necessidades específicas de cada problema.
Aproveitamos a estrutura que este template nos oferece, e criamos a janela de “CastingCandidateRegistrationWindow”, pois permite facilmente ser manuseada em código, e uma vez que extende do novo controlo ChildWindow,o efeito de “Shadow” que vemos na imagem é também gerado automaticamente pela aplicação J.
O código que utilizámos para fazer o handle entre a acção de editar ou inserir dados, e o utilizador está autenticado, foi o seguinte:
void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
{
CastingCandidateResgistrationWindow addUser = new CastingCandidateResgistrationWindow();
addUser.Closed += new EventHandler(addUser_Closed);
addUser.Show();
}
void addUser_Closed(object sender, EventArgs e)
{
CastingCandidateResgistrationWindow user = (CastingCandidateResgistrationWindow)sender;
if (user.NewCastingUser != null)
{
PluralCastingContext _PluralnContext = (PluralCastingContext)(CastingDataSource.DomainContext);
_PluralnContext.PLURAL_CastingRegistrations.Add(user.NewCastingUser);
CastingDataSource.SubmitChanges();
}
}
Com estes dois métodos, definimos o comportamento após o utilizador se autentique, foi o redireccionamento sobre a acção dada pela Framework, que notifica a aplicação que o utilizador está autenticado. E sendo assim, é possível então instanciar a nossa janela de inserção e muito importante, é passar-lhe o objecto de DomainContext que trás consigo toda a informação actual sobre a “vista” que temos do nosso objecto de dados.
Behaviours e UX
Utilizámos alguns dos behaviours que o Silverlight 3 já nos disponibiliza, tais como, o drag and drop, fluid e drop shadow. Através do Expression Blend aplicámos aos vários controlos estes comportamentos. Sendo a sua definição em xaml assim:
<il:MouseDragElementBehavior/>
<il:FluidMoveBehavior Duration="00:00:03">
<DropShadowEffect Direction="310"/>
Com estes comportamentos, e utilizando a estrutura dada pela .net ria services, facilmente conseguimos um pequeno projecto aplicado a um cenário real, mas com uma interface e comportamento bastante atractivo.
Uma vez que estes behavious, são bastante intuitivos e fáceis de serem usados, muito em breve haverá uma vasta colecção que permita facilmente dar um bom aspecto e comportamento à aplicação.
Ex: http://gallery.expression.microsoft.com/en-us/MIXBehaviorPack
O floatable window (http://floatablewindow.codeplex.com/) é outro bom exemplo de como podemos dar um comportamento à lá Windows aos nossos controlos.
Duplo clique do rato: http://weblogs.asp.net/eecsaky/archive/2009/07/26/doble-click-en-silverlight-usando-behaviors.aspx
Conclusões
Sabemos que o desenvolvimento das RIAs “só agora começou”, e ainda muito se fala e pouco se vai vendo, contudo é muito importante que as ferramentas se preparem e ofereçam várias opções aos utilizadores, não só para que se desenvolvam novas aplicações com UX cada vez mais atractivas e funcionais, mas também, para contribuir na facilidade e disponibilização de serviços aos utilizadores.
A .NET Ria Services Framework é um “must have” na família .net. e posiciona-se numa área de extrema importância e utilização “Web”, e portanto é o primeiro passo da Microsoft nesta área e na minha opinião pessoal, um passo certo e no caminho direito
Não só pelas funcionalidade “out of theBox” que nos dá, mas pela sua simplicidade e estruturação, da aplicação sobre a arquitectura ideal das aplicações para este tipo de cenários.
Com a próxima versão beta a ser lançada no PDC, estou expectante sobre o que mais esta Framework nos irá oferecer, e que novas tools venham a ser apresentadas.
Algumas referências externas e exemplos:
http://xamlpt.com/blogs/nunogodinho/archive/2009/07/21/net-ria-services.aspx
http://silverlight.net/forums/53.aspx
http://xamlpt.com/media/12/default.aspx