Zaktualizować rekord bez uprzedniego zapytania?


104

Powiedzmy, że sprawdzam bazę danych i ładuję listę pozycji. Następnie otwieram jeden z elementów w formularzu widoku szczegółowego i zamiast ponownego odpytywania elementu z bazy danych, tworzę wystąpienie elementu ze źródła danych na liście.

Czy istnieje sposób, aby zaktualizować rekord bazy danych bez pobierania rekordu pojedynczego elementu?

Oto próbka, jak teraz to robię:

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

Następnie po wyciągnięciu rekordu aktualizuję niektóre wartości w elemencie i wsuwam rekord z powrotem:

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

Pomyślałbym, że byłby na to lepszy sposób, jakieś pomysły?


2
To nie jest strasznie zły sposób robienia rzeczy. Czy masz jednoczesny dostęp do tej tabeli?
Henk Holterman

Myślę, że jest to użycie, które ORM taki jak EF jest dokładnie po to, aby służyć. Aby umożliwić wykonywanie operacji w kontekście aplikacji na obiektach, które chcesz utworzyć / zmodyfikować / usunąć, bez obawy o implementację bazowej bazy danych?
Pero P.

40
Myślę, że dla programistów z doświadczeniem w TSQL, próbujących zaakceptować i przyjąć ORM, wyszukiwanie rekordu tylko w celu jego aktualizacji i nigdy nie wykorzystuje pobranych danych jest nieco nieefektywne. Koncepcja, że ​​programista nie musi zajmować się podstawową implementacją bazy danych, jest bzdurą. Im więcej programista wie o całym systemie, tym lepsze może być rozwiązanie. Opcje nigdy nie są złe.
barrypicker

1
Podejście ORM jest dobre dla rzeczywistych obiektów, ale jeśli przechowujesz również inne rzeczy w swojej bazie danych (takie jak duże binarne bloby), bardzo przydatna może być możliwość ich aktualizacji bez wcześniejszego ładowania oryginalnej zawartości.
BrainSlugs83

Odpowiedzi:


68

17
czy możesz podać przykład?
Bart Calixto

17
context.Products.Attach (produkt); context.Entry (produkt) .State = EntityState.Modified;
Gabriel

7
@Gabriel Czy ta aktualizacja nie zaktualizuje jednak wszystkich właściwości? A jeśli chcę zmodyfikować tylko jedną?
David Pfeffer,

22
Tak, to zaktualizuje wszystkie właściwości. Jeśli chcesz zaktualizować pojedynczą właściwość, możesz to zrobić: context.Entry (użytkownik) .Property (x => x.Property) .IsModified = true; (zajrzyj tutaj stackoverflow.com/a/5567616/57369 )
Gabriel

6
Chciałbym tylko dodać ten kontekst. Entry () jest dostępne tylko w .net 4.1, jeśli nadal używasz 4.0 (tak jak ja), sprawdź to dla alternatywy: stackoverflow.com/questions/7113434/where-is- wpis kontekstu, który zasadniczo jest: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko

39

Możesz również użyć bezpośredniego SQL w stosunku do bazy danych, używając kontekstu magazynu danych. Przykład:

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

Ze względu na wydajność możesz chcieć przekazać zmienne zamiast pojedynczego zakodowanego na stałe ciągu SQL. Umożliwi to SQL Server buforowanie zapytania i ponowne użycie z parametrami. Przykład:

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

UPDATE - dla EF 6.0

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

9
dlaczego miałbyś obniżyć tę odpowiedź bez pozostawiania komentarza. Ta sugestia trafnie odnosi się do pytania oryginalnych autorów.
barrypicker

18
ExecuteStoreCommandnie jest tak naprawdę sposobem EF, aby to zrobić, po prostu używa DbConnectionzawartego wewnątrz elementu DbContextdo wykonania polecenia. Nie jest to agnostyk bazy danych, nie mówiąc już o agnostyce trwałości (np. Ten przykład ulegnie awarii, jeśli OP przełączy się na XML).
just.another.programmer

9
@ just.another.programmer - z wielką mocą wiąże się wielka odpowiedzialność.
barrypicker

13
Czy to musi być agnostyk wytrwałości? To nie tak, że będziesz zmieniać system przechowywania danych co drugi dzień.
David

5
@ BrainSlugs83 - spróbuj użyć EF na serwerach linków, które obsługują tylko OpenQuery - dużo zabawy. Czasami absolutnie potrzebujesz surowego kodu SQL, aby wykonać zadanie. Nie zawsze można wyodrębnić kod w celu przetestowania. To nie jest doskonały świat.
barrypicker

20

Kod:

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();

Wynik TSQL:

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

Uwaga:

Linia „IsModified = true” jest potrzebna, ponieważ podczas tworzenia nowego obiektu ExampleEntity (tylko z wypełnioną właściwością Id) wszystkie inne właściwości mają swoje wartości domyślne (0, null itd.). Jeśli chcesz zaktualizować bazę danych za pomocą „wartości domyślnej”, zmiana nie zostanie wykryta przez strukturę jednostki, a następnie baza danych nie zostanie zaktualizowana.

W przykładzie:

exampleEntity.ExampleProperty = null;

nie będzie działać bez wiersza „IsModified = true”, ponieważ właściwość ExampleProperty ma już wartość null podczas tworzenia pustego obiektu ExampleEntity, należy powiedzieć EF, że ta kolumna musi zostać zaktualizowana i taki jest cel tego wiersza.


To jest doskonałe. Właśnie to przetestowałem i właśnie tego chciałem. Chcę, aby zmiany przechodziły przez infrastrukturę EF (w tym przy użyciu projektu EntityFramework.Triggers), ale chciałem mieć możliwość zmiany 1 kolumny, mając tylko klucz podstawowy.
MikeJansen

11

Jeśli DataItemma pola EF będzie wstępnie sprawdzać poprawność (podobnie jak pola niedopuszczalne do wartości null), będziemy musieli wyłączyć tę walidację dla tego kontekstu:

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;

W przeciwnym razie możemy spróbować wykonać wstępną walidację i nadal aktualizować tylko jedną kolumnę:

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();

Zakładając, że dataEntityjestSystem.Data.Entity.DbContext

Możesz zweryfikować wygenerowane zapytanie, dodając to do DbContext:

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


0

Działa nieco inaczej w EF Core:

Może istnieć szybszy sposób na zrobienie tego w EF Core, ale poniższe zapewnia AKTUALIZACJĘ bez konieczności wykonywania polecenia SELECT (testowane z EF Core 2 i JET w .NET Framework 4.6.2):

Upewnij się, że model nie ma właściwości IsRequired

Następnie użyj następującego szablonu (w 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

Ogólnie rzecz biorąc, jeśli używasz Entity Framework do wykonywania zapytań o wszystkie elementy i zapisałeś obiekt jednostki, możesz zaktualizować poszczególne elementy w obiekcie jednostki i wywołać SaveChanges()po zakończeniu. Na przykład:

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();

Pobranie żądanej pozycji nie powinno generować nowego zapytania.


Ciekawa odpowiedź, czy ktoś jeszcze to potwierdził?
Ian,

5
Robi to samo, co problem OP: pobierz cały rekord, a następnie zaktualizuj go. .First () deserializuje obiekt.
Jerther
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.