[ADO.Net EF] Insercion de registros relacionados
Una de las operaciones que se puede volver un dolor de cabeza (cuando empezamos a usar EF) es la inserción o actualización de tablas que están relacionadas. Miremos a Northwind:

Como se pueden ver si queremos insertar un registro en la tabla Products, necesitamos los CategoryID y SupplierID que están relacionados con otras dos tablas.
El sentido común te dice que el insertar debería ser algo así:
1: public Int32 InsertarProduct(Products objProd)
2: {
3: Int32 prodId = 0;
4:
5: using (NorthwindEntities edmNorth = new NorthwindEntities())
6: {
7: edmNorth.AddToProducts(objProd);
8: edmNorth.SaveChanges();
9: prodId = objProd.ProductID;
10: }
11: return prodId;
12: }
Y la llamada al método sería la siguiente:
1: [TestMethod()]
2: public void InsertarProductTest()
3: {
4: RepositoryOrders target = new RepositoryOrders();
5: Products objProd = new Products();
6: objProd.ProductName = "My Product 2";
7: objProd.Suppliers = new Suppliers() { SupplierID = 18 };
8: objProd.Categories = new Categories() { CategoryID = 1 };
9: objProd.QuantityPerUnit = "24 - 50 g pkgs.";
10: objProd.UnitPrice = 34.45m;
11: objProd.UnitsInStock = 34;
12: objProd.UnitsOnOrder = 3;
13: objProd.ReorderLevel = 3;
14: objProd.Discontinued = false;
15:
16: int expected = 0; // TODO: Initialize to an appropriate value
17: int actual;
18: actual = target.InsertarProduct(objProd);
19: Assert.AreNotEqual(expected, actual);
20: }
Pero al ejecutar el código te dará un error como el siguiente: “An error occurred while updating the entries”. Debido a que el EF va intentar actualizar el registro SupplierID y CategoryID, pero el nombre no permite nulos, como estos objetos han sido creados manualmente, EF piensa que quieres actualizar ellos también, cuando sólo quieres insertar un registro de Product.
Una de las cosas que estaba haciendo era remover las relaciones manualmente (editando el modelo Xml) y el modelo no sabía que estaban relacionadas así que sólo pasaba los datos. Pero actualizar el modelo era una tarea muy trabajosa, y llegas a esos momentos en que dices: “creo que ya es hora, de buscar una forma más simple de hacer esto”.
En este foro: How to insert a row using Entity Framework?, te dan una luz de la solución, y con unos adicionales puede ser así:
1. Vamos a crear una clase parcial de Product con los campos de relación a las otras tablas. Clase parcial, para que cuando se actualice el modelo esta no se actualice.
1: public partial class Products
2: {
3: public Int32 SupplierID { get; set; }
4: public Int32 CategoryID { get; set; }
5: }
2. Vamos a cambiar el código de inserción. En el foro, se habla de dos opciones, pero la primera involucra hacer una consulta a la base datos, lo cual no es necesario en una inserción simple, por eso optamos por el segundo.
1: public Int32 InsertarProduct(Products objProd)
2: {
3: Int32 prodId = 0;
4:
5: using (NorthwindEntities edmNorth = new NorthwindEntities())
6: {
7: objProd.SuppliersReference.EntityKey =
8: new EntityKey("NorthwindEntities.Suppliers",
9: "SupplierID", objProd.SupplierID);
10: objProd.CategoriesReference.EntityKey =
11: new EntityKey("NorthwindEntities.Categories",
12: "CategoryID", objProd.CategoryID);
13:
14: edmNorth.AddToProducts(objProd);
15: edmNorth.SaveChanges();
16: prodId = objProd.ProductID;
17: }
18:
19: return prodId;
20: }
3. Y finalmente la llamada al método será así:
1: [TestMethod()]
2: public void InsertarProductTest()
3: {
4: RepositoryOrders target = new RepositoryOrders();
5: Products objProd = new Products();
6: objProd.ProductName = "My Product 2";
7: //usando clase parcial
8: objProd.SupplierID = 18;
9: objProd.CategoryID = 1;
10: objProd.QuantityPerUnit = "24 - 50 g pkgs.";
11: objProd.UnitPrice = 34.45m;
12: objProd.UnitsInStock = 34;
13: objProd.UnitsOnOrder = 3;
14: objProd.ReorderLevel = 3;
15: objProd.Discontinued = false;
16:
17: int expected = 0; // TODO: Initialize to an appropriate value
18: int actual;
19: actual = target.InsertarProduct(objProd);
20: Assert.AreNotEqual(expected, actual);
21: }
Se intento pasar los valores en los mismos objetos del modelo, pero hubo algunos errores porque se cruzaba la entidad enviada y la referencia que creamos antes de insertar el producto. Y esto de usar clases parciales, es forma transparente y simple de hacerlo, a propósito si van exponer su modelo en un servicio WCF, tiene que serializar estos atributos de la clase parcial para que también puedan ser expuestos en los contratos.
Se pudo mostrar otros detalles y los errores de otras pruebas que hice, pero si hacía eso nunca terminaba el artículo, así que mejor enviarlo masticado :D.
P.D.: Si tienen otras formas de hacer un Insert o Update con tablas relacionadas, lo pueden enviar en los comentarios.
Saludos,