sábado, agosto 31, 2013

Git Hub

Subi para o Git Hub o exemplo do último post sobre IoC com Spring.NET. O endereço é: https://github.com/jeanjmichel e o repositório é spring.net-ioc-exemple. Enjoy!

sexta-feira, agosto 30, 2013

Spring.NET IoC (exemplo prático/tutorial)



Há algum tempo estou desenvolvendo em .Net, utilizando C# como linguagem base e tenho gostado bastante.
A curva de aprendizagem para quem conhece Java é muito tenra. Ainda acho Java mais robusto, organizado e escalável do que a plataforma .Net, mas isso não inviabiliza o uso da plataforma da Microsoft em qualquer tipo de projeto, na minha opinião.

Como todo javeiro cascudo acostumado a testar várias coisas e frameworks, etc. comecei criando um projetinho de library com códigos dos mais fáceis para aprender a sintaxe e depois fui montando a minha arquitetura bem semelhante ao que fazia em projetos Java.

Então esbarrei em IoC (Inversion of Control) eu já havia utilizado o Spring Framework em projetos Java, e para minha grata surpresa ele também existe para .Net, e então decidi utilizá-lo.

A configuração dele é um pouco diferente do Java, na sintaxe apenas, pois o conceito ainda é ter um arquivo com as configurações das classes, propriedades, etc. (não cheguei a pesquisar se há alguma coisa semelhante ao Spring Annotation para .Net).

Mas deixe-me registrar para quem mais isso possa interessar como colocar o Spring.Net para rodar.

O que eu fiz foi criar um projeto do tipo Console Application, chamado TestIoC e via Manage NuGet Packages adicionei o Spring.NET Core ao meu projeto.
Depois disso criei três pastas no projeto:

model
dao
service

Na pasta model eu tenho uma classe de modelo simples, um POCO (Plain Old CLR Object) chamado Product:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace model
{
    public class Product
    {
        public int Id
        {
            set;
            get;
        }

        public String Description
        {
            set;
            get;
        }

        public double Price
        {
            set;
            get;
        }

        public Product(int id, String description, double price)
        {
            this.Id = id;
            this.Description = description;
            this.Price = price;
        }
    }
}

Na pasta dao eu tenho a classe que simula uma pesquisa no banco de dados:

using System.Collections.Generic;
using model;

namespace dao
{
    public class ProductDAO : IProductDAO
    {
        public List ListAll()
        {
            List products = new List();
         
            products.Add(new Product(1,
                                     "Pinarello Dogma road bike carbon frame and forks",
                                     6300.00));

            products.Add(new Product(2,
                                     "Caloi Sprint road bike alloy frame carbon forks",
                                     1161.41));

            products.Add(new Product(3,
                                     "Colnago Move road bike alloy frame carbon forks",
                                     1283.14));

            return products;
        }
    }
}

* Esta classe realiza a interface IProductDAO que define o método List ListAll().

E na pasta service eu tenho a classe que manipula o POCO Product através da classe ProductDAO, a classe ProductService ficou assim:

using System.Collections.Generic;
using dao;
using model;

namespace service
{
    public class ProductService : IProductService
    {
        IProductDAO productDAO;

        public IProductDAO IProductDAO
        {
            get;
            set;
        }

        public ProductService()
        {
        }

        public List FindAll()
        {
            return this.productDAO.ListAll();
        }
    }
}

* Esta classe realiza a interface IProductService que define o método List ListAll().

De cara notamos que há um campo chamado productDAO nesta classe. Então para buscar e retornar uma lista de produtos esta classe service utilizar “alguma classe” que realize essa interface IProductDAO e contenha o método ListAll. No nosso caso a classe que faz isso é ProductDAO ;)

Agora na classe Program, onde está o método main desta aplicação, iremos fazer uma chamada a ProductService.FindAll e exibir os resultados.

using System;
using Spring.Context;
using Spring.Context.Support;
using service;
using model;

namespace program
{
    class Program
    {
        static void Main(string[] args)
        {
            IApplicationContext ctx = ContextRegistry.GetContext();
            IProductService productService = (IProductService)ctx.GetObject("ProductService");

            foreach (Product p in productService.FindAll())
            {
                Console.WriteLine("Id: " + p.Id +
                                  " Desc.: " + p.Description +
                                  " Price: $" + p.Price);
            }

            Console.ReadKey();
        }
    }
}

Notaram que em nenhum momento eu estou criando uma instância do meu DAO? E que aqui eu estou utilizando os dados retornados por ele para o service ProductService?
Eu não sei como a minha classe service está sabendo quem utilizar como realização de IProductDAO. E eu não quero me importar com isso também ;) Alguém teve o trabalho de criar a classe ProductDAO, e onde ela busca os dados para mim não interfere no que eu quero fazer com eles. Não faz sentido eu me preocupar com isso.
E é aqui que a mágica acontece, onde o Spring vai retornar uma instância de ProductService para cá e já vai injetar nela uma instância de ProductDAO. Por isso eu não estou instanciando o service aqui, eu estou pedindo uma instância dele para o “contexto” do Spring.NET.

O contexto do Spring.NET é inicializado com a aplicação, e a partir de um arquivo de configuração ele fica sabendo quem é quem e de que precisam. Confuso? Nâo!

O contexto do Spring.NET sabe que existe uma classe chamada ProductDAO e uma outra chamada ProductService, e que essa última tem uma propriedade chamada productDAO do tipo IProductDAO que precisa ser injetada.
Por isso quando eu peço ao contexto uma instância de uma classe chamada ProductService ela vem completa e pronta para uso, com o respectivo DAO atribuído e pronto para uso, sem que eu ganhe mais um cabelo branco por causa disso me preocupando em instanciar o DAO correto, etc.

Para essa mágica acontecer temos no arquivo de configuração da aplicação o seguinte conteúdo:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.net/tx"
xmlns:db="http://www.springframework.net/database">

<object name="ProductService" type="service.ProductService, TestIoC">
<property name="productDAO" ref="ProductDAO"/>
</object>

<object name="ProductDAO" type="dao.ProductDAO, TestIoC"></object>

</objects>
</spring>
</configuration>


Notem que cada classe mapeada para o contexto está em uma tag <object> dentro do universo <objects>.
Então mapeamos o ProductDAO:

<object name="ProductDAO" type="dao.ProductDAO, TestIoC"></object>

Onde type é o nome qualificado da classe (namespace.classe) e depois da virgula (,) temos o nome do nosso projeto.

Nota: Na minha primeira tentativa de colocar o Spring.NET para rodar foi lançada uma exception com a mensagem: “Error creating context 'spring.root': Could not load type from string”. Perdi um tempo até entender que no atributo type o que vem depois da virgula é o nome do assembly do projeto, e não da classe. E o nome do assembly deste projeto é TestIoC.exe.

E o mapeamento da classe ProductService:

<object name="ProductService" type="service.ProductService, TestIoC"> <property name="productDAO" ref="ProductDAO"/> </object>

A única diferença é que estamos informando a dependência dessa classe, onde a propriedade chamada productDAO é referente a classe mapeada ProductDAO.

Agora é por tudo para rodar e obter o resultado:

Id: 1 Desc.: Pinarello Dogma road bike carbon frame and forks Price: $6300
Id: 2 Desc.: Caloi Sprint road bike alloy frame carbon forks Price: $1161.41
Id: 3 Desc.: Colnago Move road bike alloy frame carbon forks Price: $1283.14

Então, programando sempre orientado a interfaces e ainda podendo abstrair as dependências, o projeto fica muito mais desacoplado e muito mais escalável.

Vale a pena lembrar que o Spring Framework é um universo de mais alguns bons projetos, tanto para Java quando para .Net. Há um projeto para aspectos, para controle de transações, log, etc.
Visite o site deles e confira.

E eu ainda vou ter uma Pinarello Dogma ;)