最初のクエリなしでレコードを更新しますか?


103

データベースにクエリを実行して、アイテムのリストをロードするとします。次に、アイテムの1つを詳細ビューフォームで開き、データベースからアイテムを再クエリする代わりに、リストのデータソースからアイテムのインスタンスを作成します。

個々のアイテムのレコードをフェッチせずにデータベースレコードを更新する方法はありますか?

これが私が今それをやっている方法のサンプルです:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

次に、レコードをプルした後、アイテムのいくつかの値を更新して、レコードをプッシュバックします。

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

これを行うにはもっと良い方法があると思いますか?


2
それは物事を行うのにひどく悪い方法ではありません。そのテーブルに同時にアクセスできますか?
Henk Holterman、2010年

これは、EFのようなORMがサービスを提供するためのまさにその使用法だと思います。アプリケーションのコンテキスト内の操作を、基になるDB実装に関係なく、作成、変更、削除するオブジェクトに対して実行できるようにするには、
Pero P.

40
TSQLのバックグラウンドを持ち、ORMを受け入れて受け入れようとしている開発者は、レコードを検索して更新するだけであり、フェッチしたデータを利用しないことは少し非効率的だと思います。開発者が基礎となるDB実装に関与する必要がないというこの概念は、人ごみです。開発者がシステム全体について知っているほど、ソリューションは改善されます。オプションは決して悪いことではありません。
バリーピッカー

1
ORMアプローチは実際のオブジェクトには問題ありませんが、データベースに他のもの(大きなバイナリBLOBなど)も格納している場合、最初に元のコンテンツを読み込まずにそれらを更新できると非常に便利です。
BrainSlugs83 2017年

回答:


68

Attach()メソッドを使用する必要があります。

オブジェクトのアタッチとデタッチ


16
例を提供できますか?
Bart Calixto

16
context.Products.Attach(product); context.Entry(product).State = EntityState.Modified;
ガブリエル

6
@Gabrielこれはすべてのプロパティを更新しませんか?1つだけ変更したい場合はどうなりますか?
David Pfeffer、2012

22
はい、これですべてのプロパティが更新されます。単一のプロパティを更新する場合は、次のようにします。context.Entry(user).Property(x => x.Property).IsModified = true; (ここで見てstackoverflow.com/a/5567616/57369を
ガブリエル

6
context.Entry()は.net 4.1でのみ使用できます。4.0をまだ使用している場合(私のように)、代わりにこれを確認してください:stackoverflow.com/questions/7113434/where-is- 基本的には次のコンテキストエントリ:context.ObjectStateManager.ChangeObjectState(yourObject、EntityState.Modified);
dyslexicanaboko 2012

39

データストアのコンテキストを使用して、データベースに対して直接SQLを使用することもできます。例:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

パフォーマンス上の理由から、ハードコードされた単一のSQL文字列ではなく、変数を渡すことができます。これにより、SQL Serverはクエリをキャッシュし、パラメーターで再利用できます。例:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

更新-EF 6.0の場合

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

9
コメントを残さずにこの回答をダウングレードする理由 この提案は、元の著者の質問にスポットを当てて対処します。
バリーピッカー

18
ExecuteStoreCommandこれは実際にはこれを実行するEFの方法ではありません。コマンドを実行するためにDbConnection含まれているDbContextを使用しているだけです。これはデータベースに依存せず、永続性に依存しません(たとえば、OPがXMLに切り替えた場合、この例はクラッシュします)。
just.another.programmer

9
@ just.another.programmer-大きな力には大きな責任が伴います。
バリーピッカー

13
それは永続的な不可知論でなければなりませんか?ストレージシステムを1日おきに交換するわけではありません。
デビッド

5
@ BrainSlugs83-OpenQueryのみをサポートするリンクサーバー全体でEFを使用してみてください-とても楽しいです。場合によっては、仕事を完了するために生のSQLが絶対に必要です。常にコードを分離してテストできるとは限りません。その世界は完璧ではありません。
バリーピッカー2017年

20

コード:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

結果のTSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

注意:

「IsModified = true」の行が必要なのは、新しいExampleEntityオブジェクトを作成すると(Idプロパティが設定されている場合のみ)、他のすべてのプロパティにはデフォルト値(0、nullなど)があるためです。「デフォルト値」でDBを更新する場合、変更はエンティティフレームワークによって検出されず、DBは更新されません。

例では:

exampleEntity.ExampleProperty = null;

空のExampleEntityオブジェクトを作成したとき、プロパティExamplePropertyはすでにnullであるため、行「IsModified = true」がないと機能しません。EFに、この列を更新する必要があることを伝える必要があります。これがこの行の目的です。


これは完璧です。私はこれをテストしました、そしてそれはまさに私が欲しかったものです。変更はEFインフラストラクチャ(EntityFramework.Triggersプロジェクトの使用を含む)を通過する必要がありますが、主キーのみを持つ1つの列を変更できるようにしたいと考えています。
MikeJansen

11

DataItemEFが事前検証するフィールドがある場合(null不可フィールドなど)、このコンテキストの検証を無効にする必要があります。

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

それ以外の場合は、事前検証を満たしても、単一の列のみを更新できます。

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

と仮定するdataEntitySystem.Data.Entity.DbContext

これをに追加して、生成されたクエリを確認できますDbContext

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);


0

EF Coreでは多少異なります。

EF Coreでこれを行うにはより高速な方法があるかもしれませんが、以下はSELECT(.NET Framework 4.6.2のEF Core 2およびJETでテスト済み)を実行せずにUPDATEを確実にします。

モデルにIsRequiredプロパティがないことを確認します

次に、次のテンプレート(VB.NET)を使用します。

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using

-1

一般的に言って、Entity Frameworkを使用してすべてのアイテムをクエリし、エンティティオブジェクトを保存した場合、エンティティオブジェクトの個々のアイテムを更新し、SaveChanges()終了時に呼び出すことができます。例えば:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

必要な1つのアイテムを取得しても、新しいクエリは生成されません。


興味深い答えです、誰か他の人がこれを確認しましたか?
Ian

5
これはOPの問題と同じことを行います。レコード全体をフェッチしてから更新します。.First()はオブジェクトをデシリアライズします。
Jerther、2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.