Posts Tagged ‘.Net’

DAO Genérico e o Entity Framework – Parte 3

segunda-feira, março 8th, 2010

Finalmente, vamos agora para a implementação do nosso DAO Genérico usando o Entity Framework. Na Parte 2 nós criamos a interface IDAO e também o ContextManager, um helper para a criação do contexto. A implementação será chamada GenericDAO e definirá, através de generics, dois tipos, sendo que um será o tipo do objeto que será persistido e o outro será o tipo (classe) do contexto do Entity Framework. Vamos para o código!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using Net.Bragil.Data.Context;
 
namespace Net.Bragil.Data.DAO
{
 
    /// Repositório genérico para o Entity Framework com as operações mais 
    /// comuns de persistência, como inserção, atualização,
    /// exclusão e seleção.
    public class GenericDAO<E, C> : IDAO<E, C>, IDisposable
        where E : EntityObject
        where C : ObjectContext
    {
 
        /// Contexto do Entity Framework
        private C context;
 
        /// Contexto do Entity Framework
        public C Context
        {
            get { return context; }
            set { context = value; }
        }
 
 
        /// Retorna o nome do EntitySet do objeto persistente
        private string entitySetName;
 
        /// Retorna o nome do EntitySet do objeto persistente
        protected string EntitySetName
        {
            get
            {
                if (String.IsNullOrEmpty(entitySetName))
                {
                    entitySetName = GetEntitySetName(typeof(E).Name);
                }
 
                return entitySetName;
            }
        }
 
        /// Construtor padrão, sem argumentos. Obtém o contexto do 
        /// Entity Framework usando o ContextManager.
        public GenericDAO()
        {
            // Obtém o contexto
            this.context = ContextManager.GetContext<C>();
        }
 
        /// Insere um novo objeto persistente
        public void Insert(E entity)
        {
            context.AddObject(EntitySetName, entity);
        }
 
        /// Atualiza um objeto existente.
        public virtual void Update(E entity)
        {
            EntityKey key;
            object originalItem;
 
            if (entity.EntityKey == null)
                // Obtém o entity key do objeto que será atualizado
                key = Context.CreateEntityKey(EntitySetName, entity);
            else
                key = entity.EntityKey;
            try
            {
                // Obtém o objeto original
                if (Context.TryGetObjectByKey(key, out originalItem))
                {
                    if (originalItem is EntityObject &&
                        ((EntityObject)originalItem).EntityState != EntityState.Added)
                    {
                        // Autaliza o objeto
                        context.ApplyPropertyChanges(key.EntitySetName, entity);
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        /// Exclui um objeto persistente
        public void Delete(E entity)
        {
            context.DeleteObject(entity);
        }
 
        /// Retorna um objeto que satisfaça a cláusula passada como parâmetro.
        public E SelectOne(Expression<Func<E, bool>> where)
        {
            return context.CreateQuery<E>(EntitySetName).Where(where).FirstOrDefault();
        }
 
 
        /// Salva as alterações no banco de dados.
        public void SaveChanges()
        {
            context.SaveChanges();
        }
 
        /// Retorna todos os objetos persistentes.
        public List<E> SelectAll()
        {
            return context.CreateQuery<E>(EntitySetName).ToList();
        }
 
        /// Retorna um IQueryable com todos os objetos
        public IQueryable<E> QueryAll()
        {
            return context.CreateQuery<E>(EntitySetName).AsQueryable<E>();
        }
 
        /// Retorna todos os objetos usando paginação.
        public List<E> SelectAll(int maximumRows, int startRowIndex)
        {
            return context.CreateQuery<E>(EntitySetName).Skip<E>(startRowIndex).Take(maximumRows).ToList();
        }
 
        /// Retorna um IQueryable com todos os objetos usando paginação
        public IQueryable<E> QueryAll(int maximumRows, int startRowIndex)
        {
            return context.CreateQuery<E>(EntitySetName).Skip<E>(startRowIndex).Take(maximumRows);
        }
 
        /// Retorna todos os objetos que satisfaçam a cláusula passada
        public List<E> SelectWhere(Expression<Func<E, bool>> where)
        {
            return context.CreateQuery<E>(EntitySetName).Where(where).ToList();
        }
 
        /// Retorna todos os objetos que satisfaçam a cláusula passada, usando paginação
        public List<E> SelectWhere(Expression<Func<E, bool>> where, int maximumRows, int startRowIndex)
        {
            return context.CreateQuery<E>(EntitySetName).Where(where)
 
                          .Skip<E>(startRowIndex).Take(maximumRows).ToList();
        }
 
        /// Retorna o número de objetos
        public int GetCount()
        {
            return context.CreateQuery<E>(EntitySetName).Count();
        }
 
        /// Retorna o número de objetos que satisfaçam a cláusula passada
        public int GetCount(Expression<Func<E, bool>> where)
        {
            return context.CreateQuery<E>(EntitySetName).Where(where).Count();
        }
 
        /// Libera os recursos do Entity Framework.
        public void Dispose()
        {
            if (context != null)
                context.Dispose();
        }
 
        /// Retorna o nome do EntitySet, possibilitando a criação de métodos genéricos.
        private string GetEntitySetName(string entityTypeName)
        {
            var container = context.MetadataWorkspace
                                            .GetEntityContainer(context.DefaultContainerName,
                                                                       DataSpace.CSpace);
            string entitySetName = (from meta in container.BaseEntitySets
                                    where meta.ElementType.Name == entityTypeName
                                    select meta.Name).FirstOrDefault();
 
            return entitySetName;
        }
    }
}

DAO Genérico e o Entity Framework – Parte 2

quinta-feira, março 4th, 2010

Na primeira parte deste artigo, criamos o banco de dados e geramos o contexto do Entity Framework para o modelo. Agora, vamos começar a definir o componente para o DAO Genérico. Segue uma lista dos requisitos para o componente:

Deverá oferecer a possibilidade de trabalhar independente dos tipos envolvidos

Ou seja, poderemos usar o mesmo componente para persistir qualquer objeto, independente do contexto usado. Isso é facilmente conseguido através do uso de Generic Types, sendo possível parametrizar os tipos que serão trabalhados durante as operações de persistência.

Deverá simplificar o desenvolvimento para as rotinas básicas de persistência

O objetivo será tornar o desenvolvimento o mais simples possível, procurando encapsular todo o “trabalho sujo” e criar uma interface limpa e amigável para o programador.

Baseado nas especificações acima, segue a Interface do componente DAO para o Entity Framework:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
 
namespace Net.Bragil.Data.DAO
{
    /// Interface para a implementação do padrão Repository usando o Entity Framework.
    public interface IDAO<E, C>
    {
        /// Contexto do Entity Framework
        C Context { get; set; }
 
        /// Inserção
        void Insert(E entity);
 
        /// Atualização
        void Update(E entity);
 
        /// Exclusão
        void Delete(E entity);
 
        /// Retorna o objeto que satisfaça a cláusula passada como argumento (cláusula WHERE)
        E SelectOne(Expression<Func<E, bool>> where);
 
        /// Retorna todos os objetos de um tipo
        List<E> SelectAll();
 
        /// Retorna os objetos usando paginação
        List<E> SelectAll(int maximumRows, int startRowIndex);
 
        /// Retorna todos os objetos que satisfaçam a cláusula passada
        List<E> SelectWhere(Expression<Func<E, bool>> where);
 
        /// Retorna os objetos que satisfaçam a cláusula passada, usando paginação
        List<E> SelectWhere(Expression<Func<E, bool>> where, int maximumRows, int startRowIndex);
 
        /// Retorna um objeto IQueryable, possibilitando formar queries usando expressões Lambda
        IQueryable<E> QueryAll();
 
        /// Retorna um IQueryable com os objetos usando paginação
        IQueryable<E> QueryAll(int maximumRows, int startRowIndex);
 
        /// Retorna a quantidade de objetos persistentes.
        int GetCount();
 
        /// Retorna a quantidade de objetos persistentes que satisfaçam a cláusula WHERE
        int GetCount(Expression<Func<E, bool>> where);
    }
}

Como você pode ver, o componente oferecerá a maioria dos recursos de persistência, de uma forma muito mais amigável do que usando o Entity Framework diretamente.

Foi necessário também a criação de uma classe auxiliar para instanciação do contexto do Entity Framework usando a estratégia “One-Per-Request”, ou seja, é criada apenas uma instância do contexto por requisição. O objeto do contexto é armazenado em HttpContext.Current.Items, de modo que as chamadas subsequentes dentro da mesma requisição retornam o contexto anteriormente instanciado e armazenado na requisição. Segue o código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System.Web;
using System.Data.Objects;
 
namespace Net.Bragil.Data.Context
{
    /// Classe gerenciadora do contexto do Entity Framework. Utiliza generics 
    /// para determinar em tempo de execução o tipo do contexto e Reflection 
    /// para instanciar o tipo genérico. Armazena o contexto no HttpContext, 
    /// de modo que não seja necessário instanciar o contexto nas próximas 
    /// chamadas dentro da requisição web.
    public sealed class ContextManager
    {
 
        /// Construtor privado, não será possível instanciar a classe usando new
        private ContextManager()
        {
 
        }
 
        /// Obtém o contexto do Entity Framework usando generics.
        public static T GetContext<T>()
            where T: ObjectContext
        {
            string ocKey = "ocm_" + HttpContext.Current.GetHashCode().ToString("x");
 
            if (HttpContext.Current != null)
            {
                if (!HttpContext.Current.Items.Contains(ocKey))
                {
                    // Instancia o contexto através de Reflection
                    T ctx = typeof(T).GetConstructor(System.Type.EmptyTypes)
                    				 .Invoke(System.Type.EmptyTypes) as T;
 
                    // Armazena na requisição
                    HttpContext.Current.Items.Add(ocKey, ctx);
                }
 
                return HttpContext.Current.Items[ocKey] as T;
            }
            else
                // Caso a aplicação não seja web, instancia e retorna o contexto.
                return typeof(T).GetConstructor(System.Type.EmptyTypes)
                				.Invoke(System.Type.EmptyTypes) as T;
        }
    }
}

Já estamos quase lá! No próximo artigo vamos desenvolver a implementação da classe GenericDAO.

DAO Genérico e o Entity Framework – Parte 1

quinta-feira, março 4th, 2010

Este é o primeiro de uma série de 3 artigos abordando a criação de um DAO Genérico para o Entity Framework. Para quem não sabe, o DAO (Data Access Object) é um Design Pattern que abstrai as complexidades da camada de acesso a dados, oferendo uma interface de mais alto nível que possibilita um menor acoplamento com a tecnologia de persistência. Para um melhor entendimento, vamos apresentar de forma prática a criação da camada de persistência para um modelo simples de banco de dados, a geração do contexto do Entity Framework, o desenvolvimento do DAO e finalmente um pequeno exemplo mostrando os recursos do componente criado.

Os requisitos para o desenvolvimento proposto são os seguintes:

  • Visual Studio 2008 SP1
  • .Net Framework 3.5
  • Banco de dados SQL Server Express Edition

Obs: é importante salientar que há outros bancos de dados cujos drivers possuem suporte ao Entity Framework. Verifique se o banco de dados que você usa possui driver para .Net com suporte ao Entity Framework.

Vamos trabalhar com um modelo de dados bem simples, mas suficiente para o nosso exemplo, conforme segue:

db

Basicamente, um cadastro simples de pessoas com endereço completo. Depois de definido o modelo de dados, vamos realizar a geração do contexto do Entity Framework (edmx). Se você não sabe como fazer isso, leia este artigo.

Pronto, já temos o banco de dados e o contexto do Entity Framework. O tipo do objeto de contexto do Entity Framework foi nomeado como DbEntities.

entities

No próximo artigo vamos abordar o desenvolvimento do componente DAO.

Trabalhando com arquivos .INI em .Net com Nini

quarta-feira, agosto 19th, 2009

Os arquivos de configuração .INI há um bom tempo foram substituídos no .Net pelos arquivos XML, muito mais flexíveis e padronizados. Entretanto, quando trabalhamos com sistemas legados, seja para fazer integrações, migrações ou o que quer que seja, acabamos nos deparando com os saudosos arquivos .INI. Ao invés de criar rotinas caseiras para trabalhar com estes arquivos, que tal uma biblioteca em C# que cumpra esse papel com louvor? A biblioteca Nini faz isto e muito mais: além dos arquivos .INI, ela abstrai o acesso a arquivos de configuração XML, .Net Config, Registry e argumentos de linha de comando. O site do projeto não possui nada, apenas os links para poder baixar, mas a documentação vem junto com o arquivo baixado.

Segue um exemplo do próprio manual da Nini:

1
2
3
4
5
; MyApp.ini
[Logging]
File Name = MyApp.log
MessageColumns = 5
MaxFileSize = 40000000000000
1
2
3
4
5
6
7
8
// Usando C#                                                                                                                
using Nini.Config;
 
IConfigSource source = new IniConfigSource("MyApp.ini");
 
string fileName = source.Configs["Logging"].Get("File Name");
int columns = source.Configs["Logging"].GetInt("MessageColumns");
long fileSize = source.Configs["Logging"].GetLong("MaxFileSize");
1
2
3
4
5
6
7
8
'E usando VB.Net
Imports Nini.Config
 
Dim source As New IniConfigSource("MyApp.ini")
 
Dim fileName As String = source.Configs("Logging").Get("File Name")
Dim columns As Integer = source.Configs("Logging").GetInt("MessageColumns")
Dim fileSize As Long = source.Configs("Logging").GetLong("MaxFileSize")

Álcool ou Gasolina? – Aplicativo para Windows Mobile 6 para tirar sua dúvida

sexta-feira, agosto 7th, 2009

Neste post vou apresentar um aplicativo para Windows Mobile 6 que recebe como entrada o valor do litro do álcool, da gasolina e exibe uma mensagem dizendo qual dos dois é mais vantajoso abastecer, bastante útil quando o seu veículo é flex.

Requisitos:

O código é bem simples, o valor do litro do álcool é dividido pelo valor do litro da gasolina, se o resultado for maior ou igual a 0,7 compensa abastecer com gasolina, caso contrário, abasteça com álcool.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using System;
 
using System.Text;
using System.Windows.Forms;
 
namespace AlcoolGasolina
 
{
 
    public partial class frmPrincipal : Form
    {
        public frmPrincipal()
        {
            InitializeComponent();
        }
 
        /// Método de validação da entrada. Considera inválidos strings vazias e 
        /// valores menores ou iguais a zero.
        private bool isValid(string valor)
        {
            // Não deve ser uma string vazia ou nula
            if (String.IsNullOrEmpty(valor))
                return false;
            try
            {
                // deve ser um valor numérico válido
                double num = Convert.ToDouble(valor);
 
                // não deve ser menor ou igual a zero
                if (num > 0)
                    return true;
                else
                    return false;
            }
 
            catch (Exception)
            {
                return false;
            }
        }
 
 
		/// Verifica se os valores digitados são válidos e, em caso positivo, 
        /// faz o cálculo e apresenta a mensagem para o usuário informando
        /// qual é mais viável abastecer, álcool ou gasolina. Se os valores 
        /// não forem válidos para o cálculo, será exibida uma mensagem de 
        /// alerta, informando que os valores de entrada são inválidos.
        private void btnCalcular_Click(object sender, EventArgs e)
        {
            if (isValid(txtAlcool.Text.Trim()) && isValid(txtGasolina.Text.Trim()))
            {
                double alcool = Convert.ToDouble(txtAlcool.Text.Trim());
                double gasolina = Convert.ToDouble(txtGasolina.Text.Trim());
 
                double resultado = alcool / gasolina;
                if (resultado >= 0.7)
                    lblResultado.Text = "Abasteça com Gasolina";
                else
                    lblResultado.Text = "Abasteça com Álcool";
            }
            else
            {
                MessageBox.Show("Valores de entrada inválidos.",
                                "Erro",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation,
                                MessageBoxDefaultButton.Button1);
 
            }
 
            txtAlcool.Text = string.Empty;
            txtGasolina.Text = string.Empty;
        }
 
        /// Fecha a aplicação.
        private void mnuSair_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
 
        /// Menu "Sobre...", exibe uma mensagem com o nome da aplicação, 
        /// nome do autor e endereço do blog do autor.
        private void mnuSobre_Click(object sender, EventArgs e)
        {
            StringBuilder mensagem = new StringBuilder("Álcool ou Gasolina\n\n");
            mensagem.Append("Por Rogério Bragil\n\nBlog do Bragil - www.bragil.net");
            MessageBox.Show(mensagem.ToString(),
                            "Sobre",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.None,
                            MessageBoxDefaultButton.Button1);
        }
    }
}