Creación / actualización de entidades de Breeze con una relación de uno a uno.

Estoy trabajando en un proyecto usando Breeze y encontré un problema con algunas entidades que había creado usando una relación de uno a uno. Esta es una historia un poco larga, pero tiene un final feliz, así que tengan paciencia :)

Aquí hay una versión reducida de mi código C #:

public class Person {
    [Key]
    public int Id { get; set; }
    public virtual User User { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class User {
    [Key]
    public int Id { get; set; }
    [Required]
    public virtual Person Person { get; set; }
    [Required]
    public string EmailAddress { get; set; }
    [Required]
    public string Password { get; set; }
}
public class MyDbContext : DbContext {
    public DbSet<Person> Person { get; set; }
    public DbSet<User> User { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Entity<User>().HasRequired(x => x.Person);
    }
}

Esto crea una tabla de base de datos de Personas con una clave primaria generada automáticamente, y una tabla de Usuarios con una clave primaria ingresada manualmente, que es un subconjunto de la tabla de Personas.

Yo creo estas entidades, e intento guardarlas en mi código javascript con algo como:

var manager = new breeze.EntityManager('api/Db');
// other breeze initialization stuff, metadata etc.
var person=manager.createEntity('Person', undefined, breeze.EntityState.Detached);
var user=manager.createEntity('User', undefined, breeze.EntityState.Detached);
// set other fields, name, email, password
user.Person(user);
manager.addEntity(user);
manager.saveChanges().then(function() { // etc

Cuando se llama a la función SaveChanges en mi BreezeController obtengo esta excepción:

Validation error: Property: 'Person', Error: 'The Person field is required.'

Al examinar el JSON que se envía al BreezeController, encuentro que el ID tanto de la Persona como del Usuario era '-1', y que el JSON no anidaba la entidad Persona dentro de la propiedad de la Persona del Usuario. Por lo tanto, no había forma de que EFContextProvider supiera que se suponía que había una relación entre estos objetos.

Mi primer intento de resolver esto fue enviar la entidad 'Persona' al backend con saveChanges, y luego enviar el 'Usuario' en una transacción separada. P.ej:

manager.addEntity(person);
manager.saveChanges().then(function() {
              user.Person(user); // this line moved from previous example,
                                 // breeze throws error here
              manager.addEntity(user);
              manager.saveChanges().then(function() { // etc

Usando este enfoque obtuve este error de breeze.js:

Cannot attach an object to an EntityManager without first setting its key
or setting its entityType 'AutoGeneratedKeyType' property to something other
than 'None' at checkEntityKey (http://localhost:12151/scripts/breeze.debug.js)

Así que traté de configurar la clave en el Usuario para que se complete el valor cuando se guardó la Persona. Utilizando "user.Id (person.Id);" Ahora en el BreezeController sigo recibiendo el mismo error:

Validation error: Property: 'Person', Error: 'The Person field is required.'

Luego traté de modificar el método SaveChanges de mi BreezeController para buscar un objeto 'Usuario' entrante que no tiene un conjunto de propiedades 'Persona', luego buscar a la persona en la base de datos y asignarla a la Persona antes de llamar a _efContextProvider. SaveChanges (saveBundle); Perdí el código exactamente por cómo lo hice, pero es irrelevante ya que tampoco funcionó correctamente ... Creó una SEGUNDA 'Persona' en la base de datos, exactamente la misma que se guardó en el primer administrador. saveChanges, pero tenía una nueva clave primaria generada. Sin embargo, esto asoció exitosamente a la segunda Persona con el Usuario.

Después de pensarlo de la noche a la mañana, se me ocurrió una solución de trabajo mediante la subclasificación del EFContextProvider con esto:

public class MyEfContextProvider : EFContextProvider<MyDbContext>
{
    protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(
        Dictionary<Type, List<EntityInfo>> saveMap)
    {
        AssociateOneToOne(saveMap, typeof(Person), typeof(User));
        return saveMap;
    }
    private static void AssociateOneToOne(IReadOnlyDictionary<Type,
                List<EntityInfo>> saveMap, Type parent, Type child)
    {
        if (!(saveMap.ContainsKey(parent) && saveMap.ContainsKey(child))) return;
        Func<EntityInfo, object> idFunc = delegate(EntityInfo info)
        {
            var o = info.Entity;
            return o.GetType().GetProperty("Id").GetValue(o);
        };
        var childProp = child.GetProperty(parent.Name);
        var childMap = saveMap[child].ToDictionary(idFunc, info => info.Entity);
        var parentMap = saveMap[parent].ToDictionary(idFunc, info => info.Entity);
        foreach (var childEntry in childMap)
        {
            childProp.SetValue(childEntry.Value, parentMap[childEntry.Key]);
        }
    }
}

Ahora me doy cuenta de que esta ha sido una pregunta larga, y gracias por leer. Pero la única pregunta que tengo es, ¿por qué tengo que hacerlo de esta manera? ¿Es esto algo que Breeze no ha implementado todavía? Si es así, ¿es así como lo he hecho?

Respuestas a la pregunta(2)

Su respuesta a la pregunta