6

モデルを更新すると、更新しようとしている子リレーションでエラーが発生します。

私のモデルは、Order が OrderItem と関係があるとします。私の見解では、オーダーアイテムのエディターテンプレートとともにオーダーの詳細があります。データを更新すると、Order へのリンクは null ですが、orderid が入力されているため、リンクできるはずです。TryUpdateModel は true を返しますが、保存は次のように失敗します。

InvalidOperationException: 操作に失敗しました: 1 つ以上の外部キー プロパティが null 非許容であるため、リレーションシップを変更できませんでした。リレーションシップに変更が加えられると、関連する外部キー プロパティが null 値に設定されます。外部キーが null 値をサポートしていない場合は、新しい関係を定義するか、外部キー プロパティに別の非 null 値を割り当てるか、関連のないオブジェクトを削除する必要があります。]

私の更新方法:

    public ActionResult ChangeOrder(Order model)
    {
        var order = this.orderRepository.GetOrder(model.OrderId);

        if (ModelState.IsValid)
        {
            var success = this.TryUpdateModel(order);
        }

        this.orderRepository.Save();

        return this.View(order);
    }

SO やその他のソースで見たすべてのソリューションを試しましたが、どれも成功しませんでした。

.Net MVC 3、EF 4.3.1 を DBContext と共に使用します。

4

2 に答える 2

4

ここにはコードの匂いがたくさんありますが、修正するときはエレガントにしようと思います :)

「注文」があなたのEFエンティティであるとしか思えませんか?その場合は、フォームのビュー モデルを作成し、データをそこにコピーして、ビューとは別にしておくことを強くお勧めします。ビュー モデルには、実際には、フォームが使用または操作するプロパティのみを含める必要があります。

また、orderRepository.GetOrder() は、データ ストアから注文を取得するデータ レイヤー呼び出しであると思いますか?

また、潜在的に未使用の変数を宣言しています。" var order =" はモデルが無効な場合でも読み込まれ、" var success =" は使用されません。

TryUpdateModel と UpdateModel は、実際のプログラミングではあまり堅牢ではありません。正直なところ、彼らがそこにいるべきだと完全に確信しているわけではありません。私は通常、サービス/ファクトリ パターンなど、より抽象化されたアプローチを使用します。より多くの作業が必要になりますが、より多くの制御が可能になります。

あなたの場合、次のパターンをお勧めします。最小限の抽象化がありますが、TryUpdateModel / UpdateModel を使用するよりも多くの制御が可能です。

    public ActionResult ChangeOrder(OrderViewModel model) {
        if(ModelState.IsValid) {
            // Retrieve original order
            var order = orderRepository.GetOrder(model.OrderId);

            // Update primitive properties
            order.Property1 = model.Property1;
            order.Property2 = model.Property2;
            order.Property3 = model.Property3;
            order.Property4 = model.Property4;

            // Update collections manually
            order.Collection1 = model.Collection1.Select(x => new Collection1Item {
                Prop1 = x.Prop1,
                Prop2 = x.Prop2
            });

            try {
                // Save to repository
                orderRepository.SaveOrder(order);
            } catch (Exception ex) {
                ModelState.AddModelError("", ex.Message);
                return View(model);
            }
            return RedirectToAction("SuccessAction");
        }
        return View(model);
    }

理想的ではありませんが、もう少し役立つはずです...

この投稿を参照してください。これは同様です。

于 2012-07-17T09:09:48.260 に答える
3

ユーザーはあなたのビューで次のアクションを実行できると思います。

  1. 注文 (ヘッダー) データの変更
  2. 既存の注文項目を削除する
  3. 注文商品データの修正
  4. 新しい注文項目を追加する

変更されたオブジェクト グラフ (注文 + 注文アイテムのリスト) を正しく更新するには、4 つのケースすべてに対処する必要があります。TryUpdateModelデータベース内のオブジェクト グラフを正しく更新できません。

を使用して次のコードを直接記述しますcontext。コンテキストの使用をリポジトリに抽象化できます。次のコードに含まれるすべてのリポジトリで同じコンテキスト インスタンスを使用していることを確認してください。

public ActionResult ChangeOrder(Order model)
{
    if (ModelState.IsValid)
    {
        // load the order from DB INCLUDING the current order items in the DB
        var orderInDB = context.Orders.Include(o => o.OrderItems)
            .Single(o => o.OrderId == model.OrderId);

        // (1) Update modified order header properties
        context.Entry(orderInDB).CurrentValues.SetValues(model);

        // (2) Delete the order items from the DB
        // that have been removed in the view
        foreach (var item in orderInDB.OrderItems.ToList())
        {
            if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
                context.OrderItems.Remove(item);
                // Omitting this call "Remove from context/DB" causes
                // the exception you are having
        }

        foreach (var item in model.OrderItems)
        { 
            var orderItem = orderInDB.OrderItems
                .SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);

            if (orderItem != null)
            {
                // (3) Existing order item: Update modified item properties
                context.Entry(orderItem).CurrentValues.SetValues(item);
            }
            else
            {
                // (4) New order item: Add it
                orderInDB.OrderItems.Add(item);
            }
        }

        context.SaveChanges();

        return RedirectToAction("Index"); // or some other view
    }

    return View(model);
}
于 2012-07-17T10:43:32.270 に答える