Câu trả lời:
Câu trả lời của Ladislav được cập nhật để sử dụng DbContext (được giới thiệu trong 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;
và không sử dụngdb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
bạn có thể muốn tiếp tục xác thực lĩnh vực bạn đang cập nhật:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Bạn có thể nói với EF những thuộc tính nào phải được cập nhật theo cách này:
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();
}
}
Về cơ bản, bạn có hai tùy chọn:
userId
cung cấp - toàn bộ đối tượng được tảipassword
lĩnh vực.SaveChanges()
phương thức của bối cảnhTrong trường hợp này, tùy thuộc vào cách xử lý vấn đề này một cách chi tiết. Tôi mới thử nghiệm điều này và trong trường hợp tôi chỉ thay đổi một trường duy nhất của một đối tượng, những gì EF tạo ra cũng giống như những gì bạn tạo bằng tay - cũng giống như:
`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`
Vì vậy, EF đủ thông minh để tìm ra những cột nào thực sự đã thay đổi và nó sẽ tạo ra một câu lệnh T-SQL để xử lý chỉ những cập nhật thực sự cần thiết.
Password
cột cho đã cho UserId
và không có gì khác - về cơ bản thực thi UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) và bạn tạo một hàm nhập cho thủ tục được lưu trữ đó trong mô hình EF của bạn và bạn gọi đây là chức năng thay vì thực hiện các bước được nêu ở trênTrong Entity Framework Core, Attach
trả về mục nhập, vì vậy tất cả những gì bạn cần là:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Tôi đang sử dụng cái này:
thực thể:
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; }
}
mã truy cập:
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();
Trong khi tìm kiếm giải pháp cho vấn đề này, tôi đã tìm thấy một biến thể trên câu trả lời của GONeale thông qua blog của 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();
}
" Như bạn có thể thấy, nó lấy tham số thứ hai của nó là biểu thức của hàm. Điều này sẽ cho phép sử dụng phương thức này bằng cách chỉ định trong biểu thức Lambda thuộc tính nào cần cập nhật. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(Một giải pháp tương tự cũng được đưa ra ở đây: https://stackoverflow.com/a/5749469/2115384 )
Phương thức tôi hiện đang sử dụng trong mã của riêng tôi , được mở rộng để xử lý Biểu thức loại (Linq) ExpressionType.Convert
. Điều này là cần thiết trong trường hợp của tôi, ví dụ với Guid
và các thuộc tính đối tượng khác. Những cái đó được 'bọc' trong một Convert () và do đó không được xử lý bởi 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();
}
Tôi đến trễ trò chơi ở đây, nhưng đây là cách tôi đang thực hiện nó, tôi đã dành một lúc để tìm kiếm một giải pháp mà tôi đã bị bão hòa; điều này tạo ra một UPDATE
câu lệnh CHỈ cho các trường được thay đổi, vì bạn xác định rõ ràng chúng là gì thông qua khái niệm "danh sách trắng" an toàn hơn để ngăn chặn việc tiêm mẫu web.
Một đoạn trích từ kho dữ liệu ISession của tôi:
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;
}
Điều này có thể được gói trong một thử..catch nếu bạn muốn, nhưng cá nhân tôi thích người gọi của tôi để biết về các ngoại lệ trong kịch bản này.
Nó sẽ được gọi theo cách tương tự như thế này (đối với tôi, điều này là thông qua API Web ASP.NET):
if (!session.Update(franchiseViewModel.Franchise, new[]
{
"Name",
"StartDate"
}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
UpdateModel
lệnh của ASP.NET MVC ), theo cách đó bạn đảm bảo việc tiêm mẫu của hacker không thể xảy ra và họ không thể cập nhật các trường mà họ không được phép cập nhật. Tuy nhiên, nếu ai đó có thể chuyển đổi mảng chuỗi thành một loại tham số biểu thức lambda và làm việc với nó trong Update<T>
, tuyệt vời
var entity=_context.Set<T>().Attach(item);
theo sau entity.Property(propertyName).IsModified = true;
trong vòng lặp sẽ hoạt động.
Khung thực thể theo dõi các thay đổi của bạn trên các đối tượng mà bạn đã truy vấn từ cơ sở dữ liệu thông qua DbContext. Ví dụ: nếu tên đối tượng DbContext của bạn là dbContext
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
Tôi biết đây là một chủ đề cũ nhưng tôi cũng đang tìm kiếm một giải pháp tương tự và quyết định đi theo giải pháp @ Doku-so cung cấp. Tôi đang bình luận để trả lời câu hỏi của @Imran Rizvi, tôi đã theo liên kết @ Doku-so cho thấy cách thực hiện tương tự. Câu hỏi của @Imran Rizvi là anh ta đã gặp lỗi khi sử dụng giải pháp được cung cấp 'Không thể chuyển đổi biểu thức Lambda thành Loại' Biểu thức> [] 'vì đó không phải là loại đại biểu'. Tôi muốn cung cấp một sửa đổi nhỏ mà tôi đã thực hiện cho giải pháp của @ Doku-so để khắc phục lỗi này trong trường hợp có ai khác đi qua bài đăng này và quyết định sử dụng giải pháp của @ Doku-so.
Vấn đề là đối số thứ hai trong phương thức Cập nhật,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Để gọi phương thức này bằng cú pháp được cung cấp ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
Bạn phải thêm từ khóa 'params' trước phần thứ hai.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
hoặc nếu bạn không muốn thay đổi chữ ký phương thức thì để gọi phương thức Cập nhật, bạn cần thêm từ khóa ' mới ', chỉ định kích thước của mảng, sau đó sử dụng cú pháp khởi tạo đối tượng bộ sưu tập cho từng thuộc tính để cập nhật như đã thấy phía dưới.
Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });
Trong ví dụ của @ Doku-so, anh ấy đang chỉ định một mảng Biểu thức để bạn phải chuyển các thuộc tính để cập nhật trong một mảng, vì mảng đó bạn cũng phải chỉ định kích thước của mảng. Để tránh điều này, bạn cũng có thể thay đổi đối số biểu thức để sử dụng IEnumerable thay vì một mảng.
Đây là cách tôi thực hiện giải pháp @ Doku-so.
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();
}
Sử dụng:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-vì vậy cung cấp một cách tiếp cận tuyệt vời bằng cách sử dụng chung chung, tôi đã sử dụng khái niệm này để giải quyết vấn đề của mình nhưng bạn không thể sử dụng giải pháp của @ Doku-so và trong cả bài đăng này và bài đăng được liên kết không ai trả lời các câu hỏi lỗi sử dụng.
entityEntry.State = EntityState.Unchanged;
tất cả các giá trị được cập nhật trong tham số entityEntry
được hoàn nguyên, vì vậy không có thay đổi nào được lưu, bạn có thể vui lòng giúp đỡ về nó không, cảm ơn
Trong EntityFramework Core 2.x không cần Attach
:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;
// other property changes might come here
context.SaveChanges();
Đã thử điều này trong SQL Server và định hình nó:
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
Tìm đảm bảo rằng các thực thể đã được tải không kích hoạt CHỌN và cũng tự động đính kèm thực thể nếu cần (từ các tài liệu):
/// 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.
Kết hợp một số gợi ý tôi đề xuất như sau:
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;
}
}
gọi bằng
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
Hoặc bằng cách
await UpdateDbEntryAsync(dbc, d => d.Property1);
Hoặc bằng cách
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
Tôi sử dụng ValueInjecter
nuget để tiêm Binding Model vào cơ sở dữ liệu Thực thể bằng cách sử dụng như sau:
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();
}
Lưu ý việc sử dụng quy ước tùy chỉnh không cập nhật Thuộc tính nếu chúng không có trong máy chủ.
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);
}
}
Sử dụng:
target.InjectFrom<NoNullsInjection>(source);
Tra cứu câu trả lời này
Bạn sẽ không biết liệu tài sản có bị xóa một cách cố ý hay không HOẶC nó không có bất kỳ giá trị nào. Nói cách khác, giá trị tài sản chỉ có thể được thay thế bằng giá trị khác nhưng không bị xóa.
Tôi đã tìm kiếm tương tự và cuối cùng tôi đã tìm thấy giải pháp
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
tin tôi đi, nó làm việc cho tôi như một cơ duyên
Đây là những gì tôi sử dụng, sử dụng tùy chỉnh MethNonNull (obj Dest, obj src), nó làm cho nó hoàn toàn linh hoạt
[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
, bạn có nghĩa là mật khẩu băm, phải không? :-)