Pregunta Entity Framework, Code First modeling y una referencia cíclica


He pasado varios días tratando de resolver este problema. Mientras hacía un proyecto simple para ejemplificar mi problema, tropecé con una posible solución. Entonces, esta es una especie de pregunta doble.

Pero primero, un poco de información de fondo:

Acabo de empezar a usar Entity Framework 4.1 (EF) y Code First para crear los modelos para mi proyecto ASP.NET MVC. Necesito algunos modelos similares a esto:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Family
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Father> Fathers { get; set; }
        public virtual ICollection<Mother> Mothers { get; set; }
    }

    public class Mother
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Father
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Child
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int MotherID { get; set; }
        public int FatherID { get; set; }

        public virtual Mother Mother { get; set; }
        public virtual Father Father { get; set; }
    }
}

Y el DbContext:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;

namespace TestApp.Models
{
    public class TestContext : DbContext
    {
        public DbSet<Family> Families { get; set; }
        public DbSet<Mother> Mothers { get; set; }
        public DbSet<Father> Fathers { get; set; }
        public DbSet<Child> Children { get; set; }
    }
}

(Por favor, perdonen el ejemplo cojo, eso es lo que el cerebro frito de mi viernes fue capaz de inventar).

Una familia puede tener varias madres y varios padres. Y un niño tiene una madre y un padre. Comprobé con uno de los gurús de .NET en mi trabajo, quien estuvo de acuerdo en que no hay nada extraordinario en esto. Al menos hasta donde podemos ver.

Pero cuando ejecuto el código, obtengo esta excepción:

System.Data.SqlServerCe.SqlCeException: la relación referencial dará como resultado una referencia cíclica que no está permitida. [Restricción nombre = Madre_Familia]

Veo el ciclo: Family - Mother - Child - Father - Family. Pero si crease las tablas de la base de datos yo mismo (que prefiero no, eso es lo que me gusta de Code First), sería una estructura de datos perfectamente válida, hasta donde yo sé.

Entonces, mi primera pregunta es: ¿Por qué es esto un problema cuando se usa código primero? ¿Hay alguna manera de decirle a EF cómo manejar adecuadamente el ciclo?

Luego, como escribo inicialmente, mientras creaba un proyecto simple para ejemplificar mi problema, accidentalmente tropecé con una posible solución. Simplemente olvidé algunas de las propiedades al definir mis modelos. Para mayor claridad en el siguiente ejemplo, en lugar de eliminarlos, he comentado las partes de los modelos que olvidé:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Family
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Father> Fathers { get; set; }
        public virtual ICollection<Mother> Mothers { get; set; }
    }

    public class Mother
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Father
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Child
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // public int MotherID { get; set; }
        // public int FatherID { get; set; }

        public virtual Mother Mother { get; set; }
        public virtual Father Father { get; set; }
    }
}

Por lo tanto, eliminar estos SomethingID las propiedades de referencia parecen resolver mi problema. Como puede ver en el controlador del proyecto de muestra al que estoy enlazando al final de esta publicación, aún puedo hacer ciclos y hacer cosas como mothers.First().Family.Fathers.First().Children.First().Mother.Family.Name sin ningún problema. Pero todos los tutoriales y ejemplos sobre el modelado de EF y Code First que he estado viendo (p. este por Scott Guthrie) incluir estas propiedades, por lo que se siente mal no usarlas.

Y entonces, mi segunda pregunta es: ¿Habrá algún inconveniente y problema que aún no haya descubierto haciendo esto?

Descargue el proyecto de ejemplo aquí: http://blackfin.cannedtuna.org/cyclical-reference-test-app.zipy abra TestSolution.sln. Las propiedades se comentan en el proyecto de ejemplo. Descomente las líneas en TestModels.cs para agregar las propiedades, lo que da como resultado la excepción de referencia cíclica.

NÓTESE BIEN: La solución es crear y crear una base de datos SQL CE ubicada en c: \ TestApp.sdf

Actualización, diciembre de 2011: Nunca resolví este problema técnicamente, pero dejé mi trabajo y encontré otro trabajo donde no tengo que usar las tecnologías de Microsoft. Ese tipo de solucionado mi problema :)

Como el soporte técnico en el antiguo lugar solía escribir al solucionar problemas: "Se ha proporcionado una solución alternativa".


8
2017-08-08 12:31


origen


Respuestas:


Pero si creé las tablas de la base de datos yo mismo (que prefiero no,   eso es lo que me gusta de Code First) sería perfectamente válido   estructura de datos, por lo que puedo decir.

Esto es algo que deberías verificar dos veces. La excepción proviene directamente de la base de datos y no de Entity Framework. Es probable que también una estructura de tabla con las mismas restricciones creadas a mano no sea válida. Tenga en cuenta que sus propiedades de clave externa Mother.FamilyID, Father.FamilyID, Child.MotherID y Child.FatherID son no anulable, por lo que representan las relaciones requeridas y las columnas correspondientes en la base de datos tampoco son anulables.

Cuando elimina todas estas propiedades de sus clases modelo, sus relaciones se vuelven repentinamente Opcional porque las propiedades de navegación pueden ser null. ¡Este es otro modelo ahora ya que las columnas FK en el DB pueden ser anulables! Aparentemente este es un modelo permitido.

Si desea tener propiedades de clave extranjera en su modelo que representen relaciones opcionales en lugar de obligatorias, puede usar tipos que aceptan valores numéricos: public int? FamilyID { get; set; }, public int? MotherID { get; set; }, etc.


3
2017-08-08 15:01



Este es un problema conocido y usted no es el primero en toparse con él. Por lo que he escuchado, están trabajando en una solución mejor en la próxima versión de WCF, sin embargo, por el momento, por mi experiencia, es mucho mejor crear DataContracts que representen los datos que se enviarán por cable, cambiando así la estructura de datos. para eliminar la referencia cíclica.

Sé que es un dolor, pero existen otros beneficios en los que probablemente querrás hacer otros cambios a las estructuras que tus clientes consumen de todos modos en lugar de dejarlos jugar con los objetos tal como existen en tu db.


0
2017-08-08 14:23



Tenía el mismo problema pero lo resolví usando el consejo en esta respuesta Primero, el código del Entity Framework: dos claves externas de la misma tabla que funciona mejor que cambiar el tipo de columnas clave a opcional.


0
2018-04-29 13:52