¿Puede guardar la entidad principal en una relación uno a varios en el marco de la entidad?

c# entity-framework entity-framework-core one-to-many

Pregunta

Tengo una relación simple, de uno a muchos, entre dos entidades.

public class Contact
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    // the children
    public List<Message> Messages { get; set; }
}

public class Message
{
    public string Id { get; set; }
    public string ContactId { get; set; }
    public string Source { get; set; }
    // the parent
    public Contact Contact { get; set; }
}

Así es como se ve la migración

migrationBuilder.CreateTable(
                name: "Contact",
                columns: table => new
                {
                    Id = table.Column<string>(nullable: false),
                    FirstName = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Contact", x => x.Id);
                    table.UniqueConstraint("UK_Id", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Message",
                columns: table => new
                {
                    Id = table.Column<string>(nullable: false),
                    ContactId = table.Column<string>(nullable: true),                        
                    Source = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Message", x => x.Id);
                    table.UniqueConstraint("UK_Id", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Message_Contact_ContactId",
                        column: x => x.ContactId,
                        principalTable: "Contact",
                        principalColumn: "Id");
                });

Ahora puedo crear un nuevo Contact , agregar un nuevo Message a la propiedad Messages y guardar sin problemas. Si cargo ese contacto, recibo todos los mensajes asociados con él, no hay problema.

Lo que me pregunto es cómo hacer esto a la inversa. Quiero crear un nuevo mensaje (que aún no existe en la base de datos), establecer la propiedad de Contact en un nuevo objeto de contacto y guardar. Terminé con una restricción de clave externa (lo cual tiene sentido. No se puede guardar el mensaje hasta que se haya guardado el contacto). Pero pensé que el marco de la entidad era lo suficientemente inteligente como para entender las relaciones y saber insertar el contacto antes del mensaje. ¿Estoy configurando algo mal?

Actualizar

Aquí está la prueba de la unidad que estoy tratando de pasar

    [TestMethod]
    public void ShouldSaveEntityParentRelationshipsCorrectly()
    {
        var message = new Message
        {
            Id = "2848"
            , IsUrgent = true
            , MessageType = MessageType.Inbox
            , Note = "One ring to rule them all"
            , Contact = new Contact
            {
                Id = "454545"
                , FirstName = "Frodo"
                , LastName = "Baggins"
            }
        };

        service.Save(message); //Foreign key constraint error

        var entity = service.Find<Message>()
            .Include(c => c.Contact)
            .First(p => p.Id == "2848");

        Assert.AreEqual("Frodo", entity.Contact.FirstName);
        Assert.AreSame(entity, message, "Messages are not the same");

        Assert.IsNotNull(entity.Contact);

        Assert.AreSame(message.Contact, entity.Contact, "Contacts are not the same");
    }

Esto es lo que hace el service.Save bajo el capó.

public virtual void Save<T>(T entity) where T : class, IEntity
    {
        var context = Context();
        var entry = context.Entry(entity);
        var state = entry.State;

        if (state == EntityState.Detached)
            Add(entity);
        else if (state == EntityState.Deleted)
            Remove(entity);
        else
            Update(entity);

        SaveChanges();
    }

    public virtual void SaveChanges()
    {
        try
        {
            Context().SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            Logger.Current.Log(ex);
            throw ex;
        }
    }

    public T Add<T>(T entity) where T : class, IEntity
    {
        return Context().Set<T>().Add(entity).Entity;
    }

Respuesta aceptada

En beta8 y versiones posteriores, DbSet.Add() agrega solo la entidad y sus hijos . Debido a que Contact es un padre a Message , primero debe agregarlo explícitamente.

    service.Save(message.Contact);
    service.Save(message);

Consulte https://github.com/aspnet/EntityFramework/pull/2979 para obtener más detalles.


Respuesta popular

Depende de la versión de EF7 que estés usando. Para versiones beta7 y anteriores;

A diferencia de las versiones anteriores de EF, la llamada actual a Add () en un objeto que usa EF7 no marca ninguno de sus objetos relacionados como agregado.

Parece que esto se resolvió en beta8. Más información aquí .



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué