none
Entity Framework + UnitOfWork RRS feed

  • Pergunta

  • Boa tarde,

    Estou com uma duvida ao usar UnitOfWork com EF.

    Com os exemplos que encontrei, eu crio uma classe static, com uma propriedade static(contexto), e utilizo nas classes de repositório.

    A dúvida é, como o objeto a classe e o objeto são estáticos, será compartilhado por todos os usuários.

    Existe uma maneira melhor de usar UnitOfWork ( De preferencia não usando repositório genérico).

    Uso a versão 4.4 do EF.

    Desde já, obrigado!

    segunda-feira, 2 de dezembro de 2013 18:25

Respostas

  • Sim, concerteza ... na verdade é o mesmo contexto mas, instância diferentes, mas, existe uma forma de contornar isso amigo! 

    Assim!

    Coloque um Construtor com Parametro passando o seu Contexto nesse Construtor!

    Exemplo!

    public class Repository<T>: IRepository<T> where T: class, new()
    {
    	public System.Data.Entity.DbContext Context 
        {
    		get;
    		private set;
        }
    	public Repository()
    	{
    		this.Context = Activator.CreateInstance<DatabasePhoneEntities>();
    		this.Model = this.Context.Set<T>();
    	}
    	public Repository(DbContext Context)
    	{
    		this.Context = Context;
    		this.Model = this.Context.Set<T>();
    	}
    }


    Ai na codificação se no caso for utilizar Entidades que elas precisam trabalhar no mesmo contexto

    ficaria assim:

    DatabasePhoneEntities DbMaster = new DatabasePhoneEntities();
    Repository<Notice> Db = new RepositoryNotice(DbMaster);
    Repository<Credit> DbCredit = new RepositoryCredit(DbMaster);
    

    Ou seja a mesma instância do contexto é repassada nos repositorios, não trazendo o erros que você já relatou !!!

    OK!

    Faça os testes funciona perfeitamente!


    Fulvio Cezar Canducci Dias

    terça-feira, 3 de dezembro de 2013 12:51

Todas as Respostas

  • Eu não indicaria mesmo utilizar Static, pode criar uma grande sobrecarga no seu projeto e pode ser até prejudicial pelo fato que você mesmo relatou!

    A melhor maneira é utilizando Repositório Generico ... !!!

    Se pode criar e ficar repetindo todo as vezes seu código por isso utilizamos repositorio generico que a pela tipagem fica melhor de se trabalhar mas, ai é de você

    pode criar uma Interface e gerar para cada Entidade sua unidade de trabalho ... !!! mas, vai ter que ficar reescrevendo coisas que no repositorio generico seria muito mais simples e melhor uma vez só!


    Fulvio Cezar Canducci Dias


    segunda-feira, 2 de dezembro de 2013 23:44
  • Mas mesmo que eu faça isso, ainda terei problema, já que será um contexto por repositório.

    Já que lido com objetos complexos, ainda terei o problema de objeto modificado em outro contexto.

    Não queria usar repositório genérico, porque já tenho a implementação.

    terça-feira, 3 de dezembro de 2013 11:03
  • Mas mesmo que eu faça isso, ainda terei problema, já que será um contexto por repositório.

    Já que lido com objetos complexos, ainda terei o problema de objeto modificado em outro contexto.

    Não queria usar repositório genérico, porque já tenho a implementação.

    Vamos lá! 

    Eu lido com objeto Complexos (Cite ????)

    Você não é obrigado a usar Genérico mas, imagine criar um interface e gerar todos os unitofwork de cada entidade ??? vai causar repetição de código e na POO isso não é aceitavel !!! entendeu ?


    Fulvio Cezar Canducci Dias

    terça-feira, 3 de dezembro de 2013 11:46
  • Concordo que não é viável fazer isso.

    O problema que eu tinha era que, quando cada repositório tem seu contexto, quando ia fazer updates, gerava problema.

    Exemplo, eu tenho um objeto A, que tem como propriedade um objeto B.

    Como os objetos são carregados em diferentes contextos, ao persistir mudança em A, da erro.

    terça-feira, 3 de dezembro de 2013 12:44
  • Sim, concerteza ... na verdade é o mesmo contexto mas, instância diferentes, mas, existe uma forma de contornar isso amigo! 

    Assim!

    Coloque um Construtor com Parametro passando o seu Contexto nesse Construtor!

    Exemplo!

    public class Repository<T>: IRepository<T> where T: class, new()
    {
    	public System.Data.Entity.DbContext Context 
        {
    		get;
    		private set;
        }
    	public Repository()
    	{
    		this.Context = Activator.CreateInstance<DatabasePhoneEntities>();
    		this.Model = this.Context.Set<T>();
    	}
    	public Repository(DbContext Context)
    	{
    		this.Context = Context;
    		this.Model = this.Context.Set<T>();
    	}
    }


    Ai na codificação se no caso for utilizar Entidades que elas precisam trabalhar no mesmo contexto

    ficaria assim:

    DatabasePhoneEntities DbMaster = new DatabasePhoneEntities();
    Repository<Notice> Db = new RepositoryNotice(DbMaster);
    Repository<Credit> DbCredit = new RepositoryCredit(DbMaster);
    

    Ou seja a mesma instância do contexto é repassada nos repositorios, não trazendo o erros que você já relatou !!!

    OK!

    Faça os testes funciona perfeitamente!


    Fulvio Cezar Canducci Dias

    terça-feira, 3 de dezembro de 2013 12:51
  • Uso do UnitOfWork com Generic é bem simples e muito prático.

    Interface IUnitOfWork

    namespace Exemplo.Repository
    {
        public interface IUnitOfWork : IDisposable
        {
            GenericRepository<Estado> EstadosRepository { get; }
            GenericRepository<Cidade> CidadesRepository { get; }
    		BannersRepository BannersRepository { get; }
            void Save();
        }
    }

    ExemploUnitOfWork implementando a interface

    namespace Exemplo.Repository
    {
        public class ExemploUnitOfWork : IUnitOfWork
        {
            public ExemploUnitOfWork()
            {
                _disposed = false;
                _context = new ExemploContext();
            }
    
            #region Private
            private bool _disposed;
            private readonly ExemploContext _context;
    
            private GenericRepository<Estado> _estadosRepository;
            private GenericRepository<Cidade> _cidadesRepository;
    		private BannersRepository _bannersRepository;
    
            public GenericRepository<Estado> EstadosRepository
            {
                get { return _estadosRepository ?? (_estadosRepository = new GenericRepository<Estado>(_context)); }
            }
    
            public GenericRepository<Cidade> CidadesRepository
            {
                get { return _cidadesRepository ?? (_cidadesRepository = new GenericRepository<Cidade>(_context)); }
            }
    		
            public BannersRepository BannersRepository
            {
                get { return _bannersRepository ?? (_bannersRepository = new BannersRepository(_context)); }
            }
    
            public void Save()
            {
                _context.SaveChanges();
            }
    
            protected virtual void Dispose(bool disposing)
            {
                if (!_disposed)
                {
                    if (disposing)
                    {
                        _context.Dispose();
                    }
                }
                _disposed = true;
            }
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        }
    }

    Repositório Generico (Get, Delete, Update, Etc)

    namespace Exemplo.Repository
    {
        public class GenericRepository<TEntity> : IDisposable where TEntity : class
        {
            private bool disposed = false;
            internal DbContext context;
            internal DbSet<TEntity> dbSet;
    
            public GenericRepository(DbContext context)
            {
                this.context = context;
                this.dbSet = context.Set<TEntity>();
            }
    
            public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
            {
                IQueryable<TEntity> query = dbSet;
    
                if (filter != null)
                {
                    query = query.Where(filter);
                }
    
                foreach (var includeProperty in includeProperties.Split
                    (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    query = query.Include(includeProperty);
                }
    
                if (orderBy != null)
                {
                    return orderBy(query).ToList();
                }
                else
                {
                    return query.ToList();
                }
            }
    
            public virtual IEnumerable<TEntity> GetWithQuery(string queryString, params object[] par)
            {
                return dbSet.SqlQuery(queryString, par);
            }
    
            public virtual int Execute(string queryString, params object[] par)
            {
                return context.Database.ExecuteSqlCommand(queryString, par);
            }
    
            public virtual TEntity GetByID(object id)
            {
                return dbSet.Find(id);
            }
    
            public virtual void Insert(TEntity entity)
            {
                dbSet.Add(entity);
            }
    
            public virtual void Delete(object id)
            {
                TEntity entityToDelete = dbSet.Find(id);
                Delete(entityToDelete);
            }
    
            public virtual void Delete(TEntity entityToDelete)
            {
                if (context.Entry(entityToDelete).State == EntityState.Detached)
                {
                    dbSet.Attach(entityToDelete);
                }
                dbSet.Remove(entityToDelete);
            }
    
            public virtual void Update(TEntity entityToUpdate)
            {
                dbSet.Attach(entityToUpdate);
                context.Entry(entityToUpdate).State = EntityState.Modified;
            }
    
            protected virtual void Dispose(bool disposing)
            {
                if (!this.disposed)
                {
                    if (disposing)
                    {
                        context.Dispose();
                    }
                }
                this.disposed = true;
            }
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        }
    }

    Outros Repositórios Específicos

    namespace Exemplo.Repository
    {
        public class BannersRepository : GenericRepository<Banner>
        {
            private ExemploContext dbContext;
    
            public BannersRepository(ExemploContext context)
                : base(context)
            {
                this.dbContext = context;
            }
    
            public IEnumerable<Banner> GetBannersOnline()
            {
                var itens = from item in dbContext.Banners
                         where item.Publicar
                         select item;
                return itens;
    
            }
        }
    }

    Exemplo de uso

    namespace Exemplo.Controllers
    {
    	public class CidadesController : Controller
    	{
    		private readonly IUnitOfWork Repository = new ExemploUnitOfWork();
            
    		public ActionResult Index()
            {
    			//Busca cidades com id > 50 e inclui o estado.
    			var cidades = Repository.CidadesRepository.Get(includeProperties: "Estado", filter: p => p.nIdCidade > 50);
    			
    			//Banners tem todos os metodos do genetico e os especificos
    			var banners = Repository.BannersRepository.GetBannersOnline();
    		
                return View();
            }
    	}
    }

    terça-feira, 3 de dezembro de 2013 12:56
  • Para não afetar muito o projeto, o que eu fiz.

    Eu criei uma classe que todos meus repositórios vão herdar para controlar o contexto.

    Nessa classe, eu verifico se existe o contexto, senão crio um novo, isso baseado numa implementação do UnitOfWork que funciona com threads, e que pelo que entendi, ele cria uma thread(controlando com Guid)

    Acha viável? Senão usarei seu exemplo.


    terça-feira, 3 de dezembro de 2013 12:57
  • Faz o seguinte eu nunca fiz assim é novo isso pra mim! (UNITOFWORK com Threads)

    eu acho legal essa troca de informações para um fim em comum quem sabe melhor desempenho!!!

    O Exemplo que te passei é injeção de dependencia e não afeta em nada o projeto, mas, 

    o que você relatou eu nunca fiz compartilha para gente testar quem sabe não é a melhor solução e mais performática!!!


    Fulvio Cezar Canducci Dias

    terça-feira, 3 de dezembro de 2013 13:03
  • No meu exemplo o Context é instanciado no construtor do UnitOfWork e passado via construtor para cada repositório.

    Assim tenho somente uma instancia do Context distribuída entre os repositórios.

    Como você falou que trabalha com vários Contexts, não pode fazer o mesmo processo? basta passar no construtor do repositório a instancia correta.

    Ex: new GenericRepository<Cidade>(Context1), new GenericRepository<Cidade>(Context2), etc

    terça-feira, 3 de dezembro de 2013 13:08
  • Eu acho valido com certeza a troca de informações.

    Esse é o primeiro projeto que estou usando EF, e estou esbarrando em alguns problemas.

    Vou testar, e depois aviso se funciona.

    terça-feira, 3 de dezembro de 2013 13:18
  • Eu acho valido com certeza a troca de informações.

    Esse é o primeiro projeto que estou usando EF, e estou esbarrando em alguns problemas.

    Vou testar, e depois aviso se funciona.

    Ok, sorte!

    Fulvio Cezar Canducci Dias

    terça-feira, 3 de dezembro de 2013 13:35