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.