Posts Tagged ‘Repository’

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.