Objetivo
Nesta parte do tutorial, falaremos um pouco do suporte do Spring.Net ao ADO.Net e Transações. A idéia central dessa parte do tutorial está em explicar a idéia de configurar as transações na aplicação como um “aspecto” e não como código “pregado” na aplicação.
Como pré-requisito dessa parte do tutorial, somente a parte 1 (Dependency Injection) é suficiente. Porém, nada impede da idéia de transação ser usada em conjunto com o suporte a Web Services do Spring. Resolvi fazer em cima da parte 1, justamente para explicar separadamente cada um dos conceitos.
Conceito de Transação no Spring.Net
Aqui não tem nenhuma novidade a idéia de uma transação é o velho conceito de “ou tudo acontece ou nada acontece”. O usuário vai gravar um pedido com todas as suas informações. Um pedido, N itens, cada um dos itens com N programações de entrega. Ou o pedido acontece inteiro na base de dados, ou não acontece. Se acontecer “somente um pedaço”, teremos um problema (pedido sem itens, itens sem programação de entrega).
Esse conceito começou nos bancos de dados relacionais, onde se tem a idéia de iniciar uma transação, fazer uma série de atualizações. Se tudo deu certo, damos um “commit” no final e tudo acontece. Se algo der errado, damos um “rollback” no final e nada acontece.
Na teoria isso é muito bonito e simples, mas na prática é comum vermos o nosso código com o controle de transação misturado no meio da regra de negócio. Por exemplo, vamos imaginar o seguinte código na nossa camada de negócio (reusável por mais de uma UI):
public void gravarPedido(Pedido p){ //Consiste dados do cabeçalho do pedido //outras consistências de item. //Gravação transacional do pedido. using (TransactionScope tx = new TransactionScope){ //Cria conexão, grava cabeçalho do pedido foreach (ItemPedido ip in p.Itens){ //Grava item do pedido } } }
Peraí… o código é para ter regra de negócio ou controle de transação? Será que não estamos “sujando” nossa regra de negócio?
Dessa forma, podemos criar um método que encapsula as regra de negócio do pedido e da transação, porém, se formos utilizá-lo em outro método transacional que “gera pedidos”, passamos a ter problema, pois quem manda na transação?
Então teríamos outra saída, delegar o gerenciamento da transação para a UI. Aí caímos na questão… faz sentido a interface com o usuário ser responsável pela transação? Se o programador que está consumindo uma regra de negócio pré-escrita “esquecer” da transação, tudo vai por água abaixo.
A idéia do Spring aqui é justamente resolver o problema da transação pensando na mesma como um aspecto (ou requisito não-funcional).
Antes de explicarmos como funciona esse conceito no Spring, devemos entender que para que possamos usufruir de suas facilidades é necessário usar uma framework de acesso a dados que suporte o Spring. No caso do ADO.net, o Spring fez algumas evoluções para que ele funcione junto com o seu conceito de transação. O Spring também suporta NHibernate, mas não é o foco desse artigo. É possível também implementar suporte do Spring para outras frameworks de mapeamento objeto/relacional ou acesso a dados (o Spring oferece mecanismos para isso).
Suporte do Spring ao ADO.Net
Seguindo a idéia do nosso exemplo, o tutorial de Spring parte 1 (Spring.Net – Parte 1 – Dependency Injection), vamos implementar uma nova camada de persistência, usando Spring.Data.
O suporte ADO do Spring.Net tem por objetivo “desacoplar” também qual “provider” do ADO está sendo utilizado, por isso utiliza sempre interfaces como IDbConnection, IDbDataReader e amigos. Dessa forma, se trocarmos o provider e escrevermos queries em “ANSI-SQL”, podemos facilmente ter uma aplicação que suporte vários bancos de dados sem grandes dores de cabeça.
Modelo E/R
Fugi o tempo inteiro de usar base de dados nesses exemplos, mas infelizmente agora não vai dar mais para escapar. Criei um exemplo usando o SQL Server. Daria pra fazer usando outro provider sem grandes problemas.
As tabelas que precisamos são as seguintes:
create table Pedido( PedidoID int identity(1,1) not null, ClienteID int not null, ValorTotal float not null constraint PK_Pedido primary key (PedidoID) ) create table ItemPedido( ItemPedidoID int identity(1,1) not null, PedidoID int not null, ProdutoID int not null, Quantidade float not null, PrecoUnitario float not null constraint FK_Pedido foreign key (PedidoID) references Pedido (PedidoID) constraint PK_ItemPedido primary key (ItemPedidoID) )
Implementando a camada de Persistência no nosso Exemplo
Primeiro vamos criar na nossa solution um novo assembly e batizá-lo de DAL.ADO.SQLServer. Esse assembly deve referenciar os seguintes assemblies:
- Model (porque ele precisa conhecer os VO’s para persistir)
- DAL.Interface (porque ele precisa implementar a interface da nossa camada de acesso a dados para poder ser “injetado” pelo Spring).
Vamos criar nesse assembly as classes PedidoDAO e ItemPedidoDAO:
PedidoDAO.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DAL.Interface; using System.Data.SqlClient; using Model; using System.Data; using System.Data.SqlTypes; using Spring.Data.Common; using Spring.Data.Generic; using Spring.Data; namespace DAL.ADO.SQLServer { public class PedidoDAO : AdoDaoSupport, IPedidoDAO { #region IPedidoDAO Members public void inserirPedido(Pedido p) { IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("ClienteID").Type(DbType.Int32).Value(p.ClienteID); builder.Create().Name("ValorTotal").Type(DbType.Double).Value(p.ValorTotal); string sql = "insert into Pedido (ClienteID, ValorTotal) " + "values (@ClienteID, @ValorTotal) \n" + "select SCOPE_IDENTITY()"; decimal d = (decimal)AdoTemplate.ExecuteScalar(CommandType.Text, sql, builder.GetParameters()); p.PedidoID = Convert.ToInt32(d); } public void alterarPedido(Pedido p) { string sql = "update Pedido " + "set " + " ValorTotal = @ValorTotal, " + " ClienteID = @ClienteID " + "where " + " PedidoID = @PedidoID"; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("@ValorTotal").Type(DbType.Double).Value(p.ValorTotal); builder.Create().Name("@ClienteID").Type(DbType.Int32).Value(p.ClienteID); builder.Create().Name("@PedidoID").Type(DbType.Int32).Value(p.PedidoID); AdoTemplate.ExecuteNonQuery(CommandType.Text, sql); } public void excluirPedido(Pedido p) { string sql = "delete from Pedido " + "where PedidoID = @PedidoID"; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("PedidoID").Type(DbType.Int32).Value(p.PedidoID); AdoTemplate.ExecuteNonQuery(CommandType.Text, sql); } private void mapearLinhaPedido(IDataReader dataReader){ } public Model.Pedido pegarPorID(int PedidoID) { string sql = "select PedidoID, ClienteID, ValorTotal from Pedido where PedidoID = @PedidoID"; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("PedidoID").Type(DbType.Int32).Value(PedidoID); IList<Pedido> l = AdoTemplate.QueryWithRowMapper<Pedido>(CommandType.Text, sql, new PedidoRowExtractor(), builder.GetParameters()); if (l.Count > 0) return l[0]; else return null; } #endregion } public class PedidoRowExtractor: IRowMapper<Pedido>{ #region IRowMapper<Pedido> Members Pedido IRowMapper<Pedido>.MapRow(IDataReader reader, int rowNum) { Pedido p = new Pedido(); p.PedidoID = reader.GetInt32(0); p.ClienteID = reader.GetInt32(1); p.ValorTotal = (float)reader.GetDouble(2); return p; } #endregion } }
ItemPedidoDAO.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DAL.Interface; using System.Data.SqlClient; using System.Data; using Model; using Spring.Data.Generic; using Spring.Data.Common; using Spring.Data; namespace DAL.ADO.SQLServer { public class ItemPedidoDAO : AdoDaoSupport, IItemPedidoDAO { #region IItemPedidoDAO Members public void InserirItemPedido(Model.ItemPedido ip) { string sql = "insert into ItemPedido (PedidoID, ProdutoID, Quantidade, PrecoUnitario) " + "values (@PedidoID, @ProdutoID, @Quantidade, @PrecoUnitario) \n " + "select SCOPE_IDENTITY()"; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("PedidoID").Type(DbType.Int32).Value(ip.PedidoID); builder.Create().Name("ProdutoID").Type(DbType.Int32).Value(ip.ProdutoID); builder.Create().Name("Quantidade").Type(DbType.Double).Value(ip.Quantidade); builder.Create().Name("PrecoUnitario").Type(DbType.Double).Value(ip.PrecoUnitario); decimal d = (decimal)AdoTemplate.ExecuteScalar(CommandType.Text, sql, builder.GetParameters()); ip.ItemPedidoID = Convert.ToInt32(d); } public void AlterarItemPedido(Model.ItemPedido ip) { string sql = "update ItemPedido " + "set " + " PedidoID = @PedidoID, " + " ProdutoID = @Produto, " + " Quantidade = @Quantidade, " + " PrecoUnitario = @PrecoUnitario " + "where " + " ItemPedidoID = @ItemPedidoID "; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("PedidoID").Type(DbType.Int32).Value(ip.PedidoID); builder.Create().Name("ProdutoID").Type(DbType.Int32).Value(ip.ProdutoID); builder.Create().Name("Quantidade").Type(DbType.Double).Value(ip.Quantidade); builder.Create().Name("PrecoUnitario").Type(DbType.Double).Value(ip.PrecoUnitario); builder.Create().Name("ItemPedidoID").Type(DbType.Int32).Value(ip.ItemPedidoID); AdoTemplate.ExecuteNonQuery(CommandType.Text, sql, builder.GetParameters()); } public void ExcluirItemPedido(Model.ItemPedido ip) { string sql = "delete from ItemPedido " + "where ItemPedidoID = @ItemPedidoID "; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("ItemPedidoID").Type(DbType.Int32).Value(ip.ItemPedidoID); AdoTemplate.ExecuteNonQuery(CommandType.Text, sql, builder.GetParameters()); } public List<Model.ItemPedido> LerPorPedidoID(int PedidoID) { List<ItemPedido> l = new List<ItemPedido>(); string sql = "select ItemPedidoID, PedidoID, ProdutoID, Quantidade, PrecoUnitario " + "from ItemPedido where PedidoID = @PedidoID"; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("PedidoID").Type(DbType.Int32).Value(PedidoID); IList<ItemPedido> l2 = AdoTemplate.QueryWithRowMapper<ItemPedido>(CommandType.Text, sql, new ItemPedidoRowMapper(), builder.GetParameters()); foreach(ItemPedido ip in l2) l.Add(ip); return l; } public class ItemPedidoRowMapper: IRowMapper<ItemPedido>{ #region IRowMapper<ItemPedido> Members public ItemPedido MapRow(IDataReader reader, int rowNum) { ItemPedido ip = new ItemPedido(); ip.ItemPedidoID = reader.GetInt32(0); ip.PedidoID = reader.GetInt32(1); ip.ProdutoID = reader.GetInt32(2); ip.Quantidade = (float)reader.GetDouble(3); ip.PrecoUnitario = (float)reader.GetDouble(4); return ip; } #endregion } #endregion } }
Entendendo um pouco do Spring.Data
Vamos discutir um pouco a implementação dessa persistência:
public class PedidoDAO : AdoDaoSupport, IPedidoDAO { //... }
As classes de persistência na camada de acesso a dados devem herdar de AdoDaoSupport. Essa classe possui internamente a propriedade “AdoTemplate”. O AdoTemplate é uma classe do Spring com os métodos para suporte ao ADO.Net e é o principal ponto.
A implementação do AdoTemplate será “injetada” na configuração do contexto do Spring, por isso, não devemos nos preocupar com “de onde ela vem”. Não agora.
public void inserirPedido(Pedido p) { IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("ClienteID").Type(DbType.Int32).Value(p.ClienteID); builder.Create().Name("ValorTotal").Type(DbType.Double).Value(p.ValorTotal); string sql = "insert into Pedido (ClienteID, ValorTotal) " + "values (@ClienteID, @ValorTotal) \n" + "select SCOPE_IDENTITY()"; decimal d = (decimal)AdoTemplate.ExecuteScalar(CommandType.Text, sql, builder.GetParameters()); p.PedidoID = Convert.ToInt32(d); }
“CreateDbParametersBuilder” é um método que vem da classe AdoDaoSupport. É um “facilitador” para criar parâmetros, independente do banco de dados que está instanciado. Sempre são usados tipos de “DbType”, e na hora de se chamar builder.GetParameters(), o Spring automaticamente instancia os parâmetros do tipo que o provider do ADO precisa. O prefixo do parâmetro (@) não é necessário também. O AdoTemplate automaticamente o adiciona de acordo com a necessidade do banco de dados, justamente para minimizar a dependência entre os bancos.
O exemplo acima, utiliza AdoTemplate.ExecuteScalar, para poder devolver no PedidoID o identity gerado pelo banco de dados. Por que o identity retorna como um decimal é um mistério pra mim e também não é objetivo do meu artigo tentar desvendá-lo.
public Model.Pedido pegarPorID(int PedidoID) { string sql = "select PedidoID, ClienteID, ValorTotal from Pedido where PedidoID = @PedidoID"; IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("PedidoID").Type(DbType.Int32).Value(PedidoID); IList<Pedido> l = AdoTemplate.QueryWithRowMapper<Pedido>(CommandType.Text, sql, new PedidoRowExtractor(), builder.GetParameters()); if (l.Count > 0) return l[0]; else return null; }
Esse método já é um pouco mais complexo, pois ele “mapeia” o retorno da query para o VO. A parte de escrever a query e passar os parâmetros é idêntico ao método anterior, porém, a hora de pegar o retorno usamos o método “QueryWithRowMapper”.
Esse método, recebe como parâmetro um IRowMapper, implementado na classe abaixo:
Pedido IRowMapper<Pedido>.MapRow(IDataReader reader, int rowNum) { Pedido p = new Pedido(); p.PedidoID = reader.GetInt32(0); p.ClienteID = reader.GetInt32(1); p.ValorTotal = (float)reader.GetDouble(2); return p; }
Pra resumir, ao executar o método, o AdoTemplate cria internamente o SqlCommand (ou o command apropriado ao DbProvider em questão, é transparente), executa a query, pega o datareader de retorno e faz um loop nele. Para cada registro encontrado, faz uma chamada em “MapRow”, para obter o retorno, e já retorna a lista prontinha.
Existem uma série de outros métodos auxiliares como QueryWithResultSetExtractor, QueryWithRowCallback e QueryWithCommandCreator, para facilitar o mapeamento dos resultados para VO’s.
Configurando o Contexto
Agora que nossa implementação está completa, vamos à configuração. Primeiro vamos criar uma referência para o DAL.ADO.SqlServer na nossa Web Application, para que o Spring encontre o assembly no “bin” da WebApplication quando for instanciar nossa camada de persistência.
É necessário adicionar também uma referência para
Em seguida, vamos fazer as configurações no Web.config. Vamos adicionar o seguinte nó dentro de /
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
Essa configuração, permite que parsers sejam especificados dentro do nó a seção . Então vamos adicionar dentro do nó :
<parsers> <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /> </parsers>
Essa configuração, permite que o namespace “http://www.springframework.net/database” possa ser usado na configuração. Vamos adicionar então no nó o atributo xmlns:db:
<objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database" > <!-- aqui vão outros nós --> </objects>
Por último vamos adicionar dentro do nó / os seguintes nós:
<object id="PedidoDAO" type="DAL.ADO.SqlServer.PedidoDAO, DAL.ADO.SqlServer"> <property name="AdoTemplate" ref="adoTemplate" /> </object> <object id="ItemPedidoDAO" type="DAL.ADO.SqlServer.ItemPedidoDAO, DAL.ADO.SqlServer" > <property name="AdoTemplate" ref="adoTemplate" /> </object> <object id="PedidoBO" type="BLL.Implementation.PedidoBO, BLL.Implementation" autowire="byType" /> <object type="~/Default.aspx" > <property name="pedidoBO" ref="PedidoBO" /> </object> <db:provider id="DbProvider" provider="SqlServer-2.0" connectionString="server=(local);user=sa;pwd=123456;initial catalog=SpringTeste"/> <object id="adoTemplate" type="Spring.Data.Generic.AdoTemplate, Spring.Data"> <property name="DbProvider" ref="DbProvider" /> </object>
Essas configurações especificam o seguinte:
- PedidoDAO: Objeto de persistência para o pedido, usando a implementação da nossa camada de acesso a dados recém-criada. Aqui estamos setando manualmente o AdoTemplate para a instância que será criada posteriormente.
- ItemPedidoDAO: Idem, para item de pedido
- PedidoBO: nada muda em relação à parte 1 do tutorial.
- ~/Default.aspx: nada muda em relação à parte 1 do tutorial.
- DbProvider: O registro daquele namespace “database” é o q permite que o prefixo “db” seja reconhecido pela web.config. O DBProvider é um objeto do Spring que encapsula o Provider do ADO para o SQLServer. Existem vários outros providers para vários outros bancos. Na documentação do Spring existe a lista completa http://www.springframework.net/doc-latest/reference/html/ado.html.
- adoTemplate: Instância do AdoTemplate, apontando para o provider instanciado, que será “injetada” na nossa camada de persistência.
Como vemos, toda a parte de instância e configuração da conexão com o banco de dados é feita via configuração.
Se executarmos nossa aplicação agora, veremos que ela funciona exatamente como na parte 1 do tutorial, porém, persistindo e recuperando todas as informações de um banco de dados SQL Server.
Transação
Calma. Eu não iria fazer o leitor ter todo esse trabalho se não tivesse algum ganho significativo.
Agora vamos começar a configurar a parte interessante da coisa que é o controle de transação. Para configurar a transação, vamos adicionar o seguinte nó na Web.config, dentro do nó spring/parsers:
<parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data"/>
Essa configuração permite que o Spring passe a reconhecer o namespace http://www.springframework.net/tx. Para podermos utilizá-lo, vamos adicionar o namespace no nó objects, ficando ele da seguinte forma (não precisa alterar os nós dentro do nó objects):
<objects xmlns="http://www.springframework.net" xmlns:tx="http://www.springframework.net/tx" xmlns:db="http://www.springframework.net/database" >
Agora, vamos adicionar dentro do nó spring/objects os seguintes nós:
<object id="transactionManager" type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"> <property name="DbProvider" ref="DbProvider"/> </object> <tx:attribute-driven transaction-manager="transactionManager" />
Esses objetos no contexto servem pra duas coisas: O transactionManager instancia a classe do spring que gerencia as transações. Nesse caso, para gerenciar, estamos usando a “AdoPlatformTransactionManager”, que basicamente encapsula uma transação do próprio ADO (outras opções seriam uma transação do tipo System.Transactions ou transações distribuídas). A tag tx:attribute-driven na verdade é uma forma resumida de configurar os recursos de AOP do Spring (que serão estudados num próximo artigo) a mapear todos os métodos anotados com [Transaction] e torná-los transacional. Existem outras formas de fazer isso. Ex.: Configurar todos os métodos que começam com “do” para serem transacionais, ou simplesmente configurar todo e qualquer método exposto num service Spring para serem transacionais.
Como no nosso exemplo usamos a “transação orientada a atributos”, precisamos “anotar” nosso método como Transacional. Vamos então na classe PedidoBO, colocar a seguinte anotação (É necessária uma referência para Spring.Data, para encontrar a definição de TransactionAttribute):
[Transaction] public void inserirPedido(Model.Pedido p) { //... }
Pronto. Adicionando essa configuração, tudo que acontece dentro de “inserirPedido” automaticamente é transacional. Se esse método por sua vez utilizar outro que seja transacional, automaticamente o método de “fora” será o transacional, de forma que para a aplicação fica transparente a configuração da transação (se é distribuída, Ado, etc.) e o código de negócio não fica “poluído” com código relacionado à acesso ao banco de dados.
Para confirmar que de fato a transação está ocorrendo, vamos alterar o código do nosso “inserirPedido” no PedidoBO para o seguinte:
[Transaction] public void inserirPedido(Model.Pedido p) { //Calcula automaticamente o valor total do cabeçalho do pedido baseado nos itens. //Só pra exemplificar uma regra de negócio. float valorTotal = 0; foreach(ItemPedido ip in p.Itens){ valorTotal += (ip.Quantidade * ip.PrecoUnitario); } p.ValorTotal = valorTotal; //Persiste o pedido e seus itens. _pedidoDAO.inserirPedido(p); //Se colocarmos um breakpoint na linha abaixo veremos que p.PedidoID já recebeu até o identity do banco. foreach(ItemPedido ip in p.Itens){ ip.PedidoID = p.PedidoID; //Passa o ID com autoincremento para os filhos. _itemPedidoDAO.InserirItemPedido(ip); } throw new Exception("Vamos testar a transação"); //Quando chegamos aqui, automaticamente ocorre o rollback da transação }
Agora vamos colocar um breakpoint em cima da linha com “foreach” para verificarmos que de fato a transação acontece, pois quando uma exception é gerada dentro do método, automaticamente ocorre o rollback do pedido e itens inseridos.
Conclusão
Vemos que além dos ganhos significativos que temos no isolamento do “provider” ADO através dos recursos do Spring.Net, temos nas transações declarativas uma poderosa ferramenta para “desacoplar” o código de controle de transação do código de negócio. Esse processo facilita tanto o reuso da regra de negócio quanto minimiza a dependência de código específico de banco de dados na aplicação (Ex.: SqlDbType nos parâmetros).
Nesse artigo vemos um “exemplo” de como usar a transação. Utilizando um Spring.Data.Core.TxScopeTransactionManager como transaction manager por um exemplo, automaticamente nossa aplicação passará a usar transações do tipo “System.Transactions”. Quando uma transação passa a operar entre duas bases de dados diferentes, o TxScope automaticamente “promove” a transação local para uma transação distribuída. Em outras palavras, automaticamente conseguimos fazer que o Spring (baseado nos recursos do TxScope) faça com que os métodos de negócio anotados com [Transaction] suportem transações distribuídas (entre duas bases de dados diferentes!).
Código Fonte
O código fonte deste artigo está disponível para download em: http://ericlemes.wikidot.com/local–files/dotnet-spring-pt3/SpringParte3.zip .
Olá Eric,
Gostaria de tirar uma dúvida com voce (li o capitulo sobre transações no manual do spring mas não encontrei o que preciso):
é possível utilizar o gerenciamento de transações do Spring SEM UTILIZAR IOC?
Isso porque preciso ganhar tempo, mudando o mínimo possível da aplicação.
Ficaria algo assim :
[Transaction]
public void Teste()
{
(new Teste1DAO()).FazAlgo();
(new Teste2DAO()).FazAlgo();
}
Note que meus 2 DAOs são instanciados diretamente (na verdade isso é feito em uma factory), ou seja, não existem Interfaces nem pros BOs nem pros DAOs.
É possível fazer algo assim ou sou obrigado a utilizar as interfaces e fazer a configuração via XML de cada DAO/BO (tal qual você mostrou nos seus artigos) ?
Obrigado pela atenção.
[]s
Robson,
Infelizmente, tem que passar pelo IoC. Como a transação é inserida no código via aspecto (a quarta parte do tutorial explica como), é necessário passar pelo IoC para que ela funcione.
Abraço,
Eric
Eric,
Obrigado pela atenção.
Pelo jeito, terei que abandonar temporariamente o Spring (prazo curto) e utilizar o TransactionScope em meus BOs..mas espero assim que tiver mais tempo, retornar ao mesmo.
Valeu!
[]s
Olá Eric,
Você fez um ótimo trabalho com esses exemplos.
Tenho uma dúvida e talvez você possa me ajudar.
É possível utilizar o Spring.net com o ASP.NET MVC?
Obrigado,
César.
Cesar,
Não muda muita coisa trabalhando no ASP.Net MVC. Até a época que eu estava integrando meus serviços com ASP.Net MVC, não existia nada “pronto” do Spring.Net para fazer com que a injeção ocorresse dentro dos controllers. Para isso tínhamos duas saídas:
A primeira é pegar os serviços diretamente do contexto com
IApplicationContext ctx = IApplicationContext.GetContext();
IMeuService service = (IMeuService) ctx.GetObject(“MeuService”);
A outra é colocar os controllers também como objetos dentro do contexto do spring (colocá-los dentro dos XML’s) e usar um ControllerFactory para buscá-los do contexto, conforme esse post explica:
http://weblogs.asp.net/fredriknormen/archive/2007/11/17/asp-net-mvc-framework-create-your-own-icontrollerfactory-and-use-spring-net.aspx
Eu particularmente estranhei um pouco essa abordagem por manter uma única instância do controller entre vários requests, o que pode ser problemático e exigir alguma preocupação extra com acesso do mesmo controller por mais de uma thread.
Por isso, eu gostei mais da primeira. Achei mais simples.
Daria também para extender essa idéia do ControllerFactory e fazer ele devolver uma nova instância do controller, varrer via reflection as propriedades públicas do controller e buscar por tipo no contexto do spring as instâncias dos services. Só não sei se vale a pena pagar o preço.
Abraço,
Eric
Obrigado pela resposta Eric.
Me ajudou muito.
Abraço,
César.
Blz Eric?
Pelo que entendi, para acesso ao SQL Server no framework Spring.Net só posso usar o ADOTemplate? ou dá para adaptar uma classe ADO proprietária?
Explicando: muitas aplicações legadas no qual eu trabalho utilizam as classes Microsoft.Practices.EnterpriseLibrary.Data,
Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory e Microsoft.Practices.EnterpriseLibrary.Data.Database
abs,
Rolemberg
Rolemberg,
Infelizmente é necessário usar uma das classes de acesso a dados do Spring, no caso o AdoTemplate. Tem outros providers pra NHibernate ou outras formas de acesso a dados.
Dá para usar outras, porém os aspectos de transação que já vem prontos no Spring (transação por anotação) não funcionam corretamente.
É importante ressaltar que esses exemplos todos foram em cima de Spring 1.1 (os artigos tem quase 2 anos de existência). Hoje é provável que existam outras alternativas já que a framework está na versão 1.3.
Abraço,
Eric