domingo, 28 de julio de 2013

[Entity Framework][Code First] Asociación mucho a muchos

 

Introducción


Al momento de relacionar entidades algunas pueden necesitar asociarse con colecciones de otras entidades, pero que sucede cuando esto se necesita en ambas direcciones?, es aquí donde entran las relaciones mucho a muchos, en donde a nivel de base de datos se va a requerir de una tabla intermedia para poder implementar la relación.

En este articulo veremos como lograr esta asociación y analizaremos que pasa a nivel de base de datos, el objetivo final será lograr un modelo de objeto como el siguiente:

image

y su equivalente en el modelo de datos:

image

 

Definición de las entidades


En el proyecto “Entities” serán definidas las clases que modelan los empleados y territorios, cada uno define una propiedad del tipo colección hacia a otra.

 

public class Territory
{
    public int TerritoryID { get; set; }
    public string TerritoryDescription { get; set; }

    public virtual ICollection<Employee> Employees { get; set; }

}
public class Employee
{

    public int EmployeeID { get; set; }

    public string LastName { get; set; }
    public string FirstName { get; set; }

    public string Address { get; set; }
    public string City { get; set; }
    public string Region { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }

    public virtual ICollection<Territory> Territories { get; set; }
}

 

Definición del contexto


La clase de context, la cual hereda de Dbcontext, define las propiedades que representan cada entidad, utilizando el tipo DbSet<>.

La sobrecarga del método OnModelCreating será el responsable de la configuración del mapping, para que no quede toda el código junto en un solo sitio y sea mantenible en el tiempo se hará uso de clases separadas para cada entidad, estas clases heredan de EntityTypeConfiguration<>.

 

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public DbSet<Territory> Territories { get; set; }
    public DbSet<Employee> Employees { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new TerritoryMap());

        modelBuilder.Configurations.Add(new EmployeeMap());

        base.OnModelCreating(modelBuilder);
    }

}

En este caso se decidió utilizar la entidad del territorio para definir la relación mucho a muchos, esto se logra definiendo el HasMany()

La relación requiere definir el nombre que tendrá la tabla intermedia, así como los campos claves que actuaran en la relación.

 

public class TerritoryMap : EntityTypeConfiguration<Territory>
{
    public TerritoryMap()
    {
        HasKey(x => x.TerritoryID);
        Property(x => x.TerritoryDescription).HasColumnType("nchar").HasMaxLength(50).IsRequired();

        HasMany(x => x.Employees)
            .WithMany(x => x.Territories)
            .Map(mc =>
            {
                mc.MapLeftKey("TerrytoryID");
                mc.MapRightKey("EmployeeID");
                mc.ToTable("EmployeeTerritories");
            });
    }
}

Nota: la definición de la relación no era obligatorio hacerlo en la entidad Territory, se podría haber realizado en la clase Map del Employee sin ningún problema, solo hay que tener en cuenta cambiaran las definiciones de MapLeftKey() y MapRighKey()

El empleado define el mapeo normalmente

public class EmployeeMap : EntityTypeConfiguration<Employee>
{
    public EmployeeMap()
    {
        HasKey(x => x.EmployeeID);
        Property(x => x.LastName).HasMaxLength(20).IsRequired();
        Property(x => x.FirstName).HasMaxLength(10).IsRequired();

        Property(x => x.Address).HasMaxLength(60);
        Property(x => x.City).HasMaxLength(15);
        Property(x => x.Region).HasMaxLength(15);
        Property(x => x.PostalCode).HasMaxLength(10);
        Property(x => x.Country).HasMaxLength(15);
    }
}

Especialización del repositorio


Para poder operar con las colecciones se requiere estar dentro del contexto de EF, pero sucede que la implementación del repositorio desconecta las instancias de las entidades al devolverás para poder ser usadas desde fuera por lo tanto aplicar cambios en estas no ejecutara las acciones de actualización de forma correcta.

Es aquí donde se requiere personalizar el repositorio, por ejemplo en el repositorio del territorio definir métodos que permiten agregar o quitar empleados.

Si bien los parámetros de estos métodos son tipos de cada entidad, se podrían haber utilizado solo los IDs de las mismas

 

public class TerritoryRepository : BaseRepository<Territory>
{

    public void AddEmployees(Territory territory, List<Employee> employes)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            //marcamos el territorio para que no reciba cambios
            context.Entry(territory).State = EntityState.Unchanged;

            if (territory.Employees == null)
                territory.Employees = new List<Employee>();

            //recorremos cada empleado que se quiera asociar 
            employes.ForEach(x =>
            {
                //el empleado tampoco debe recibir cambios
                context.Entry(x).State = EntityState.Unchanged;
                //asociamos a la colecion de empleados del territorio el nuevo item
                //este si recibira cambios
                territory.Employees.Add(x);
            });
            
            context.SaveChanges();
        }
    }

    public void RemoveEmployees(Territory territory, List<Employee> employees)
    {
        //validamos que haya algo que remover
        if (employees == null || employees.Count == 0)
            return;

        using (NorthWindContext context = new NorthWindContext())
        {
            //recuperamos el terrotorio y sus empleados
            //esto es necesario porque el objeto donde se debe remover tiene que estar dentro del contexto de EF
            Territory territorySel = context.Set<Territory>().Include("Employees").FirstOrDefault(x => x.TerritoryID == territory.TerritoryID);

            if (territory.Employees == null || territory.Employees.Count == 0)
                return;

            employees.ForEach(x =>
                {
                    //localizamos al empleado dentro de la coleccion que se recupero anteriormente
                    Employee employeeRemove = territorySel.Employees.First(e => e.EmployeeID == x.EmployeeID);
                    //se remueve de la coleccion haciendo uso de la instancia
                    territorySel.Employees.Remove(employeeRemove);
                });

            context.SaveChanges();
        }
    }

}

Test – Obtener territorio con empleados asociados


Empezaremos con un test simple, en donde solo se relacionen las entidades y puedan recuperarse

[TestMethod]
public void Get_ListEmployee_Territory()
{
    TerritoryRepository repoTerritory  = new TerritoryRepository();

    Territory territoryNew = new Territory()
    {
        TerritoryDescription = "territoty 1",
        Employees = new List<Employee>()
        {
            new Employee() { FirstName = "Name 1", LastName ="LastaName 1" },
            new Employee() { FirstName = "Name 2", LastName ="LastaName 2" }
        }
    };
    repoTerritory.Create(territoryNew);

    var territorySel = repoTerritory.Single(x => x.TerritoryID == territoryNew.TerritoryID, 
                                                new List<Expression<Func<Territory,object>>>(){ x=>x.Employees });

    Assert.IsNotNull(territorySel);
    Assert.IsNotNull(territorySel.Employees);
    Assert.AreEqual(territorySel.Employees.Count, 2);

}

La creación del territorio implicara varias acciones como puede observarse en la captura del profiler

image

Se insertan los empleados, a continuación el territorio y por ultimo los registros que relacionan ambas entidades

Para recuperar las relaciones es necesario definir la propiedad como parte del Include()

image

En una sola instrucción SELECT se recuperan el territorio y los empleados asociados a esta.

 

Test – Agregar empleados a territorio existentes


Seria muy raro que al momento de crear un territorio se disponga de los empleados que se asocian al mismo, y solo estos conformen la relación, lo común es tener entidades independientes y asociarlas de forma dinámica, por lo general mediante acciones del usuario en la aplicación.

 

[TestMethod]
public void Update_ExistingEmployee_Territory()
{
    TerritoryRepository repoTerritory = new TerritoryRepository();
    EmployeeRepository repoEmployee = new EmployeeRepository();

    //se crean los empleados
    Employee employeeNew1 = new Employee() { 
        FirstName = "Name 1", 
        LastName ="LastaName 1" 
    };
    repoEmployee.Create(employeeNew1);

    Employee employeeNew2 = new Employee() { 
        FirstName = "Name 2", 
        LastName ="LastaName 2" 
    };
    repoEmployee.Create(employeeNew2);

    //se crea el territorio
    Territory territoryNew = new Territory()
    {
        TerritoryDescription = "territoty 1"
    };
    repoTerritory.Create(territoryNew);

    //asignamos los empleados al territorio existente
    repoTerritory.AddEmployees(territoryNew, new List<Employee>(new Employee[]{ employeeNew1, employeeNew2 }));

    //validamos que la asignacion se haya realizado correctamente 
    //recuperando la entidad y sus relaciones
    var territorySel = repoTerritory.Single(x => x.TerritoryID == territoryNew.TerritoryID,
                                                new List<Expression<Func<Territory, object>>>() { x => x.Employees });

    Assert.IsNotNull(territorySel);
    Assert.IsNotNull(territorySel.Employees);
    Assert.AreEqual(territorySel.Employees.Count, 2);


}

La primer parte de la ejecución del test crea las entidades de forma independiente

image

Luego se añade los empleados al territorio

image

y al final para validar que todo este correcto recuperando el territorio y su relación con los empleados

image

 

Test – Remover un empleado asociado


Hasta el momento hemos agregando entidades a la colección, pero es muy común querer quitarlas, para esto haremos uso del método creado especialmente en el repositorio de la entidad Territorio.

Antes de seguir hay que aclarar que la estructura de las tablas que crea Entity Framework desde el modelo asigna en las relaciones la opción de borrado en cascada, es necesario conocer esto si es que se quiere eliminar una entidad completa, ya que el hacerlo provocara que todas las asociaciones que esta tenga también sean eliminadas

 

image

 

image

Veamos el código del test que remueve un empleado en concreto de la colección

[TestMethod]
public void Delete_AssignedEmployee_Territory()
{
    TerritoryRepository repoTerritory = new TerritoryRepository();
    EmployeeRepository repoEmployee = new EmployeeRepository();

    //se crean los empleados
    Employee employeeNew1 = new Employee()
    {
        FirstName = "Name 1",
        LastName = "LastaName 1"
    };
    repoEmployee.Create(employeeNew1);

    Employee employeeNew2 = new Employee()
    {
        FirstName = "Name 2",
        LastName = "LastaName 2"
    };
    repoEmployee.Create(employeeNew2);

    //se crea el territorio
    Territory territoryNew = new Territory()
    {
        TerritoryDescription = "territoty 1"
    };
    repoTerritory.Create(territoryNew);

    //asignamos los empleados al territorio existente
    repoTerritory.AddEmployees(territoryNew, new List<Employee>(new Employee[] { employeeNew1, employeeNew2 }));

    //validamos que la asignacion se haya realizado correctamente 
    //recuperando la entidad y sus relaciones
    var territorySel = repoTerritory.Single(x => x.TerritoryID == territoryNew.TerritoryID,
                                                new List<Expression<Func<Territory, object>>>() { x => x.Employees });

    Assert.IsNotNull(territorySel);
    Assert.IsNotNull(territorySel.Employees);
    Assert.AreEqual(territorySel.Employees.Count, 2);

    //removemos uno de los empleados asignados
    repoTerritory.RemoveEmployees(territoryNew, new List<Employee>(new Employee[] { employeeNew1 }));

    //recuperamos el territorio para validar que se haya eliminado el empleado 
    var territorySel2 = repoTerritory.Single(x => x.TerritoryID == territoryNew.TerritoryID,
                                                new List<Expression<Func<Territory, object>>>() { x => x.Employees });

    Assert.IsNotNull(territorySel2);
    Assert.IsNotNull(territorySel2.Employees);
    Assert.AreEqual(territorySel2.Employees.Count, 1);

    Employee employeeSel = territorySel2.Employees.First();
    Assert.AreEqual(employeeSel.FirstName, employeeNew2.FirstName);
    Assert.AreEqual(employeeSel.LastName, employeeNew2.LastName);
}

La primer parte es idéntica al Test anterior, se crean las entidades individualmente, se asignan los empleados al territorio y se validad que todo se haya realizado correctamente.

Luego se procede a quitar uno de los empleados, ejecutándose la instrucción SQL que remueve el registro

image

 

Documentación de referencia


Associations in EF Code First: Part 6 – Many-valued Associations

Configuring a Many-to-Many Relationship

 

Código


Se utilizo Visual Studio 2012, no se adjunta ninguna base de datos, esta será creada al ejecutar los test utilizando el modelo definido en el contexto de EF.

[C#]
 

39 comentarios:

  1. Gracias Leo!!!
    Te pregunto : que marques el territorio y los empleados como unchanged a la hora de agregar, hace que no se vuelvan a agregar porque ya existen, no?

    Si es asi yo no podria mandar como paramatros un territorio y empleados que solo esten en memoria y no hallan sido creados anteriormente?

    Otra vez gracias, son muy utiles tus articulos y muy entendibles!!!

    ResponderEliminar
  2. hola Patos

    exacto el poner la entidad como unchanged evita que se vuelva a crear

    cuando dices que estan en memoria apuntas a que son entidades que estan dentro del contexto, o son entidades desconectadas ? porque hay que aclarar que aqui al usar el repositorio las entidades son desoconectadas del contexto por eso es que se vuelven adjuntar cuando se realiza el Entry()

    la idea es que esta accion solo permite relacionar entidades, si quieres actualizarlas solo usas el metodo Update() comun aplicando el cambio a la entidad en particular

    saludos

    ResponderEliminar
  3. claro Leo yo decia desconectadas, pero si ya me sacaste la duda!

    Gracias!!

    ResponderEliminar
  4. Hola, tengo una duda
    ¿Se podría crear una columna más a la tabla de relación EmployeeTerritories??

    Saludos

    ResponderEliminar
  5. hola DaNny

    el tema es que si agregas una columna deja de ser una relacion mucho a mucho de forma que la tabla intermedia es transparente y oculta en el modelo

    la entidad pasa a tomar peso y ser una entidad nueva que relaciona dos entidades

    deberas crear la clase que representa la tabla intermedia para poder ponerle esa propiedad

    saludos

    ResponderEliminar
  6. Hola Leandro, muy buen post!
    Estoy tratando de implementar el repositorio (“IRepository” y “BaseRepository”) en una aplicación Visual Basic, me podrás ayudar a convertir esta función que no la puedo resolver?
    En IRepository:
    T Single(Expression> predicate, List>> includes);
    En BaseRepository:
    public T Single(Expression> predicate, List>> includes)

    Y si no fuera tanta molestia, como la llamaría desde el cliente?
    var categorySelected = repoCategory.Single(x => x.CategoryID == categoryNew.CategoryID, new List>>() { x=>x.Products });

    Intente utilizar el conversor (http://www.developerfusion.com/tools/convert/csharp-to-vb/) pero me da error.
    Las funciones de crear, actualizar y eliminar me anduvieron bien. Tambien la que devuelve la lista completa y el single simple.

    Muchas gracias!!!

    ResponderEliminar
  7. hola Jorge

    la conversion podria ser

    Public Function Single(Expression(Of Func(Of T, Boolean)) predicate) As T

    y luego para usarlo

    Dim categorySelected = repoCategory.Single(Function(x) x.CategoryID = categoryNew.CategoryID, New List(Of Expression(Of Func(Of Territory,object))) { Function(x) x.Employees} )

    estas converisones las realice directamente y no las probe en codigo, quizas le falte algun With en algun punto

    por como veo que queda la escritura de este codigo me parece que va a ser mas facil que aprendas c#

    saludos

    ResponderEliminar
  8. Gracias Leandro,
    Lo voy a probar luego te comento.

    En realidad aprender c# es una asignatura pendiente, algun dia la voy a cumplir...

    Muy buen post te lo digo de nuevo!

    Saludos,

    ResponderEliminar
  9. Bueno, no tengo problema por agregar una entidad nueva. La cuestión es que para hacerlo pongo una referencia al objeto Employee y otra para Territories y las dos marcadas con la anotación [Key].
    Visual Stdio me da el error de 'EmployeeTerritories' has no key defined. Define the key for this EntityType.
    Es como si no entendiera que es una clave compuesta.

    Un saludo y gracias

    ResponderEliminar
  10. hola DaNny

    pero esta tabla EmployeeTerritories es la tipica tabla intermedia de uan relacion muchos a muchos ?

    porque si es asi esta no se modela como clase, sino que la defines solo en el nombre al usar el HasMany()

    o es que es atabla intermedia contiene algun otro campo?

    para definir campo compuestos usarias

    HasKey(x => new {x.Employee, x.TerritoryID});


    saludos

    ResponderEliminar
  11. Leandro como estas mira tus post son muy buenos el entity se ve que ahorra mucho trabajo, estoy empezando a conocer Entity Framework pero estoy muy interesado en el tema no he podido seguir bien un hilo de aprendizaje y en este post me pierdo por que no entiendo como se ejecuta el test si me pudieras dar una mano con una serie de pasos o documentos para aprender y como ejecuto este test

    ResponderEliminar
  12. hola ksoto

    imagino estas utilizando alguna version del VS mayor a la Express, si es asi deberias poder ver el proyecto de test y acceder a la ventana

    imagen

    en la imagen esta la opcion "Test View" con es aopcion listara todos los test, recuerda compilar para que se actualice la lista

    despues simplemente es seleccinar uno o todos y usar la opcion de esa ventana para ejecutarlos, aqui lo explica

    Cómo: Ejecutar pruebas desde Microsoft Visual Studio

    saludos

    ResponderEliminar
  13. Hola Leandro, muy buen post. Tengo una consulta, quizás me puedas guiar con algo. El tema es el siguiente: una vez realizado el mapeo de la tabla, posteriormente en la base de datos se cree una tabla con un nombre diferente pero la misma estructura de sus campos y poder utilizar sin realizar cambios en el código fuente, mas bien solo a nivel de sus archivos de esquemas? existe alguna forma de realizar esto, es como especificar el nombre de la tabla de forma dinámica. Gracias por tus comentarios.

    ResponderEliminar
  14. hola Ismael

    desde el mapping puedes indicar el nombre de la tabla usando el ToTable()

    por ejemplo

    public class TerritoryMap : EntityTypeConfiguration
    {
    public TerritoryMap()
    {
    ToTable("Terrenos");
    HasKey(x => x.TerritoryID);

    }
    }

    como veras defino un nombre que no tiene que ver con el de la clase de la entidad

    saludos

    ResponderEliminar
  15. hola Leandro, antes que nada un placer y disculpa las molestias, lo que sucede es que deseaba saber si podria ayudarme, soy estudiante de ing. en software y me dejaron como proyecto desarrollar el modelo de runge kutta de cuarto orden pero no se como realizarlo, veo que es posible con matlab y me preguntaba si es posible usar las librerias de matlab para usarlos en c#, es decir para correr las rutinas de matlab en c# 2010 o alguna otra forma usando c#2010, gracias de antemano y un gusto.
    ATTE: Marco Mendoza
    P.D. GRACIAS por la atencion en foros/ blogs, me ha servido de mucho sus respuestas a otros usuarios.

    ResponderEliminar
  16. hola Marco

    la verdad no sabria como poder ayudarte ya que no tengo ni idea que es runge kutta
    y tampoco he usado matlab integrado con c#
    se que hay librerias que puedes referenciar y usar en tu codigo, pero no conozco mucho mas

    no preguntaste en los foros de metlab? o quizas ver algun blog que publique como integrarlo con .net, mas que nada que librerias referenciar

    saludos

    ResponderEliminar
  17. ha, de acuerdo, muchas gracias, verificare en los foros, si encuentro algo y lo resuelvo pasare el tip por aqui.

    Gracias Leandro.

    ResponderEliminar
  18. Que pasa si yo solo tengo los codigos(pk) de las entidades que quiero relacionar. A mi entidad 1 le agrego un listado de las unidades relacionadas (entidad 2). Tengo que inicializar todas mi entidades 2 desde la base, algo como:
    foreach (var e in listaEntidades2)
    {
    entidad1.entidades2.Add(db.entidades2.SingleOrDefault(x => x.codigo == e));
    }
    O hay alguna forma de no hacer ese foreach ?

    ResponderEliminar
  19. hola Nymph

    no se si entendi la pregunta
    si la entidad2 ya la tienes en la lista podrias ver si se adjunta en el contexto
    pero algo que no comentas es si ese listaEntidades2 esta definido con alguna clase que pertenece a EF
    porque podrias hacer

    foreach (var e in listaEntidades2)
    {
    context.Entry(e).State = EntityState.Unchanged;
    entidad1.entidades2.Add(e);
    }


    Entity states and SaveChanges

    saludos

    ResponderEliminar
  20. Gracias por tu respuesta.

    Quizas no fui tan explicito.
    Supongamos que mi accion recibe..
    int[] entidades2 (en el view era checkbox solo con el codigo)

    Y luego con esos codigo creo objetos entidad2, pero estos solo tienen el codigo, que en resumidas cuentas seria lo unico que yo ocupo para crear una relacion.

    Y aqui viene la duda, para mi con los codigos de la entidad 2 me basta, pero no se si EF necesita que yo inicialize cada entidad2 (haciendo el foreach de selects) para agregarlos a la entidad1 o con saber el codigo me basta?

    Saludos y gracias.

    ResponderEliminar
  21. hola Nymph

    si a EF tambien le alcanza con los id, solo que deberias crear la entidad nuevo asignar el id y realizar el attach

    Add/Attach and Entity States

    podrias definir

    EntidadClass entity = new EntidadClass() { Id = valor};

    y esa entidad adjuntarla pero marcarla como unchange para que no la vuelva a crear

    saludos

    ResponderEliminar
  22. Buenisimo Leandro, consulta se puede navegar sobre una propiedad d euna clase q es otra clase mapeada? . pq en mi caso me sale el error
    La instancia de ObjectContext se ha eliminado..??? hace falta en el controler al inicio hacer new de todos los repositorie? gracias totales

    ResponderEliminar
  23. Leandro buenas tardes , hice todos lso repositorios como publicasates. tenes un ejemplod e un get de una clase con Icoleccition de otra case adentro. no me anda aun. nunca pense que me costaria tanto. con NHibernate tube mas suerte. estoy ahogado ya a punto de largar todo. gracias por al ayuda.

    ResponderEliminar
  24. hola Ariel

    para recuperar una entidad y su colecion asociada solo tienes que usar el Include() indicando la propiedad que quieres cargar de esta forma la query que crear EF ya incluye las relaciones a esa asociacion de navegacion

    saludos

    ResponderEliminar
  25. hola Leandro , a donde pongo el Include()?

    en este caso en el Perfil si me lista los usuarios pero en el Usuario No los perfiles, hay que hacer doble map?

    aca va un ejemplo de como pueblo pq no anda , igual no ejecuta esa sentencia no el gusta el == no se pq.

    using (GigaContext context = new GigaContext())
    {
    List _l = (List)context.Set().ToList();
    foreach (Perfil item in _l)
    {
    List _u = (List)context.Set().Where( x => x.IdPerfil == item.IdPerfil).ToList();
    item.Usuarios = _u;

    }
    return _l;
    }

    en el map hay que poner el inlcude??

    creo q sigo con NHibernete pq c EF no pego una.
    Gracias

    ResponderEliminar
  26. te agradezco toda la buena voluntad y increhible dedicacion al los temas. no te jodo mas
    abandono el estudio de asp mvc sigo con java y hibernate .
    gaste tiempo y no anduvo el ORM
    gracias totales

    ResponderEliminar
  27. hola Ariel

    crees que con java y hibernate va a ser mas simple? yo lo dudo, si tienes problemas con .net con java no va a ser mas simple porque el principio no varia tanto

    si te gusta hibernate porque no usas NHibernate que es la implementacion para .net

    saludos

    ResponderEliminar
  28. Hola Leandro no habia leido la pregunta.
    no digo que sea mas facil. con hibernate trabajo ahce mucho y no em resulto dificl ni al principio. y em funciona. no se si es mejor o peor. me encantaria que me funcione EF. en serio. en este caso me encntaria ver un get all con la clases contenidas. pense q esto hiba a ser mas facil. con toda la mejor.

    ResponderEliminar
  29. Leandro ahí pude hacer andar el include
    aunque todavía no entiendo si mapeamos y ponemos lazy false pq hay q hacer inlcude? ya debería traerlo solo?

    ahora la clase q incluyo tambien quiero q incluya una coleccion q tiene dentro
    como se hace??

    Ejemplo

    public override List Menu GetAll()
    {
    List Menu _lista;
    using (GigaContext context = new GigaContext())
    {
    _lista = (List Menu)context.Menus.Include("MenuItems").ToList();

    }
    return _lista;
    }

    quiero que los menuItems tengan adentro , como hago por cada MenuItems.Include("Algo")??

    Gracias Totales

    ResponderEliminar
  30. hola Ariel

    recomendaria veas el video

    [Entity Framework] Code First - Cargando Entidades Relacionadas

    veras como trabja EF cuando esta lazy activo y como arma la query cuando le indicas que propiedad relacionada vas a utilizar

    si ya conoces que hay una propiedad relacionada que vas a trabajar su informacion al definirla en el include() la recupera en una sola operacion, esto es mucho mas optimo que realizar queries individuales por cada dato que consultes

    saludos

    ResponderEliminar
  31. de donde sacas el : BaseRepository
    en mi proyecto me marca que no existe el type, estoy poniendo la clase en la carpeta de Model, me gustaria saber si debo poner la clase en otro lado

    ResponderEliminar
  32. Buen dia Leandro una pregunta, si se tiene dos tablas una dependiente de la otra como podria hacer una consulta de la dependiente sin que me retorne las propiedades de navegacion.

    es que Tengo esta entidad en EF que es la que quiero hacerle una consulta

    public partial class Charity
    {
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Charity()
    {
    this.Registration = new HashSet();
    }

    public int CharityId { get; set; }
    public string CharityName { get; set; }
    public string CharityDescription { get; set; }
    public string CharityLogo { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection Registration { get; set; }
    }

    pero al devolverla como una lista tipo Charity con este metodo:

    MarathonSkills2015Entities context;

    public List ListCharity()
    {
    using (context = new MarathonSkills2015Entities())
    {
    var charities = (from c in context.Charity
    select c).ToList();
    return charities;
    }
    }


    me devuelve la columna Registration sin nada pero al colocarlo en un DataGridView me bota un error.

    ResponderEliminar
  33. Hola Leandro una pregunta estoy haciendo un proyecto de la facultad y estamos usando mvc 4 con la version 5.0.0.0 del entitty , y no tiene la opcion code first , mas bien estamos usando la opcion EF Disagner desde Base de Datos Y no se como configurar al modelo de la BD que tengo que mapear en mi vista para crear un grupo objetivo con varios clientes

    ResponderEliminar
    Respuestas
    1. hola
      Como que no tiene la opcion de code first? la opcion si esta en EF aunque veo que usan el modelo visual con un edmx
      La verdad no entendi que es eso del "grupo objetivo" pero si se trata de una entidad podrias definir la tabla o la clase segun sea que esten en Databse First o Model First. Si generan el modelo en el edmx desde la db podrias generar la tabla y luego actualizar el modelo visual

      Eliminar
  34. Hola Leandro, Que pasa si quiero actualizar la lista de Empleados tengo que modificar el método AddEmployees si tu respuesta es si ¿Como?

    ResponderEliminar
    Respuestas
    1. hola
      Cuando realizas una asociacion entre entidades realizas operaciones de agregar o remover esta asociacion, pero no existe ninguna operacion de actualizar. Es por eso que tienes el AddEmployees() y RemoveEmployees()
      saludos

      Eliminar
    2. OK,
      Leandro, he implementado el método Remove pero no ingresa al ForEach para hacer la eliminación

      Eliminar
    3. private void dgvModelo_CellContentClick(object sender, DataGridViewCellEventArgs e)
      {
      //Detecta si se ha seleccionado el header de la grilla
      if (e.RowIndex == -1)
      return;
      if(dgvModelo.Columns[e.ColumnIndex].Name == "colSN")
      {
      //Se toma la fila seleccionada
      DataGridViewRow row = dgvModelo.Rows[e.RowIndex];
      //Se selecciona la celda del checkbox
      DataGridViewCheckBoxCell cellSeleccion = row.Cells["colSN"] as DataGridViewCheckBoxCell;

      if(Convert.ToBoolean(cellSeleccion.Value))
      {
      string mensaje = string.Format("Mensaje. \n\nSe ha seleccionado," +
      " \nDescripcion: '{0}'", row.Cells["colDescripcion"].Value);
      MetroMessageBox.Show(this, mensaje, strModelo.nameEntity, MessageBoxButtons.OK,
      MessageBoxIcon.Information);
      }
      else
      {
      Proveedor _proveedor = new Proveedor();
      _proveedor.ProveedorId = string.IsNullOrEmpty(txtProveedorId.Text)
      ? 0
      : Convert.ToInt32(txtProveedorId.Text);
      _proveedor.RazonSocial = txtRazonSocial.Text.Trim();
      _proveedor.NroDocumento = txtNroDocumento.Text;
      _proveedor.DocumentoIdentidad = ((EnumDocumentoEdentidad)(cboTipoDocumento.SelectedValue));
      _proveedor.CategoriaId = Convert.ToInt32(cboCategoria.SelectedValue);
      _proveedor.Direccion = txtDireccion.Text;
      _proveedor.Fijo = txtFijo.Text;
      _proveedor.Celular = txtCelular.Text;
      _proveedor.Representante = txtRepresentante.Text;
      _proveedor.Email = txtEmail.Text;
      List listRowCheck = GetChecked(dgvModelo, "colSN");
      _repositoryProveedor.RemoveModelos(_proveedor, listRowCheck);
      txtProveedorId.Text = Convert.ToString(_proveedor.ProveedorId);

      string mensaje = string.Format("Mensaje. \n\nSe ha quitado la seleccion," +
      " \nDescripcion: '{0}'", row.Cells["colDescripcion"].Value);
      MetroMessageBox.Show(this, mensaje, strModelo.nameEntity, MessageBoxButtons.OK,
      MessageBoxIcon.Information);
      }
      }
      }

      Eliminar