javascript-创建/更新具有一对一关系的微风实体

我正在使用Breeze进行项目时,遇到了一些我使用一对一关系创建的实体的问题.这是一个漫长的故事,但是结局很美,所以请耐心:)

这是我的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);
    }
}

这将创建一个具有自动生成的主键的Persons数据库表,以及一个具有手动输入的主键的Users表,该表是Persons表的子集.

我创建了这些实体,并尝试将它们保存在我的javascript代码中,如下所示:

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

在我的BreezeController中调用SaveChanges函数时,出现以下异常:

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

检查发送到BreezeController的JSON后,我发现Person和User的ID均为“ -1”,并且JSON没有将Person实体嵌套在User的Person属性内.因此,EFContextProvider无法知道这些对象之间应该存在关系.

我解决这个问题的第一个尝试是使用saveChanges将’Person’实体发送到后端,然后在单独的事务中发送’User’.例如:

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

使用这种方法,我从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)

因此,我尝试将用户上的键设置为保存人时填写的值.使用“ user.Id(person.Id);”现在在BreezeController中,我仍然遇到相同的错误:

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

接下来,我尝试修改BreezeController的SaveChanges方法,以检查传入的“ User”对象,该对象没有设置“ Person”属性,然后在调用_efContextProvider之前在数据库中查找该人,并将其分配给Person.调用SaveChanges(saveBundle);我完全失去了执行该操作的代码,但是这并没有意义,因为它也无法正常运行…它在数据库中创建了SECOND’Person’,与第一个管理器中保存的人完全相同. saveChanges,但是它生成了一个新的主键.但是,这确实将第二个人与用户相关联.

经过一整夜的思考,我提出了一个可行的解决方案,将EFContextProvider子类化为:

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]);
        }
    }
}

现在我意识到这是一个很长的问题,感谢您的阅读.但是我唯一的问题是,为什么我必须这样做呢?这是Breeze尚未实现的吗?如果是这样,我做的方式还可以吗?

解决方法:

您发现了一个错误…或者在我看来.我已将其归档,修复后会通知您.

另一方面,您的BreezeJS客户端代码比需要的折磨得更多.为什么创建一个未初始化的分离实体…然后添加它?

行user.Person(user);似乎根本不正确.那应该失败.我认为您的意思是user.Person(person);.但是您也不需要.

尝试这个:

var manager = new breeze.EntityManager('api/Db');
// other initialization stuff.

// adds new, initialized Person entity
var person = manager.createEntity('Person', {
        // perhaps init fields: firstName, lastName
    });

// adds new, initialized User entity, associated w/ parent Person
var user = manager.createEntity('User', {
       personId: person.Id(), // SETS THE KEY TO PARENT PERSON
       // perhaps init fields: emailAddress, password (really!?!)
});

manager.saveChanges().then(function() { ... etc.

2013年5月8日更新

是的,BreezeJS方面的修补程序为that commit.我们将尽快发布它……尽管您当然可以随意进行中间提交.我不会…因为未正确标记中间的,未加标签的提交.

我认为您仍然会遇到问题……这次是在EntityFramework方面. 1-1是Code First中的绝招;您可能想要look at this * thread.

为了针对您的情况,我认为您需要将User的定义更改为:

public class User {
    [Key]
    [ForeignKey("Person")]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    //[Required]
    public virtual Person Person { get; set; }

    [Required]
    public string EmailAddress { get; set; }

    [Required]
    public string Password { get; set; }
}

您不需要上面显示的User的“ Fluent API”映射,它可能会妨碍您的操作.

// DELETE THIS !!!
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
     modelBuilder.Entity().HasRequired(x => x.Person);
}

FWIW,DocCode中的“订单/国际订单”关系实际上与您的“个人/用户”相同. saveNorthwindTests.js中的测试“可以保存新的Northwind Order& InternationalOrder [1 ..(0,1)关系]”确认模型定义和Breeze修复.

希望这可以帮助.

上一篇:使用VS+VisualGDB编译Linux版本RCF


下一篇:c# – 我将如何使用带有.net 4.0的实体框架6的breeze