Jawaban:
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();
}
}
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:
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();
}
}
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:
`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.
Password
kolom untuk yang diberikan UserId
dan 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 atasDi Entity Framework Core, Attach
mengembalikan entri, jadi yang Anda butuhkan adalah:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Saya menggunakan ini:
kesatuan:
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 is not 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();
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. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(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 dengan Guid
dan properti objek lainnya. Itu 'dibungkus' dalam Konversi () dan karenanya tidak ditangani oleh System.Web.Mvc.ExpressionHelper.GetExpressionText
.
public int Update(T entity, Expression<Func<T, object>>[] properties)
{
DbEntityEntry<T> entry = dataContext.Entry(entity);
entry.State = EntityState.Unchanged;
foreach (var property in properties)
{
string propertyName = "";
Expression 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);
}
entry.Property(propertyName).IsModified = true;
}
dataContext.Configuration.ValidateOnSaveEnabled = false;
return dataContext.SaveChanges();
}
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:
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));
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 dalam Update<T>
, hebat
var entity=_context.Set<T>().Attach(item);
diikuti oleh entity.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
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
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 ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
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.
entityEntry.State = EntityState.Unchanged;
semua nilai yang diperbarui dalam parameter entityEntry
dapatkan kembali, jadi tidak ada perubahan yang disimpan, dapatkah Anda membantu, terima kasih
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 primary key values. If an entity with the given primary key values
/// 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 database for an entity with the given primary key values
/// and this entity, if found, is attached to the context and returned. If no entity is found, then
/// null is returned.
Menggabungkan beberapa saran saya usulkan sebagai berikut:
async Task<bool> UpdateDbEntryAsync<T>(T entity, params Expression<Func<T, object>>[] properties) where T : class
{
try
{
var entry = db.Entry(entity);
db.Set<T>().Attach(entity);
foreach (var property in properties)
entry.Property(property).IsModified = true;
await db.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("UpdateDbEntryAsync exception: " + ex.Message);
return false;
}
}
dipanggil oleh
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
Atau oleh
await UpdateDbEntryAsync(dbc, d => d.Property1);
Atau oleh
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
Saya menggunakan ValueInjecter
nuget 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.
public class NoNullsInjection : LoopInjection
{
protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
{
if (sp.GetValue(source) == null) return;
base.SetValue(source, target, sp, tp);
}
}
Pemakaian:
target.InjectFrom<NoNullsInjection>(source);
Cari jawaban ini
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.
Saya mencari yang sama dan akhirnya saya menemukan solusinya
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
percayalah itu bekerja untuk saya seperti pesona.
Inilah yang saya gunakan, menggunakan custom InjectNonNull (obj dest, obj src) yang membuatnya sepenuhnya fleksibel
[HttpPost]
public async Task<IActionResult> Post( [FromQuery]Models.Currency currency ) {
if ( ModelState.IsValid ) {
// find existing object by Key
Models.Currency currencyDest = context.Currencies.Find( currency.Id );
context.Currencies.Attach( currencyDest );
// update only not null fields
InjectNonNull( currencyDest, currency );
// save
await context.SaveChangesAsync( );
}
return Ok();
}
// Custom method
public static T InjectNonNull<T>( T dest, T src ) {
foreach ( var propertyPair in PropertyLister<T, T>.PropertyMap ) {
var fromValue = propertyPair.Item2.GetValue( src, null );
if ( fromValue != null && propertyPair.Item1.CanWrite ) {
propertyPair.Item1.SetValue( dest, fromValue, null );
}
}
return dest;
}
public async Task<bool> UpdateDbEntryAsync(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
{
try
{
this.Context.Set<TEntity>().Attach(entity);
EntityEntry<TEntity> entry = this.Context.Entry(entity);
entry.State = EntityState.Modified;
foreach (var property in properties)
entry.Property(property).IsModified = true;
await this.Context.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
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? :-)