Oleh Password, Anda berarti password hash, kan? :-)
Edward Brey
Jawaban:
370
Jawaban Ladislav diperbarui untuk menggunakan DbContext (diperkenalkan pada EF 4.1):
public void ChangePassword(int userId, string password){
var user= new User(){ Id = userId, Password = password };using(var db = new MyEfContextName()){
db.Users.Attach(user);
db.Entry(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();}}
Saya hanya dapat membuat kode ini berfungsi dengan menambahkan db.Configuration.ValidateOnSaveEnabled = false; sebelum db. Simpan Perubahan ()?
Jake Drew
3
Namespace mana yang harus dimasukkan untuk digunakan db.Entry(user).Property(x => x.Password).IsModified = true;dan tidakdb.Entry(user).Property("Password").IsModified = true;
Johan
5
Pendekatan ini melempar OptimisticConcurencyException ketika tabel memiliki bidang stempel waktu.
Maksim Vi.
9
Saya pikir layak disebutkan bahwa jika Anda menggunakan db.Configuration.ValidateOnSaveEnabled = false;Anda mungkin ingin terus memvalidasi bidang yang Anda perbarui:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Ziul
2
Anda perlu mengatur ValidateOnSaveEnabled menjadi false jika Anda memiliki bidang yang diperlukan dalam tabel Anda yang tidak Anda berikan selama pembaruan Anda
Sal
54
Anda dapat memberi tahu EF properti mana yang harus diperbarui dengan cara ini:
public void ChangePassword(int userId, string password){
var user= new User{ Id = userId, Password = password };using(var context = new ObjectContext(ConnectionString)){
var users = context.CreateObjectSet<User>();
users.Attach(user);
context.ObjectStateManager.GetObjectStateEntry(user).SetModifiedProperty("Password");
context.SaveChanges();}}
lanjutkan EF, jika demikian, Anda akan melakukannya
memuat objek berdasarkan yang userIddisediakan - seluruh objek akan dimuat
perbarui passwordbidang
menyimpan objek kembali menggunakan konteks ini .SaveChanges()metode
Dalam hal ini, tergantung pada EF bagaimana menangani hal ini secara detail. Saya baru saja menguji ini, dan dalam hal ini saya hanya mengubah satu bidang objek, apa yang dibuat EF adalah apa yang Anda buat secara manual juga - sesuatu seperti:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
Jadi EF cukup pintar untuk mengetahui kolom apa yang memang telah berubah, dan itu akan membuat pernyataan T-SQL untuk menangani hanya pembaruan yang sebenarnya diperlukan.
Anda mendefinisikan prosedur tersimpan yang melakukan persis apa yang Anda butuhkan, dalam kode T-SQL (cukup perbarui Passwordkolom untuk yang diberikan UserIddan tidak ada yang lain - pada dasarnya dijalankan UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId) dan Anda membuat impor fungsi untuk prosedur tersimpan itu dalam model EF Anda dan Anda menyebutnya berfungsi alih-alih melakukan langkah-langkah yang diuraikan di atas
public class Thing
{[Key]public int Id { get;set;}public string Info { get;set;}public string OtherStuff { get;set;}}
dbcontext:
public class MyDataContext : DbContext
{public DbSet<Thing > Things { get;set;}}
kode accessor:
MyDataContext ctx = new MyDataContext();// FIRST create a blank object
Thing thing = ctx.Things.Create();// SECOND set the ID
thing.Id = id;// THIRD attach the thing (id isnot marked as modified)
db.Things.Attach(thing);// FOURTH set the fields you want updated.
thing.OtherStuff ="only want this field updated.";// FIFTH save that thing
db.SaveChanges();
Saya mendapatkan kesalahan validasi entitas ketika saya mencoba ini, tapi itu pasti terlihat keren.
devlord
Tidak berhasil dengan metode ini !!!: mungkin Anda perlu memberikan detail lebih lanjut cara menggunakannya !!! - ini adalah kesalahan: "Melampirkan entitas tipe 'Domain.Job' gagal karena entitas lain dari tipe yang sama sudah memiliki nilai kunci primer yang sama. Ini dapat terjadi saat menggunakan metode 'Lampirkan' atau pengaturan status suatu entitas menjadi 'Tidak berubah' atau 'Dimodifikasi' jika ada entitas dalam grafik yang memiliki nilai kunci yang bertentangan. Ini mungkin karena beberapa entitas baru dan belum menerima nilai kunci yang dihasilkan basis data. "
Lucian Bumb
Perfec! Periksa jawaban saya untuk melihat pendekatan yang fleksibel untuk model apa pun!
kryp
10
Saat mencari solusi untuk masalah ini, saya menemukan variasi pada jawaban GONeale melalui blog Patrick Desjardins :
public int Update(T entity, Expression<Func<T, object>>[] properties){
DatabaseContext.Entry(entity).State = EntityState.Unchanged;
foreach (var property in properties){
var propertyName = ExpressionHelper.GetExpressionText(property);
DatabaseContext.Entry(entity).Property(propertyName).IsModified = true;}return DatabaseContext.SaveChangesWithoutValidation();}
" Seperti yang Anda lihat, parameter kedua merupakan ekspresi fungsi. Parameter ini akan menggunakan metode ini dengan menentukan dalam ekspresi Lambda properti mana yang akan diperbarui. "
Metode yang saya gunakan saat ini dalam kode saya sendiri , diperluas untuk menangani juga (Linq) Ekspresi jenis ExpressionType.Convert. Ini diperlukan dalam kasus saya, misalnya dengan Guiddan properti objek lainnya. Itu 'dibungkus' dalam Konversi () dan karenanya tidak ditangani oleh System.Web.Mvc.ExpressionHelper.GetExpressionText.
Ketika saya menggunakan ini memberi saya kesalahan berikut, Tidak dapat mengonversi ekspresi Lambda ke Ketik 'Ekspresi <Fungsi <RequestDetail, objek >> []' karena ini bukan tipe delegasi
Imran Rizvi
@ImranRizvi, Anda hanya perlu memperbarui parameter ke: public int Update (entitas T, params Ekspresi <Func <T, object >> [] properti) CATATAN kata kunci params sebelum ekspresi
dalcam
6
Saya terlambat ke permainan di sini, tetapi ini adalah bagaimana saya melakukannya, saya menghabiskan beberapa saat mencari solusi yang membuat saya puas; ini menghasilkan UPDATEpernyataan HANYA untuk bidang yang diubah, saat Anda secara eksplisit mendefinisikan apa itu melalui konsep "daftar putih" yang lebih aman untuk mencegah injeksi formulir web.
Kutipan dari repositori data ISession saya:
public bool Update<T>(T item, params string[] changedPropertyNames)where T
: class, new(){
_context.Set<T>().Attach(item);
foreach (var propertyName in changedPropertyNames){//If we can't find the property, this line wil throw an exception,//which is good as we want to know about it
_context.Entry(item).Property(propertyName).IsModified = true;}return true;}
Ini bisa dibungkus dalam percobaan .. jika Anda menginginkannya, tapi saya pribadi ingin penelepon saya tahu tentang pengecualian dalam skenario ini.
Ini akan dipanggil dengan cara seperti ini (bagi saya, ini melalui ASP.NET Web API):
if(!session.Update(franchiseViewModel.Franchise, new[]{"Name","StartDate"}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Jadi solusi yang lebih baik Anda adalah apa yang Elisa? Anda harus secara eksplisit menyatakan properti apa yang Anda izinkan untuk diperbarui (seperti daftar putih yang diperlukan untuk UpdateModelperintah ASP.NET MVC ), dengan cara itu Anda memastikan injeksi form hacker tidak dapat terjadi dan mereka tidak dapat memperbarui bidang yang tidak diizinkan untuk diperbarui. Namun, jika seseorang dapat mengonversi array string menjadi semacam parameter ekspresi lambda dan bekerja dengannya dalam Update<T>, hebat
@ Elisa Ini dapat ditingkatkan dengan menggunakan Func <T, List <object>> bukan string []
Spongebob Kamerad
Bahkan kemudian ke permainan, dan mungkin ini sintaks yang jauh lebih baru, tetapi var entity=_context.Set<T>().Attach(item);diikuti oleh entity.Property(propertyName).IsModified = true;dalam lingkaran harus bekerja.
Auspex
4
Kerangka kerja entitas melacak perubahan Anda pada objek yang Anda tanyakan dari database melalui DbContext. Sebagai contoh jika nama instance DbContext Anda adalah dbContext
public void ChangePassword(int userId, string password){
var user= dbContext.Users.FirstOrDefault(u=>u.UserId == userId);user.password = password;
dbContext.SaveChanges();}
Dan bagaimana seharusnya tampilan dalam kasus ini?
Emanuela Colta
Ini salah karena akan menyimpan seluruh objek Pengguna dengan kata sandi yang diubah.
amuliar
itu benar, tetapi sisa objek Pengguna akan sama seperti sebelumnya dalam konteks, satu-satunya hal yang mungkin akan berbeda adalah kata sandi sehingga pada dasarnya hanya memperbarui kata sandi.
Tomislav3008
3
Saya tahu ini adalah utas lama tapi saya juga mencari solusi yang sama dan memutuskan untuk pergi dengan solusi @ Doku-begitu disediakan. Saya berkomentar untuk menjawab pertanyaan yang diajukan oleh @Imran Rizvi, saya mengikuti tautan @ Doku-so yang menunjukkan implementasi serupa. Pertanyaan @Imran Rizvi adalah bahwa ia mendapatkan kesalahan menggunakan solusi yang disediakan 'Tidak dapat mengonversi ekspresi Lambda ke Ketik' Ekspresi> [] 'karena ini bukan tipe delegasi'. Saya ingin menawarkan modifikasi kecil yang saya buat untuk solusi @ Doku-so yang memperbaiki kesalahan ini jika ada orang lain yang menemukan posting ini dan memutuskan untuk menggunakan solusi @ Doku-so.
Masalahnya adalah argumen kedua dalam metode Pembaruan,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Untuk memanggil metode ini menggunakan sintaks yang disediakan ...
Anda harus menambahkan kata kunci 'params' di depan arugment kedua.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
atau jika Anda tidak ingin mengubah tanda tangan metode kemudian memanggil metode Perbarui Anda perlu menambahkan kata kunci ' baru ', tentukan ukuran array, lalu akhirnya gunakan sintaks initializer objek koleksi untuk setiap properti untuk memperbarui seperti yang terlihat di bawah.
Update(Model, new Expression<Func<T, object>>[3]{ d=>d.Name },{ d=>d.SecondProperty },{ d=>d.AndSoOn });
Dalam contoh @ Doku-so, ia menetapkan array Ekspresi sehingga Anda harus meneruskan properti untuk memperbarui dalam array, karena array Anda juga harus menentukan ukuran array. Untuk menghindari ini, Anda juga bisa mengubah argumen ekspresi untuk menggunakan IEnumerable, bukan array.
Inilah implementasi solusi @ Doku-so saya.
public int Update<TEntity>(LcmsEntities dataContext, DbEntityEntry<TEntity> entityEntry, params Expression<Func<TEntity, object>>[] properties)where TEntity: class
{
entityEntry.State = System.Data.Entity.EntityState.Unchanged;
properties.ToList().ForEach((property)=>{
var propertyName = string.Empty;
var bodyExpression = property.Body;if(bodyExpression.NodeType == ExpressionType.Convert&& bodyExpression is UnaryExpression){
Expression operand =((UnaryExpression)property.Body).Operand;
propertyName =((MemberExpression)operand).Member.Name;}else{
propertyName = System.Web.Mvc.ExpressionHelper.GetExpressionText(property);}
entityEntry.Property(propertyName).IsModified = true;});
dataContext.Configuration.ValidateOnSaveEnabled = false;return dataContext.SaveChanges();}
Pemakaian:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so memberikan pendekatan keren menggunakan generik, saya menggunakan konsep untuk menyelesaikan masalah saya tetapi Anda tidak bisa menggunakan solusi @ Doku-so seperti pada posting ini dan posting terkait tidak ada yang menjawab pertanyaan kesalahan penggunaan.
Saya sedang mengerjakan solusi Anda ketika program melewati entityEntry.State = EntityState.Unchanged;semua nilai yang diperbarui dalam parameter entityEntrydapatkan kembali, jadi tidak ada perubahan yang disimpan, dapatkah Anda membantu, terima kasih
sairfan
3
Di EntityFramework Core 2.x tidak perlu untuk Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;// other property changes might come here
context.SaveChanges();
Mencoba ini di SQL Server dan memprofilkannya:
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [User] SET [someProp] = @p0
WHERE [UserId] = @p1;
SELECT @@ROWCOUNT;
',N'@p1 int,@p0 bit',@p1=1223424,@p0=1
Temukan memastikan bahwa entitas yang sudah dimuat tidak memicu SELECT dan juga secara otomatis melampirkan entitas jika diperlukan (dari dokumen):
/// Finds an entity with the given primarykeyvalues.If an entity with the given primarykeyvalues///is being tracked by the context,then it is returned immediately without making a request to the
///database. Otherwise, a query is made to the databasefor an entity with the given primarykeyvalues///and this entity,if found,is attached to the context and returned.If no entity is found,then///nullis returned.
Bagaimana membuat metode ini tersedia untuk kelas lain, mungkin seperti metode ekstensi?
Velkumar
dalam tutorial .NET CORE ini, mereka menunjukkan praktik terbaik menggunakan EF Core (baru) untuk memperbarui properti spesifik di MVC. cari 'TryUpdateModelAsync'.
Guy
1
@Beli Keren. Padahal, sekali lagi "praktik terbaik" Microsoft adalah melakukan sesuatu selain dari apa yang dibuat alat mereka ...
Auspex
Ini solusi yang bagus.
Timothy Macharia
1
Saya menggunakan ValueInjecternuget untuk menyuntikkan Binding Model ke dalam basis data Entity menggunakan berikut:
public async Task<IHttpActionResult>Add(CustomBindingModel model){
var entity= await db.MyEntities.FindAsync(model.Id);if(entity==null)return NotFound();
entity.InjectFrom<NoNullsInjection>(model);
await db.SaveChangesAsync();return Ok();}
Perhatikan penggunaan konvensi khusus yang tidak memperbarui Properti jika mereka null dari server.
Anda tidak akan tahu apakah properti itu sengaja dihapus nol atau hanya tidak memiliki nilai apa pun. Dengan kata lain, nilai properti hanya dapat diganti dengan nilai lain tetapi tidak dihapus.
public void ChangePassword(int userId, string password){
var user= new User{ Id = userId, Password = password };using(var db = new DbContextName()){
db.Entry(user).State = EntityState.Added;
db.SaveChanges();}}
Password
, Anda berarti password hash, kan? :-)Jawaban:
Jawaban Ladislav diperbarui untuk menggunakan DbContext (diperkenalkan pada EF 4.1):
sumber
db.Entry(user).Property(x => x.Password).IsModified = true;
dan tidakdb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
Anda mungkin ingin terus memvalidasi bidang yang Anda perbarui:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Anda dapat memberi tahu EF properti mana yang harus diperbarui dengan cara ini:
sumber
Pada dasarnya Anda memiliki dua opsi:
userId
disediakan - seluruh objek akan dimuatpassword
bidang.SaveChanges()
metodeDalam hal ini, tergantung pada EF bagaimana menangani hal ini secara detail. Saya baru saja menguji ini, dan dalam hal ini saya hanya mengubah satu bidang objek, apa yang dibuat EF adalah apa yang Anda buat secara manual juga - sesuatu seperti:
Jadi EF cukup pintar untuk mengetahui kolom apa yang memang telah berubah, dan itu akan membuat pernyataan T-SQL untuk menangani hanya pembaruan yang sebenarnya diperlukan.
Password
kolom untuk yang diberikanUserId
dan tidak ada yang lain - pada dasarnya dijalankanUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) dan Anda membuat impor fungsi untuk prosedur tersimpan itu dalam model EF Anda dan Anda menyebutnya berfungsi alih-alih melakukan langkah-langkah yang diuraikan di atassumber
Di Entity Framework Core,
Attach
mengembalikan entri, jadi yang Anda butuhkan adalah:sumber
Saya menggunakan ini:
kesatuan:
dbcontext:
kode accessor:
sumber
Saat mencari solusi untuk masalah ini, saya menemukan variasi pada jawaban GONeale melalui blog Patrick Desjardins :
(Solusi yang agak mirip juga diberikan di sini: https://stackoverflow.com/a/5749469/2115384 )
Metode yang saya gunakan saat ini dalam kode saya sendiri , diperluas untuk menangani juga (Linq) Ekspresi jenis
ExpressionType.Convert
. Ini diperlukan dalam kasus saya, misalnya denganGuid
dan properti objek lainnya. Itu 'dibungkus' dalam Konversi () dan karenanya tidak ditangani olehSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.sumber
Saya terlambat ke permainan di sini, tetapi ini adalah bagaimana saya melakukannya, saya menghabiskan beberapa saat mencari solusi yang membuat saya puas; ini menghasilkan
UPDATE
pernyataan HANYA untuk bidang yang diubah, saat Anda secara eksplisit mendefinisikan apa itu melalui konsep "daftar putih" yang lebih aman untuk mencegah injeksi formulir web.Kutipan dari repositori data ISession saya:
Ini bisa dibungkus dalam percobaan .. jika Anda menginginkannya, tapi saya pribadi ingin penelepon saya tahu tentang pengecualian dalam skenario ini.
Ini akan dipanggil dengan cara seperti ini (bagi saya, ini melalui ASP.NET Web API):
sumber
UpdateModel
perintah ASP.NET MVC ), dengan cara itu Anda memastikan injeksi form hacker tidak dapat terjadi dan mereka tidak dapat memperbarui bidang yang tidak diizinkan untuk diperbarui. Namun, jika seseorang dapat mengonversi array string menjadi semacam parameter ekspresi lambda dan bekerja dengannya dalamUpdate<T>
, hebatvar entity=_context.Set<T>().Attach(item);
diikuti olehentity.Property(propertyName).IsModified = true;
dalam lingkaran harus bekerja.Kerangka kerja entitas melacak perubahan Anda pada objek yang Anda tanyakan dari database melalui DbContext. Sebagai contoh jika nama instance DbContext Anda adalah dbContext
sumber
Saya tahu ini adalah utas lama tapi saya juga mencari solusi yang sama dan memutuskan untuk pergi dengan solusi @ Doku-begitu disediakan. Saya berkomentar untuk menjawab pertanyaan yang diajukan oleh @Imran Rizvi, saya mengikuti tautan @ Doku-so yang menunjukkan implementasi serupa. Pertanyaan @Imran Rizvi adalah bahwa ia mendapatkan kesalahan menggunakan solusi yang disediakan 'Tidak dapat mengonversi ekspresi Lambda ke Ketik' Ekspresi> [] 'karena ini bukan tipe delegasi'. Saya ingin menawarkan modifikasi kecil yang saya buat untuk solusi @ Doku-so yang memperbaiki kesalahan ini jika ada orang lain yang menemukan posting ini dan memutuskan untuk menggunakan solusi @ Doku-so.
Masalahnya adalah argumen kedua dalam metode Pembaruan,
Untuk memanggil metode ini menggunakan sintaks yang disediakan ...
Anda harus menambahkan kata kunci 'params' di depan arugment kedua.
atau jika Anda tidak ingin mengubah tanda tangan metode kemudian memanggil metode Perbarui Anda perlu menambahkan kata kunci ' baru ', tentukan ukuran array, lalu akhirnya gunakan sintaks initializer objek koleksi untuk setiap properti untuk memperbarui seperti yang terlihat di bawah.
Dalam contoh @ Doku-so, ia menetapkan array Ekspresi sehingga Anda harus meneruskan properti untuk memperbarui dalam array, karena array Anda juga harus menentukan ukuran array. Untuk menghindari ini, Anda juga bisa mengubah argumen ekspresi untuk menggunakan IEnumerable, bukan array.
Inilah implementasi solusi @ Doku-so saya.
Pemakaian:
@ Doku-so memberikan pendekatan keren menggunakan generik, saya menggunakan konsep untuk menyelesaikan masalah saya tetapi Anda tidak bisa menggunakan solusi @ Doku-so seperti pada posting ini dan posting terkait tidak ada yang menjawab pertanyaan kesalahan penggunaan.
sumber
entityEntry.State = EntityState.Unchanged;
semua nilai yang diperbarui dalam parameterentityEntry
dapatkan kembali, jadi tidak ada perubahan yang disimpan, dapatkah Anda membantu, terima kasihDi EntityFramework Core 2.x tidak perlu untuk
Attach
:Mencoba ini di SQL Server dan memprofilkannya:
Temukan memastikan bahwa entitas yang sudah dimuat tidak memicu SELECT dan juga secara otomatis melampirkan entitas jika diperlukan (dari dokumen):
sumber
Menggabungkan beberapa saran saya usulkan sebagai berikut:
dipanggil oleh
Atau oleh
Atau oleh
sumber
Saya menggunakan
ValueInjecter
nuget untuk menyuntikkan Binding Model ke dalam basis data Entity menggunakan berikut:Perhatikan penggunaan konvensi khusus yang tidak memperbarui Properti jika mereka null dari server.
ValueInjecter v3 +
Pemakaian:
Value Injecter V2
Cari jawaban ini
Peringatan
Anda tidak akan tahu apakah properti itu sengaja dihapus nol atau hanya tidak memiliki nilai apa pun. Dengan kata lain, nilai properti hanya dapat diganti dengan nilai lain tetapi tidak dihapus.
sumber
Saya mencari yang sama dan akhirnya saya menemukan solusinya
percayalah itu bekerja untuk saya seperti pesona.
sumber
Inilah yang saya gunakan, menggunakan custom InjectNonNull (obj dest, obj src) yang membuatnya sepenuhnya fleksibel
sumber
sumber
sumber