2010-07-02 13 views
8

Ayuda a un EF n00b a diseñar su base de datos. Tengo varias empresas que producen varios productos, por lo que hay una relación muchos a muchos entre empresas y productos. Tengo una tabla intermedia, Company_Product, que los relaciona.Pregunta de muchos a muchos de Entity Framework

Cada combinación de compañía/producto tiene una SKU única. Por ejemplo, los widgets de Acme tienen SKU 123, pero los widgets de Omega tienen SKU 456. Agregué el SKU como campo en la tabla intermedia Company_Product.

EF generó un modelo con una relación 1: * entre la empresa y las tablas Company_Product, y una relación 1: * entre el producto y las tablas Company_Product. Realmente quiero una relación : entre empresa y producto. Pero, lo más importante, no hay forma de acceder al SKU directamente desde el modelo.

¿Debo poner el SKU en su propia mesa y escribir una unión, o hay una forma mejor?

+0

Todo parece estar bien ... Debe tener 3 entidades en su modelo: Empresa, Producto y Empresa_Producto. "no hay forma de acceder al SKU directamente desde el modelo" ¿ha probado ctx.Company_Products.First(). SKU? –

Respuesta

33

Acabo de probar esto en un proyecto nuevo VS2010 (EFv4) para estar seguro, y esto es lo que encontré:

Cuando su tabla asociativa en el medio (Company_Product) sólo tiene el 2 claves externas a las otras mesas (CompanyID y ProductID), luego agregar las 3 tablas al diseñador termina modelando la relación de muchos a muchos. Ni siquiera genera una clase para la tabla Company_Product. Cada compañía tiene una colección de productos, y cada producto tiene una colección de empresas.

Sin embargo, si su tabla asociativa (Company_Product) tiene otros campos (como SKU, su clave principal u otros campos descriptivos como fechas, descripciones, etc.), el modelador EF creará una clase separada, y hace lo que ya has visto

Tener la clase en el medio con 1: * relaciones con la empresa y el producto no es algo malo, y aún puede obtener los datos que desee con algunas consultas fáciles.

// Get all products for Company with ID = 1 
var q = 
    from compProd in context.Company_Product 
    where compProd.CompanyID == 1 
    select compProd.Product; 

Es cierto que no es tan fácil simplemente navegar por las relaciones del modelo, cuando ya tiene su entidad de objetos cargados, por ejemplo, pero eso es lo que una capa de datos es para. Encapsule las consultas que obtienen los datos que desea. Si realmente desea deshacerse de esa clase media Company_Product, y tener muchos-a-muchos representados directamente en el modelo de clase, entonces tendrá que quitar la tabla Company_Product para contener solo las 2 claves externas, y deshacerse de del SKU.

En realidad, no debería decir que TIENE que hacer eso ... es posible que pueda hacer algunas ediciones en el diseñador y configurarlo de esta manera de todos modos. Lo probaré e informaré.

ACTUALIZACIÓN

Mantener la SKU en la tabla Company_Product (es decir, mi modelo EF tenía 3 clases, no 2, sino que creó la clase Company_Payload, con un 1: * a los otros 2 mesas), probé para agregar una asociación directamente entre la Compañía y el Producto.Los pasos que hemos seguido fueron:

  • Haga clic derecho sobre la clase de empresa en el diseñador
  • Añadir> Asociación
  • Set "Fin" de la izquierda para ser la empresa (que debería ser ya)
  • Conjunto "End" en el derecho de Producto
  • cambiar tanto a multiplicidades "* (Muchos)"
  • las propiedades de navegación debe ser nombrado "Productos" y "Empresas"
  • pulsa OK.
  • Haga clic derecho sobre la asociación en el modelo> haga clic en "Tabla de asignación"
  • En "Agregar una tabla o vista" seleccionar "Company_Product"
  • Mapa Compañía -> Identificación (a la izquierda) a CompanyID (a la derecha)
  • Mapa del producto -> Identificación (a la izquierda) a ProductID (a la derecha)

embargo, no funciona. Proporciona este error: Error 3025: Problema al mapear fragmentos que comienzan en la línea 175: debe especificar el mapeo para todas las propiedades clave (Company_Product.SKU) de la tabla Company_Product.

Por lo tanto, esa asociación particular no es válida, porque usa Company_Product como la tabla, pero no asigna el campo SKU a nada.

Además, mientras investigaba esto, me encontré con este tidbit "Best Practice" del libro Entity Framework 4.0 Recipies (tenga en cuenta que para una tabla de asociación con campos adicionales, además de 2 FK, se refieren a los campos adicionales como la "carga útil". En su caso, SKU es la carga útil en Company_Product).

Best Practice

Unfortunately, a project that starts out with several, payload-free, many-to-many relationships often ends up with several, payload-rich, many-to-many relationships. Refactoring a model, especially late in the development cycle, to accommodate payloads in the many-to-many relationships can be tedious. Not only are additional entities introduced, but the queries and navigation patterns through the relationships change as well. Some developers argue that every many-to-many relationship should start off with some payload, typically a synthetic key, so the inevitable addition of more payload has significantly less impact on the project.

So here's the best practice. If you have a payload-free, many-to-many relationship and you think there is some chance that it may change over time to include a payload, start with an extra identity column in the link table. When you import the tables into your model, you will get two one-to-many relationships, which means the code you write and the model you have will be ready for any number of additional payload columns that come along as the project matures. The cost of an additional integer identity column is usually a pretty small price to pay to keep the model more flexible.

(Del Capítulo 2. Fundamentos de modelado entidad de datos, 2.4. Modelado de una relación de muchos a muchos con una carga útil)

Suena como un buen consejo. Especialmente porque ya tienes una carga útil (SKU).

+0

Corriendo en el mismo problema exacto, buena respuesta. –

2

me gustaría añadir lo siguiente a la respuesta de Samuel:

Si desea consultar directamente desde un lado de un muchos-a-muchos relación (con carga) a la otra, puede utilizar el siguiente código (usando el mismo ejemplo):

Company c = context.Companies.First(); 
IQueryable<Product> products = c.Company_Products.Select(cp => cp.Product); 

la variable products sería entonces todos Product registros asociados con el registro Company c. Si desea incluir el código de artículo para cada uno de los productos, se puede usar una clase anónima, así:

var productsWithSKU = c.Company_Products.Select(cp => new { 
    ProductID = cp.Product.ID, 
    Name = cp.Product.Name, 
    Price = cp.Product.Price, 
    SKU = cp.SKU 
}); 
foreach (var 

Usted puede encapsular la primera consulta en una propiedad de sólo lectura para la simplicidad de este modo:

public partial class Company 
{ 
    public property IQueryable<Product> Products 
    { 
    get { return Company_Products.Select(cp => cp.Product); } 
    } 
} 

No puede hacer eso con la consulta que incluye el SKU porque no puede devolver tipos anónimos. Tendría que tener una clase definida, que normalmente se haría agregando una propiedad no mapeada a la clase Product o creando otra clase que hereda de Product que agregaría una propiedad de SKU.Sin embargo, si usa una clase heredada, no podrá realizar cambios en ella y hacer que la administre EF, solo sería útil para fines de visualización.

Saludos. :)

Cuestiones relacionadas