1

我正在使用 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

使用这种方法,我从微风.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 上的键设置为保存 Person 时填写的值。使用“user.Id(person.Id);” 现在在 BreezeController 我仍然得到同样的错误:

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

接下来,我尝试修改 BreezeController 的 SaveChanges 方法以检查传入的“用户”对象,该对象没有设置“人员”属性,然后在数据库中查找人员,并在调用 _efContextProvider 之前将其分配给人员。 SaveChanges(saveBundle); 我丢失了我如何做到这一点的代码,但这无关紧要,因为它也不能正常工作......它在数据库中创建了第二个“人”,与第一个经理中保存的完全相同。 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 还没有实现的东西吗?如果是这样,我这样做的方式好吗?

4

2 回答 2

4

你发现了一个错误……或者在我看来是这样。我已经提交了,我们会在修复后通知您。

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

这条线user.Person(user);似乎根本不对。那应该失败。我想你的意思是user.Person(person);。但你也不需要那个。

尝试这个:

var manager = new 微风.EntityManager('api/Db');
// 其他初始化的东西。

// 添加新的、已初始化的 Person 实体
var person = manager.createEntity('Person', {
        // 可能是初始化字段:firstName, lastName
    });

// 添加新的、已初始化的用户实体,与父 Person 关联
var user = manager.createEntity('用户', {
       personId: person.Id(), // 将密钥设置为父级
       // 也许是初始化字段:电子邮件地址、密码(真的!?!)
});

manager.saveChanges().then(function() { ... 等等。

2013 年 5 月 8 日更新

是的,该提交是 BreezeJS 方面的修复。我们很快就会发布它......当然你可以自由地获取中间提交。我不会……因为中间的、未标记的提交没有经过适当的 QA。

我认为您仍然会遇到问题……这次是在 EntityFramework 方面。1-1 是 Code First 中的绝招;你可能想看看这个 StackOverflow 线程

具体到您的情况,我认为您需要将您的定义更改User为:

公共类用户{
    [钥匙]
    [外键(“人”)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    公共 int ID { 获取;放; }

    //[必需的]
    公共虚拟人人{得到;放; }

    [必需的]
    公共字符串电子邮件地址 { 获取;放; }

    [必需的]
    公共字符串密码{获取;放; }
}

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

// 删除这个!!!
受保护的覆盖无效 OnModelCreating(DbModelBuilder modelBuilder) {
     modelBuilder.Entity().HasRequired(x => x.Person);
}

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

希望这可以帮助。

于 2013-05-06T01:10:15.173 回答
0

Breeze v 1.3.3 现已在 Breeze 网站上提供,我们确实修复了 1-1 映射的问题。您能否确认这是否可以解决您的问题?

于 2013-05-09T01:54:27.797 回答