web-dev-qa-db-ja.com

AddOrUpdateが期待どおりに機能せず、重複が生成されます

コードファーストのDBContextベースのEF5セットアップを使用しています。

DbMigrationsConfiguration.SeedでDBにデフォルトのダミーデータを入力しようとしています。このタスクを実行するために、私はDbSet.AddOrUpdateメソッドを使用します。

私の目的を説明するための最も単純なコード:

j = 0;

var cities = new[]
    {
        "Berlin",
        "Vienna",
        "London",
        "Bristol",
        "Rome",
        "Stockholm",
        "Oslo",
        "Helsinki",
        "Amsterdam",
        "Dublin"
    };
var cityObjects = new City[cities.Length];


foreach (string c in cities)
{
    int id = r.NextDouble() > 0.5 ? 0 : 1;
    var city = new City
        {
            Id = j,
            Name = c,
            Slug = c.ToLowerInvariant(),
            Region = regions[id],
            RegionId = regions[id].Id,
            Reviewed = true
        };
    context.CitySet.AddOrUpdate(cc => cc.Id, city);
    cityObjects[j] = city;
    j++;
}

Idフィールドを使用/省略し、Id/Slugプロパティを更新セレクターとして使用しようとしました。

Update-Databaseが実行されると、Idフィールドは無視され、値はSQL Serverによって自動的に生成され、DBは重複で埋められます。 Slugセレクターは重複を許可し、その後の実行で例外(Sequence contains more than one element)を生成します。

AddOrUpdateメソッドはこのように機能することを目的としていますか?手動でアップサートを実行する必要がありますか?

19
berezovskyi

まず(まだ回答はありません)、AddOrUpdateは新しいオブジェクトの配列で呼び出すことができるため、タイプ_City[]_の配列を作成し、context.CitySet.AddOrUpdate(cc => cc.Id, cityArray);を1回呼び出すだけです。

(編集)

次に、AddOrUpdateは識別子式(_cc => cc.Id_)を使用して、配列内の都市と同じIdを持つ都市を検索します。これらの都市は更新されます。配列内の他の都市が挿入されますが、IdはID列であるため、それらのId値はデータベースによって生成されます。挿入ステートメントでは設定できません。 (ID挿入をオンに設定していない限り)。したがって、ID列を持つテーブルにAddOrUpdateを使用する場合、既存のレコードのID値は予測できないため、レコードを識別する別の方法を見つける必要があります。

あなたの場合、Slugの識別子としてAddOrUpdateを使用しました。これは(コメントに従って)一意である必要があります。 Slugsが一致する既存のレコードが更新されない理由は私にはわかりません。

ちょっとしたテストを設定しました。ID(iedntity)と一意の名前でエンティティを追加または更新します。

_var n = new Product { ProductID = 999, ProductName = "Prod1", UnitPrice = 1.25 };
Products.AddOrUpdate(p => p.ProductName, n);
SaveChanges();
_

「Prod1」がまだ存在しない場合は挿入されます(Id 999は無視されます)。
そうであり、UnitPriceが異なる場合は、更新されます。

送信されたクエリを見ると、EFが名前で一意のレコードを探していることがわかります。

_SELECT TOP (2) 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName], 
[Extent1].[UnitPrice] AS [UnitPrice]
FROM [dbo].[Products] AS [Extent1]
WHERE N'Prod1' = [Extent1].[ProductName]
_

そして次に(一致が見つかり、UnitPriceが異なる場合)

_update [dbo].[Products]
set [UnitPrice] = 1.26
where ([ProductID] = 15)
_

これは、EFが1つのレコードを検出し、キーフィールドを使用して更新を実行していることを示しています。

この例を見ることで、あなたの状況が明らかになることを願っています。たぶん、SQLステートメントも監視して、そこで予期しないことが発生するかどうかを確認する必要があります。

28
Gert Arnold
var paidOutType = new List<PaidOutType>
                {
                    new PaidOutType { PaidOutTypeID = 1, Code = "001", Description = "PAID OUT 1", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 },
                    new PaidOutType { PaidOutTypeID = 2, Code = "002", Description = "PAID OUT 2", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 },
                    new PaidOutType { PaidOutTypeID = 3, Code = "002", Description = "PAID OUT 3", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 },
                };
                paidOutType.ForEach(u => smartPOSContext.PaidOutType.AddOrUpdate(u));
                smartPOSContext.SaveChanges();
0
RAJITHA DINESH