Breeze を使用してプロジェクトに取り組んでいますが、1 対 1 の関係を使用して作成したいくつかのエンティティで問題が発生しました。少し長い話ですが、ハッピーエンドですので、ご了承ください :)
これが私の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 データベース テーブルと、手動で入力された主キー (Persons テーブルのサブセット) を持つ Users テーブルが作成されます。
これらのエンティティを作成し、次のような方法で 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 が User の Person プロパティ内に 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 メソッドを変更して、'Person' プロパティが設定されていない着信 'User' オブジェクトをチェックしてから、データベースでその人を見つけて、_efContextProvider を呼び出す前にそれを Person に割り当てようとしました。 SaveChanges(saveBundle); これを行った正確な方法のコードを失いましたが、適切に機能しなかったため関係ありません...データベースに2番目の「人」が作成されました。これは、最初のマネージャーに保存されたものとまったく同じです。 saveChanges が生成されましたが、新しい主キーが生成されていました。ただし、これにより、2 番目の Person が User に正常に関連付けられました。
一晩考えた後、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 がまだ実装していないものですか? もしそうなら、私がやった方法は大丈夫ですか?