Yanıtlar:
Ladislav'ın cevabı DbContext'i kullanmak üzere güncellendi (EF 4.1'de tanıtıldı):
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;
ve kullanılamayacağıdb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
, güncellediğiniz alanı doğrulamaya devam etmek isteyebilirsiniz bahsetmeye değer olduğunu düşünüyorumif (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
EF'e hangi özelliklerin bu şekilde güncellenmesi gerektiğini söyleyebilirsiniz:
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();
}
}
Temel olarak iki seçeneğiniz vardır:
userId
- tüm nesne yüklenirpassword
alanı güncelle.SaveChanges()
yöntemini kullanarak nesneyi geri kaydetmeBu durumda, bunun nasıl ele alınacağı EF'ye bağlıdır. Bunu sadece test ettim ve sadece bir nesnenin tek bir alanını değiştirdiğimde, EF'in oluşturduğu şey, manuel olarak da oluşturacağınız şeydir - şöyle bir şey:
`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`
EF, gerçekten hangi sütunların değiştiğini anlayacak kadar akıllıdır ve sadece gerekli olan güncellemeleri işlemek için bir T-SQL ifadesi oluşturur.
Password
verilen sütunu güncelleyin UserId
ve başka bir şey yapmayın - temel olarakUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) ve EF modelinizde bu saklı yordam için bir işlev içe aktarma oluşturursunuz ve bunu çağırırsınız. yukarıda özetlenen adımları yerineEntity Framework Core'da Attach
girişi döndürür, böylece ihtiyacınız olan tek şey:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Bunu kullanıyorum:
varlık:
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; }
}
erişimci kodu:
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();
Bu soruna bir çözüm ararken, Patrick Desjardins'in blogu aracılığıyla GONeale'ın cevabında bir varyasyon buldum :
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();
}
" Gördüğünüz gibi, ikinci parametresi olarak bir işlevin ifadesini alır. Bu, hangi yöntemi güncelleyeceğini Lambda ifadesinde belirterek bu yöntemi kullanmanıza izin verir. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(Burada biraz benzer bir çözüm de verilmiştir: https://stackoverflow.com/a/5749469/2115384 )
Şu anda kendi kodumda kullandığım yöntem , (Linq) türündeki ifadeleri de işleyecek şekilde genişletildi ExpressionType.Convert
. Bu benim durumumda, örneğin Guid
ve diğer nesne özellikleri ile gerekliydi . Bunlar bir Convert () içine 'sarılmıştır' ve bu nedenle tarafından ele alınmamıştır 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();
}
Burada oyuna geç kaldım, ama ben böyle yapıyorum, memnun kaldım bir çözüm aramak için biraz zaman harcadım; UPDATE
web form enjeksiyonunu yine de önlemek için daha güvenli bir "beyaz liste" kavramı aracılığıyla açık bir şekilde tanımladığınız için, bu SADECE değiştirilen alanlar için bir ifade üretir .
ISession veri havuzumdan bir alıntı:
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;
}
İsterseniz bu bir try..catch sarılmış olabilir, ama ben şahsen bu senaryodaki istisnalar hakkında bilmek benim arayan gibi.
Bu tarzda bir şey olarak adlandırılır (benim için bu bir ASP.NET Web API'sı yoluyla oldu):
if (!session.Update(franchiseViewModel.Franchise, new[]
{
"Name",
"StartDate"
}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
UpdateModel
komutu için gerekli beyaz liste gibi) güncellenmesine izin verdiğinizi açıkça belirtmelisiniz , bu şekilde bilgisayar korsanı form enjeksiyonunun gerçekleşememesini ve güncellenmesine izin verilmeyen alanları güncelleyememelerini sağlarsınız. Bununla birlikte, biri dize dizisini bir çeşit lambda ifadeleri parametresine dönüştürebilir ve onunla çalışabilirse Update<T>
, harika
var entity=_context.Set<T>().Attach(item);
takip entity.Property(propertyName).IsModified = true;
etmelidir.
Varlık çerçevesi, veritabanından DbContext aracılığıyla sorguladığınız nesnelerdeki değişikliklerinizi izler. Örneğin, DbContext örnek adı dbContext ise
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
Bu eski bir iplik olduğunu biliyorum ama ben de benzer bir çözüm arıyordum ve sağlanan çözüm ile gitmeye karar verdi @ Doku-so sağlanan. @ İmran Rizvi tarafından sorulan soruya cevap vermek için yorum yapıyorum, benzer bir uygulama gösteren @ Doku-so linkini takip ettim. @Imran Rizvi'nin sorusu, 'Lambda ifadesi' İfade> [] 'türüne dönüştürülemiyor çünkü bir delege türü olmadığı için' sağlanan çözümü kullanarak bir hata alıyordu. @ Doku-so'nun çözümünde, başka birinin bu yazıya rastlaması ve @ Doku-so'nun çözümünü kullanmaya karar vermesi durumunda bu hatayı düzelten küçük bir değişiklik sunmak istedim.
Sorun, Güncelleme yöntemindeki ikinci argüman,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Bu yöntemi sağlanan sözdizimini kullanarak çağırmak için ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
İkinci parametrenin önüne 'params' anahtar kelimesini eklemelisiniz.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
veya yöntem imzasını değiştirmek istemiyorsanız, Güncelleme yöntemini çağırmak için ' yeni ' anahtar kelimesini eklemeniz , dizinin boyutunu belirtmeniz ve ardından her bir özellik için görüldüğü gibi güncellenmesi için koleksiyon nesnesi başlatıcı sözdizimini kullanmanız gerekir. altında.
Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });
@ Doku-so örneğinde, bir dizi ifadeyi belirtiyor, bu nedenle bir dizide güncellenecek özellikleri geçirmeniz gerekiyor, çünkü dizi nedeniyle dizinin boyutunu da belirtmelisiniz. Bundan kaçınmak için, ifade bağımsız değişkenini dizi yerine IEnumerable kullanacak şekilde değiştirebilirsiniz.
İşte @ Doku-so'nun çözümünü uygulamam.
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();
}
Kullanımı:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so jenerik kullanarak serin bir yaklaşım sağladı, ben sorunumu çözmek için kavram kullandım ama sadece Doku-so'nun çözümünü olduğu gibi kullanamazsınız ve hem bu yazı hem de bağlantılı yazıda hiç kimse kullanım hatası sorularını cevaplamadı.
entityEntry.State = EntityState.Unchanged;
parametredeki tüm güncellenmiş değerler entityEntry
geri
EntityFramework Core 2.x'te şunlara gerek yoktur Attach
:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;
// other property changes might come here
context.SaveChanges();
Bunu SQL Server'da denedim ve profil oluşturma:
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
Find, önceden yüklenmiş varlıkların SELECT tetiklememesini ve ayrıca gerekirse varlığı otomatik olarak (dokümanlardan) eklemesini sağlar:
/// 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.
Birkaç öneriyi birleştirerek aşağıdakileri öneriyorum:
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;
}
}
tarafından çağrıldı
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
Veya tarafından
await UpdateDbEntryAsync(dbc, d => d.Property1);
Veya tarafından
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
Kullandığım ValueInjecter
aşağıdaki kullanarak veritabanı Şahsiyeti meydana Modeli Bağlama enjekte etmek Nuget:
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();
}
Sunucudan boşsa Özellikler'i güncellemeyen özel kuralın kullanımına dikkat edin.
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);
}
}
Kullanımı:
target.InjectFrom<NoNullsInjection>(source);
Yukarı Bak Bu yanıtı
Özelliği kasıtlı olarak null için temizlenmiş olup olmadığını bilmiyorsunuz VEYA sadece herhangi bir değeri yoktu. Başka bir deyişle, özellik değeri yalnızca başka bir değerle değiştirilebilir, ancak silinemez.
Ben de aynısını arıyordum ve sonunda çözümü buldum
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
inan bana bir cazibe gibi benim için çalışıyor.
Bunu kullandığım, özel InjectNonNull (obj dest, obj src) kullanarak tamamen esnek kılıyor
[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
, doğru, karma şifreyi demek? :-)