我正在使用 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上的key设置为保存Person时填写的值。使用“user.Id(person.Id);”现在在 BreezeController 中我仍然得到同样的错误:
Validation error: Property: 'Person', Error: 'The Person field is required.'
接下来,我尝试修改我的 BreezeController 的 SaveChanges 方法以检查没有设置“Person”属性的传入“User”对象,然后在数据库中找到该人,并将其分配给之前的 Person调用 _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 还没有实现的东西吗?如果是这样,我这样做的方式可以吗?
最佳答案
您已经发现了一个错误……或者在我看来是这样。我已将其归档,我们会在修复后通知您。
换句话说,您的 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 日更新
是的,that commit是 BreezeJS 方面的修复。我们很快就会发布它......当然你可以自由地获取中间提交。我不会……因为中间的、未标记的提交没有得到适当的 QA。
我认为您仍然会遇到问题...这次是在 EntityFramework 方面。 1-1 是 Code First 中的绝招;你可能想要 look at this StackOverflow 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 中的 Order/InternationalOrder 关系实际上与您的个人/用户相同。 saveNorthwindTests.js 中的测试“可以保存新的 Northwind Order 和 InternationalOrder [1..(0,1) 关系]”确认模型定义和 Breeze 修复。
希望这对您有所帮助。
关于javascript - 创建/更新具有一对一关系的 Breeze 实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16370922/