clave compuesta como clave externa

91

Estoy usando Entity framework 4.1 en la aplicación MVC 3. Tengo una entidad donde tengo la clave principal que consta de dos columnas (clave compuesta). Y esto se está utilizando en otra entidad como clave externa. ¿Cómo crear la relación? En scnerios normales usamos:

public class Category
{
    public string CategoryId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public string CategoryId { get; set; }

    public virtual Category Category { get; set; }
} 

pero ¿qué pasa si la categoría tiene dos columnas clave?

DotnetGorrión
fuente

Respuestas:

169

Puede utilizar cualquiera de las API fluidas:

public class Category
{
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }

    public virtual Category Category { get; set; }
}

public class Context : DbContext
{
    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Category>()
            .HasKey(c => new {c.CategoryId1, c.CategoryId2});

        modelBuilder.Entity<Product>()
            .HasRequired(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => new {p.CategoryId1, p.CategoryId2});

    }
}

O anotaciones de datos:

public class Category
{
    [Key, Column(Order = 0)]
    public int CategoryId2 { get; set; }
    [Key, Column(Order = 1)]
    public int CategoryId3 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public string Name { get; set; }
    [ForeignKey("Category"), Column(Order = 0)]
    public int CategoryId2 { get; set; }
    [ForeignKey("Category"), Column(Order = 1)]
    public int CategoryId3 { get; set; }

    public virtual Category Category { get; set; }
}
Ladislav Mrnka
fuente
¿Necesito mantener las propiedades virtuales (Categoría de Categoría virtual pública {get; set;}) así como las modificaciones de datos?
DotnetSparrow
4
virtualen las propiedades de navegación es necesario para la carga diferida. virtualen propiedades escalares ayuda con el seguimiento de cambios de los objetos adjuntos.
Ladislav Mrnka
4
¿Qué haría usted si los nombres de columna de la tabla de claves externas fueran diferentes a los de la tabla principal? Por ejemplo, en producto, ¿cómo etiquetaría el atributo ForeignKey si los nombres de las columnas parecieran: PCategoryId2, PCategoryId3?
Respecto a esta línea: .HasRequired(p => p.Category)pero Productno tiene una propiedad de la Entidad Catagory sino dos ids que hacen la clave compuesta de una categoría. ¿Puede explicarme, porque creo que ni siquiera se compilará ... Gracias!
gdoron apoya a Monica el
@gdoron: Producttiene Categoryen mi respuesta.
Ladislav Mrnka
25

Creo que la forma más fácil es usar la Anotación de datos en la propiedad de navegación de esta manera: [ForeignKey("CategoryId1, CategoryId2")]

public class Category
{
    [Key, Column(Order = 0)]
    public int CategoryId1 { get; set; }
    [Key, Column(Order = 1)]
    public int CategoryId2 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }

    [ForeignKey("CategoryId1, CategoryId2")]
    public virtual Category Category { get; set; }
}
Christophe
fuente
Esto funcionó muy bien. Yo también prefiero usar esto en Navigationpropiedades. Sin embargo, ¿cómo puedo configurar solo cascadeDelete: falsepara esta propiedad, no para todo el sitio? Gracias
RoLYroLLs
En algunos casos, la clave externa también es parte de la clave compuesta de la tabla actual. De esta manera funcionó. La otra forma (@Ladislov) no lo hizo. Recibí el error: "Atributo de columna duplicada"
D. Kermott
RoLYroLLs: cascadeDelete se establece en el archivo de migración (después de usar el comando del administrador de paquetes de agregar-migración). Un ejemplo: AddForeignKey ("dbo.Product", "GuidedActivityID", "dbo.GuidedActivity", "ID", cascadeDelete: false);
Christophe