none
Error al eliminar entidad tras modificarla (arquitectura 3 capas + EF6 + patrón repositorio + patrón uow) RRS feed

  • Pregunta

  • Tengo un problemilla al eliminar, expongo el caso:

    Cargo en el grid los registros:

    Capa UI

    public manPaises()        
    {            
    InitializeComponent();            
    gcPaises.DataSource = objPaises.obtenerPaises();
    }

    Capa de lógica de negocio

    public IEnumerable<Pais> obtenerPaises()        
    {           
    IEnumerable<Pais> paisOrdenado = (IEnumerable<Pais>)unit.PaisesRepositorio.GetTodos();           
    return paisOrdenado.OrderByDescending(x => x.PaisId);       
    }

    si inserto y luego elimino sin problemas, si al cargar el grid elimino, sin problemas, pero si modifico un registro y quiero eliminarlo me da el siguiente error:

    Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.

    Lo que si me he dado cuenta es que estado cuando Modifico se queda en Modified mientras que cuando inserto o elimino sin modificar el registro es Unchanged

    Esto es lo que tengo en el repositorio base

    public void Actualizar(T entidad)        
    {            
    dbSet.Attach(entidad);            
    context.Entry(entidad).State = EntityState.Modified;       
    }        public void Crear(T entidad)        {            dbSet.Add(entidad);        }        public void Eliminar(T entidad)        {            if (context.Entry(entidad).State == EntityState.Detached)   
    {                dbSet.Attach(entidad);            }            dbSet.Remove(entidad);        }        public void Eliminar(int Id)        {            T entidad = dbSet.Find(Id);            if (entidad != null)            {                dbSet.Remove(entidad);            }        }

    País tienes relaciones con otras entidades

    Gracias por la ayuda

    jueves, 13 de julio de 2017 12:44

Todas las respuestas

  • Hola

    El problema que tienes es que no estas utilizando bien el patrón de diseño Unit of Work, te recomiendo no usarlo por ahora. Lo que si debes hacer es declarar el contexto en un bloque using.

    public void Create(T entity)
            {
                using (FacContext context = new FacContext())
                {
                    //Tu código
                }
            }

    Método Delete

    public void Delete(T entity)
            {
                using (FacContext context = new FacContext())
                {
                    context.Entry(entity).State = EntityState.Deleted;
                    context.SaveChanges();
                }
            }

    Me parece que cuando haces la segunda operación la entidad que persistes queda fuera de contexto. Has esta modificación y nos avisas.

    Saludos.


    Pedro Ávila
    "El hombre sabio querrá estar siempre con quien sea mejor que él."
    Lima - Perú



    viernes, 14 de julio de 2017 3:54
  • Hola

    Actualice mi pregunta anterior.


    Pedro Ávila
    "El hombre sabio querrá estar siempre con quien sea mejor que él."
    Lima - Perú

    viernes, 14 de julio de 2017 12:14
  • Hola Pedro,

    El problema es que no puedo prescindir del patrón UoW en el proyecto de winforms (no es que yo no quiera sino que lo han dejado estipulado así, lo cual desde mi punto de vista a no ser que se plantee más adelante cambiar de proveedor no le veo sentido ya que ef6 ya es una unidad de trabajo en sí), el tema tengo claro que es como se está usando y como se debe usar en una aplicación winforms (en una aplicación asp.net mvc no tengo el mayor problema al respecto), el problema se donde está y es que cada vez que hago uso de la parte de la lógica de negocio cargo un nuevo contexto y por lo tanto lo que haga queda fuera del contexto inicial, la única capa que conoce la capa de UI es la de la lógica de negocio (bueno las entidades (POCO) que también obviamente las conoce el resto)  por eso era lo de como usar correctamente este patrón en una aplicación winforms, expongo los pasos:

    1. cargo el FormPadre (manPaises.cs) y relleno la grilla haciendo uso de la lógica de negocio

    public manPaises()        
    {            
        InitializeComponent();            
        gcPaises.DataSource = objPaises.obtenerPaisesBinding();  
    }
     

    Esta es la parte de negocio de cPaises.cs (Solo expongo el método al que llamo dejo fuera del resto para no poner mucha 'parrafada')

    public class cPaises : AbstractValidator<Pais>    
    {        
        UnitOfWork unit = new UnitOfWork();    	
        
        public BindingList<Pais> obtenerPaisesBinding()        
        {            
            return unit.PaisesRepositorio.getDatosBinding();      
        }
    }

    Y esta la parte del repositorio base

    public BindingList<T> getDatosBinding()        
    {            
        var tabla = context.Set<T>();            
        tabla.Load();            
        return tabla.Local.ToBindingList<T>();        
    }

    Hasta aquí todo correcto.

    2. Cuando ejecuto en alguna acción, que pasa, que vuelve instanciar el obj uow y por lo tanto lo que haga estaría fuera del contexto inicial, ejemplo modificar:

    private void modificarPais()        {             Pais pais = objPaises.obtenerPaisPorId(Convert.ToInt32(gvPaises.GetRowCellValue(gvPaises.FocusedRowHandle, gvPaises.Columns["PaisId"])));      XtraForm frmPais = new Forms.frmPais(pais);     DialogResult dr = frmPais.ShowDialog();   

    if(dr == DialogResult.OK) ..... (reload el grid)            }

    ObtenerPaisPorID está dentro de la parte lógica y ya  por lo tanto vuelve a instanciar uow y carga un nuevo contexto quedando fuera del original      

    public class UnitOfWork : IUnitOfWork    
    {        
        private DexaContext context;        
    
        public UnitOfWork()        
        {            
            if (String.IsNullOrEmpty(cConexionBD.connectionString))
            {                
                this.context = new DexaContext();            
            }else{                
                this.context = new DexaContext(cConexionBD.connectionString);            
            }        
         }    
         ....
    }

    Por ahí van los tiros, por eso es ¿como se debe utilizar en un proyecto winforms correctamente el patrón uow teniendo en cuenta que la capa UI solo debe conocer la capa de lógica de negocios nunca la de acceso a datos?.

    He estado viendo algún ejemplo en devexpress ( que es el que utilizamos ) y expone el data access a la UI, no muy correcto a mi parecer pero para los ejemplos vale, la lógica la realiza en el mismo form.

    Muchas gracias por la ayuda


    Edito:

    Se me pasó indicar el método GuardarCambios que se encuetra en la uow al cual llamo para el insertar, actualizar o eliminar:

    public void GuardarCambios()        
    {            
        using(var transaccion = context.Database.BeginTransaction())            
    {   
                 
    try{                    
        this.context.SaveChanges();                    
        transaccion.Commit();                
    }catch (OptimisticConcurrencyException)
       ....
    }


    • Editado JonathanFS domingo, 16 de julio de 2017 5:03
    domingo, 16 de julio de 2017 3:22
  • El problema tras diversas pruebas me he dado cuenta que no está en el uso en sí de la uow sino en la entidad, este es el problema a ver si alguien me puede ayudar:

    Cuando se trata de una entidad simple, sin propiedades de navegación, tanto la inserción como modificado y posterior eliminado lo hace correctamente (incluso tras haberse editado la entidad) el problema lo tengo cuando es una entidad que  tiene propiedades de navegación, aun estando las entidades con las cuales está relacionada con un count = 0, por ejemplo:

    Paises: 1 -> *  :Clientes
    Paises: 1 -> * : Proveedores

    Aun no teniendo registros las tablas de Clientes y Proveedores cuando modifico la entidad de país ya me da el siguiente error:  

    Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. (Indicado en el primer post)

    Creo haber leído en algún lado un post del amigo Tuttini que cuando una entidad tiene propiedades de navegación hay que hacer algo pero por más que busco al respecto no encuentro ahora mismo nada, alguien me puede dar luz con esto?

    Gracias a todos :-)

    domingo, 16 de julio de 2017 6:54
  • Hola

    Hay una cosa partiendo que es un ORM y lo que es un context(Contexto)
    El contexto se encarga de obtener y manejar la conexión a la base de datos, las operaciones hechas a la base de datos en transacción y de trabajar con las entidades para convertir las operaciones en sentencias de base de datos, entre otras cosas.
    abrir y cerrar el contexto significa básicamente abrir y cerrar una conexión de base de datos
    cada vez que abres y cierras un contexto son dos contextos diferentes, los contextos no comparten información entre ellos.
    los contextos también tienen información adicional como un nivel de caché para conocer las entidades que se han asociado al contexto.
    Analizando el código
    Entidad e = new Entidad(...);
    AbreContexto();
    EF.Crear(e);
    CierraContexto();
    AbreContexto();
    EF.Delete(e); //error! e no está en contexto
    CierraContexto();

    Solución

    public void DeleteTalla(Talla entity)
            {
                var local = Context.Set<Talla>()
                    .Local
                    .Remove(entity);
                if (local != null)
                    Context.Entry(local).State = EntityState.Detached;
         
                Context.Entry(entity).State = EntityState.Delete;
                //TrySaveChanges();
            }

    En este link Miguel Muñoz Serafín explica como implementar la unidad de trabajo

    Introduccion al desarrollo de aplicaciones Web con ASP NET MVC 3a Sesion

    Introduccion al desarrollo de aplicaciones Web con ASP NET MVC 4a Sesion

    El ejemplo es en ASP.NET MVC pero solo cambia la capa de presentación no debes tener problemas en la implementación.

    Este código no lo he probado adaptalo a tus necesidades, derrepente tengas que comentar el remove


    Pedro Ávila
    "El hombre sabio querrá estar siempre con quien sea mejor que él."
    Lima - Perú




    domingo, 16 de julio de 2017 16:48