Mapeamento com o Entity FrameWork usando 3 camadas (3° Parte)
Olá , hoje iremos continuar nosso projeto com o Entity Framework (veja aqui a 1° Parte e 2° Parte)
No segundo artigo criamos nosso mapeamento no Entity Framework, repare que o processo foi muito simples, nesta 3° parte iremos criar os métodos Crud de nossas entidades mapeadas.
Para ficar claro que estamos fazendo, crie um novo projeto do tipo Class Library na sua solução e chame ele BancoHoras.DAL este projeto irá armazenar a classe Crud.
Neste projeto, exclua a classe Class1.cs criada junto com o projeto e insira uma nova classe chamada AcessoDados.cs
Deixe esta classe conforme o exemplo abaixo:
Inclua as seguintes referências
using System.Data.Objects; using System.Data.Objects.DataClasses; using BancoHoras.Interfaces; using BancoHoras.Entities |
As referências acima são necessárias para que nosso sistema funcione, o System.Data.Objects e o System.Data.Objects.DataClasses incluem as classes bases do Entity Data Model, para saber mais, consulte os link´s:
http://msdn.microsoft.com/en-us/library/system.data.objects.dataclasses.aspx
http://msdn.microsoft.com/en-us/library/system.data.objects.aspx
A referência BancoHoras.Entities , representam o projeto que contém nosso modelo de entidade e a referência BancoHoras.Interfaces será comentada na 4° parte do artigo, por enquanto apenas inclua a referência.
public class AcessoDados<T> : EntityObject where T : EntityObject, IEntityID {
private BancoDeHorasEntities context;
public string NomeEntidade { get { T entidade = Activator.CreateInstance<T>(); return entidade.GetType().Name; }
}
/// <summary> /// Método para Buscar Entidade por ID. /// </summary> /// <param name="id">Id da Entidade</param> /// <returns>Entidade</returns> public T GetById(int id) { using (context = new BancoDeHorasEntities()) { var entidade = (from e in context.CreateQuery<T>(NomeEntidade) where e.Id == id select e).FirstOrDefault(); return entidade; } }
/// <summary> /// Método para retornar todos os registros da tabela /// </summary> /// <returns>Entidade</returns> public List<T> GetAll() { using (context = new BancoDeHorasEntities()) { var entidade = from e in context.CreateQuery<T>(this.NomeEntidade) select e; return entidade.ToList(); } }
/// <summary> /// Método para inserir uma entidade /// </summary> /// <param name="entidade"></param> public void Criar(T entidade) { using (context = new BancoDeHorasEntities()) { context.AddObject(NomeEntidade, entidade); context.SaveChanges(); } }
/// <summary> /// Método para atualizar uma entidade /// </summary> /// <param name="entidade"></param> public void Atualizar(T entidade) { using (context = new BancoDeHorasEntities()) { var original = (from e in context.CreateQuery<T>(NomeEntidade) where e.Id == entidade.Id select e).FirstOrDefault(); context.ApplyPropertyChanges(original.EntityKey.EntitySetName, entidade); context.SaveChanges(); } }
/// <summary> /// Método para excluir uma entidade /// </summary> /// <param name="entidade"></param> public void Excluir(int id) { using (context = new BancoDeHorasEntities()) { var entidade = this.GetById(id); context.DeleteObject(entidade); context.SaveChanges(); } } } |
A classe acima esta herdando da interface IEntityID esta interface faz parte do projeto BancoHoras.Intefaces, por isso não iremos nos preocupar com ela neste momento, outra herança que esta nesta classe é a EntityObject. O trecho T : EntityObject, IEntityID
esta dizendo que todos os objetos utilizados na instancia da classe deverão implementar o EntityObject e IEntityId, isso é necessário para que possamos criar nossos métodos genéricos.
O BancoHorasEntities é a classe do nosso modelo de entidades criado no EntityFramework, estamos utilizando ele como um campo da classe para que seja utilizado em todos os métodos.
A intenção deste projeto é criar o acesso Crud de forma genérica, ou seja, apenas uma classe que será reutilizada para todas as entidades, por isso iremos criar uma propriedade chamada NomeEntidade, esta propriedade irá receber o objeto genérico e buscar o nome do mesmo para passar para os métodos necessários.
O método GetById recebe o id da entidade e retorna a entidade preenchida do banco de dados, repare que estamos fazendo uso do Linq e EF para abstrair todo o acesso ao banco.
O método GetAll irá retornar todas os registros do banco do tipo da entidade, sem efetuar nenhum filtro nos mesmo. Já o método Criar, irá inserir no banco de dados um registro, o método Atualizar, irá receber uma entidade (EntityState) e atualizar a mesma no banco de dados, repare que neste método temos uma implementação diferenciada, no próximo tópico iremos falar dele, já o método excluir irá excluir um registro do banco de dados.
Nota: Todas as entidades já foram mapeadas no EF, por isso quando passamos uma Entidade para o BancoHorasEntities e efetuamos alguma operação, o framework já executa a operação na tabela especificada no mapeamento.
Na próxima etapa deste artigo iremos finalizar e testar nosso projeto com 3 camadas..
Abraço
amigo como voce conseguil acessar as propriedades de uma classe generica em tempo de execucao ?var entidade = (from e in context.CreateQuery<T>(NomeEntidade) where e.Id == id select e).FirstOrDefault();no meu projeto gera erro…??????
Igor, Neste caso eu acesso a propriedade Id pois coloquei uma restrição na herança da minha classe, onde eu digo que qualquer entidade que for passada para a classe (generics) terá que implementar a interface (contrato) IEntityWithId, por isso eu consigo ver a propriedade Id, pois ela esta definida na interface.Veja na declaração da classe:public class AcessoDados<T> : EntityObject where T : EntityObject, IEntityIDQual o erro que você esta tendo ?Abs,
Nelson,Fiquei com uma duvida relacionada ao seu exemplo. No método que faz a exclusão, tem uma chamada para o metódo GetById, sendo que sendo que nesse momento ja foi dado new na variavel context (essa variavel é nivel de classe). E dentro do metodo GetById novamente é dado new na variavel context e consequentemente Dispose devido ao using. As linhas context.DeleteObject(entidade);context.SaveChanges();irão executar normalmente? Porque nesse momento o context foi Disposed…AbraçoLeonardo Xafranski
Quando você faz o CreateQuery<T>(Entidade) da erro. Notei que o correto seria colocar o nome do EntitySet. Fiz isso e aí passou.Com isto, eu consigo inserir e consultar, porém na hora de fazer Update ele não funciona. Fiz alguns testes acessando o contexto diretamente pela minha aplicação e ele passa sem dar erro mas não atualiza. Aí percebi que funciona se eu retirar a herança de EntityObject da minha classe.Com você aconteceu algo assim ou está funcionando normalmente?AbraçoFábio
Nelson,A mesma dúvida do Leonardo Constante ocorreu no meu caso.Além disso, quando executamos o código, após efetuar o context.DeleteObject(entidade), então temos o SaveChanges() e, neste momento ocorre o seguite erro:ObjectDisposedException was unhandledThe ObjectContext instance has been disposed and can no longer be used for operations that require a connection.Será que, pelo fato de dentro do GetById termos novamente um novo contexto, não perdemos a referência?
Eduardo, tudo bem.Não iria dar o erro pois os objetos são de instancias diferentes, o que pode estar ocorrendo é um bug que existe na versão 3.5, qual versão vc esta utilizando ?Abs,
Opa!Tudo bem nelson?A versão que estou utilizando é 4.0.
Além disso, surgiram 2 duvidas:1. Se eu não quero trabalhar com o interface para os Id, é possível trabalhar da mesma forma para outras camadas, apenas retirando a referência ou tenho que sair ajustando as camadas de negócio e dados?2. Estava efetuando uns testes, como o GetById mas por Nome (como um like) porém… quando tento fazer o where, só me vem o Id para filtro, teria também que padronizar os nomes na interface?
O GetByName é um método que pretendo fazer com que seja filtrado uma determinada propriedade que não é padronizada, pois tenho por exemplo produtonome e categorianome. Portanto, não acho correto definir na Interface um campo Name, pois cada entidade tem campos diferentes. O que você indica fazer?
Estava aqui pensando… acredito que, seria talvez válido implementar uma classe para Produto e/ou Categoria que teria métodos específicos de busca. Você acha que é uma solução boa ou existe algo mais correto de se fazer?
Opa, respostas em Ordem:R: Sim, basta criar o campo na interface e um método que utilize este campo para pesquisar, pode ser ate pesquisas linq.R: Eu recomendo você verificar uma Dependency Property e resolver a referencia dinamicamente usando reflection: Veja: http://msdn.microsoft.com/pt-br/library/system.windows.dependencyproperty.aspx
R: Neste caso depende muito do cenário, criar classes especializadas não existe nenhum mal, no entanto o Generics com o Reflection pode te auxiliar muito criar classes genericas que servirão para todos os cenários.R: Depende muito, se tiver familiariade com o Generics e Reflection, derepente pode pensar em construir de forma generica a classe (sirva para todos), mas não vejo nenhum mal tambem criar classes especializadas para estes cenários. Depende muito da aplicação.