답변:
Ladislav의 답변은 DbContext를 사용하도록 업데이트되었습니다 (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;
하지 않을 네임 스페이스db.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
당신이 업데이트하고있는 필드를 계속 검증하고 싶을 수도 있다고 언급 할 가치가 있다고 생각합니다 .if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
다음과 같은 방법으로 EF에 어떤 속성을 업데이트해야하는지 알 수 있습니다.
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();
}
}
기본적으로 두 가지 옵션이 있습니다.
userId
제공된 것을 기준으로 객체를로드-전체 객체가로드됩니다.password
필드를 업데이트.SaveChanges()
메소드를 사용하여 오브젝트를 다시 저장하십시오.이 경우이를 자세히 처리하는 방법은 EF에 달려 있습니다. 방금 이것을 테스트했으며 객체의 단일 필드 만 변경하는 경우 EF가 만드는 것은 수동으로 만드는 것입니다.
`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`
따라서 EF는 실제로 변경된 열을 파악할 수있을 정도로 똑똑하며 실제로 필요한 업데이트 만 처리하기 위해 T-SQL 문을 만듭니다.
Password
주어진 열을 업데이트하고 UserId
다른 것은 기본적으로 실행하지 않음 UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) EF 모델에서 해당 저장 프로 시저에 대한 함수 가져 오기를 작성하고 이것을 호출합니다. 위에서 설명한 단계를 수행하는 대신 기능나는 이것을 사용하고있다 :
실재:
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; }
}
접근 자 코드 :
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();
이 문제에 대한 해결책을 찾는 동안 Patrick Desjardins의 블로그를 통해 GONeale의 답변에서 변형이 발견되었습니다 .
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();
}
" 보시다시피, 두 번째 매개 변수로 함수의 표현식을 취합니다. 그러면 업데이트 할 특성을 Lambda 표현식에 지정하여이 메소드를 사용할 수 있습니다. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
( https://stackoverflow.com/a/5749469/2115384 ) 다소 비슷한 솔루션이 제공됩니다.
현재 내 코드에서 사용하고있는 메소드 는 (Linq) 유형의 표현식도 처리하도록 확장되었습니다 ExpressionType.Convert
. 이것은 필자의 경우 Guid
와 다른 객체 속성 과 함께 필요했습니다 . 그것들은 Convert ()에 '포장'되어 처리되지 않았습니다 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();
}
나는 여기서 게임에 늦었지만, 이것이 내가하는 일이다. 이렇게하면 UPDATE
웹 양식 삽입을 방지하기 위해보다 안전한 "화이트리스트"개념을 통해 필드를 명시 적으로 정의하므로 변경된 필드에 대해서만 명령문 이 생성 됩니다.
내 ISession 데이터 저장소에서 발췌 한 내용 :
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;
}
원하는 경우 try..catch로 묶을 수 있지만이 시나리오의 예외에 대해 발신자가 개인적으로 알고 싶습니다.
다음과 같은 방식으로 호출됩니다 (나에게 이것은 ASP.NET 웹 API를 통해 이루어졌습니다).
if (!session.Update(franchiseViewModel.Franchise, new[]
{
"Name",
"StartDate"
}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
UpdateModel
명령에 필요한 화이트리스트와 같이 업데이트 할 수있는 속성을 명시 적으로 명시해야합니다. 이렇게하면 해커 양식 삽입이 발생하지 않고 업데이트 할 수없는 필드를 업데이트 할 수 없습니다. 그러나 누군가가 문자열 배열을 일종의 람다 식 매개 변수로 변환하고 그와 함께 사용할 수 있다면 Update<T>
위대한
var entity=_context.Set<T>().Attach(item);
계속 entity.Property(propertyName).IsModified = true;
작동합니다.
엔티티 프레임 워크는 DbContext를 통해 데이터베이스에서 조회 한 오브젝트의 변경 사항을 추적합니다. 예를 들어 DbContext 인스턴스 이름이 dbContext 인 경우
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
나는 이것이 오래된 스레드라는 것을 알고 있지만 비슷한 솔루션을 찾고 있었고 @ Doku-so 제공 솔루션으로 가기로 결정했습니다. @Imran Rizvi가 묻는 질문에 대답하기 위해 댓글을 달고 있으며 유사한 구현을 보여주는 @ Doku-so 링크를 따랐습니다. @Imran Rizvi의 질문은 제공된 솔루션 'Landda식이 대리자 형식이 아니기 때문에 Lambda 식을'Expression> [] '유형으로 변환 할 수 없습니다. 다른 사람 이이 게시물을 발견하고 @ Doku-so의 솔루션을 사용하기로 결정한 경우이 오류를 수정하는 @ Doku-so의 솔루션에 작은 수정을 제공하고 싶었습니다.
문제는 Update 메서드의 두 번째 인수입니다.
public int Update(T entity, Expression<Func<T, object>>[] properties).
제공된 구문을 사용하여이 메소드를 호출하려면 ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
두 번째 arugment 앞에 'params'키워드를 추가해야합니다.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
또는 메소드 서명을 변경하지 않으려는 경우 Update 메소드를 호출하려면 ' new '키워드 를 추가 하고 배열의 크기를 지정한 다음 마지막으로 표시된대로 각 특성에 대해 콜렉션 오브젝트 이니셜 라이저 구문을 사용하십시오 이하.
Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });
@ Doku-so의 예에서 그는 Expressions 배열을 지정하므로 배열로 업데이트 할 속성을 전달해야합니다. 배열 때문에 배열의 크기도 지정해야합니다. 이를 피하기 위해 배열 대신 IEnumerable을 사용하도록 expression 인수를 변경할 수도 있습니다.
다음은 @ 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();
}
용법:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so는 generic을 사용하여 멋진 접근 방식을 제공했지만 개념을 사용하여 문제를 해결했지만 @ Doku-so의 솔루션을 그대로 사용할 수 없으며이 게시물과 링크 된 게시물 모두에서 아무도 사용 오류 질문에 대답하지 못했습니다.
entityEntry.State = EntityState.Unchanged;
매개 변수의 모든 업데이트 된 값을 entityEntry
되돌릴 때 솔루션을 작업 중이므로 변경 사항이 저장되지 않으므로 도움을 줄 수 있습니까
EntityFramework Core 2.x에서는 다음이 필요하지 않습니다 Attach
.
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;
// other property changes might come here
context.SaveChanges();
이것을 SQL Server에서 시도하고 프로파일 링했습니다.
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
찾기는 이미로드 된 엔티티가 SELECT를 트리거하지 않도록하고 필요한 경우 (문서에서) 엔티티를 자동으로 첨부합니다.
/// 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.
몇 가지 제안을 결합하여 다음을 제안합니다.
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;
}
}
에 의해 호출
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
또는
await UpdateDbEntryAsync(dbc, d => d.Property1);
또는
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
내가 사용하는 ValueInjecter
다음과 같은 사용하여 데이터베이스 엔티티로 모델을 바인딩 주입 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();
}
서버에서 속성이 null 인 경우 속성을 업데이트하지 않는 사용자 지정 규칙 사용에 유의하십시오.
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);
}
}
용법:
target.InjectFrom<NoNullsInjection>(source);
이 답변을 찾아보십시오
속성이 의도적으로 null로 지워 졌는지 또는 값이 없는지 알 수 없습니다. 즉, 속성 값은 다른 값으로 만 바꿀 수 있지만 지울 수는 없습니다.
나는 똑같은 것을 찾고 마침내 해결책을 찾았다.
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
나를 위해 매력처럼 작동합니다.
이것은 내가 사용하는 것입니다, 사용자 정의 InjectNonNull (obj dest, obj src)을 사용하여 완전히 유연하게 만듭니다.
[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
, 당신은 바로, 해시 된 암호를 의미? :-)