这里是表格
用户
UserId
UserName
Password
EmailAddress
以及代码..
public void ChangePassword(int userId, string password){
//code to update the password..
}
这里是表格
用户
UserId
UserName
Password
EmailAddress
以及代码..
public void ChangePassword(int userId, string password){
//code to update the password..
}
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;
,需要包含哪个命名空间? - Johandb.Configuration.ValidateOnSaveEnabled = false;
,您可能希望继续验证正在更新的字段:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
。 - Ziul你可以通过以下方式告诉Entity Framework需要更新哪些属性:
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();
}
}
ExecuteUpdate
:终于来了!经过长时间的等待,EF Core 7.0现在有了一种原生支持的方法来运行UPDATE
(以及DELETE
)语句,并允许您使用任意的LINQ查询(.Where(u => ...)
),而无需先从数据库中检索相关实体:这个新的内置方法称为ExecuteUpdate
——请参见"EF Core 7.0有什么新功能?"。
ExecuteUpdate
正是为这些场景而设计的,它可以操作任何IQueryable
实例,并允许您更新任意数量行的特定列,同时始终在后台发出一个单独的UPDATE
语句,使其尽可能高效。
我们来看OP的示例,即更新特定用户的密码列:
dbContext.Users
.Where(u => u.Id == someId)
.ExecuteUpdate(b =>
b.SetProperty(u => u.Password, "NewPassword")
);
正如您所看到的,调用ExecuteUpdate
需要您调用SetProperty
方法,以指定要更新的属性以及要分配给它的新值。
EF Core将把此转换为以下UPDATE
语句:
UPDATE [u]
SET [u].[Password] = "NewPassword"
FROM [Users] AS [u]
WHERE [u].[Id] = someId
ExecuteDelete
可用于删除行:还有一个与 ExecuteUpdate
相对应的东西,叫做 ExecuteDelete
,正如其名称所示,可以用来一次性删除单个或多个行,而无需先获取它们。
// Delete users that haven't been active in 2022:
dbContext.Users
.Where(u => u.LastActiveAt.Year < 2022)
.ExecuteDelete();
与ExecuteUpdate
类似,ExecuteDelete
将在后台生成DELETE
SQL语句 — 在这种情况下,生成的SQL语句如下:
DELETE FROM [u]
FROM [Users] AS [u]
WHERE DATEPART(year, [u].[LastActiveAt]) < 2022
其他注意事项:
ExecuteUpdate
和ExecuteDelete
都是“终止”方法,这意味着一旦调用该方法,更新/删除操作将立即执行。您不应该在此之后调用dbContext.SaveChanges()
。SetProperty
方法感到好奇,并且对于为什么ExectueUpdate
没有使用成员初始化表达式(例如.ExecuteUpdate(new User { Email = "..." })
),则请参考GitHub问题的此评论(以及周围的评论)。Execute
(还有其他候选项)感到好奇,请参阅此评论和前面(相当长的)对话。ExecuteUpdateAsync
和ExecuteDeleteAsync
。Attach
方法会返回实体的入口对象,因此你只需要这样做:var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Entry
意味着附加实体。 - Joerg KrauseIn this case, it's up to EF how to handle this in detail. I just tested this, and in the case I only change a single field of an object, what EF creates is pretty much what you'd create manually, too - something like:
````UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`
EF聪明到足以找出哪些列已经被更改,它将创建一个T-SQL语句来处理那些确实必要的更新。
UserId
的Password
列的更新操作(基本上是执行UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
)然后在你的EF模型中为该存储过程创建一个函数导入,并且调用这个函数,而不是执行上述步骤。我正在使用以下代码:
实体(entity):
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();
在寻找解决办法时,我发现了GONeale答案的一个变体,来自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();
}
"如你所见,此方法的第二个参数需要一个函数表达式。这样可以通过指定 Lambda 表达式来更新要更新的属性。"
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
我正在自己的代码中使用的一种类似的解决方案,扩展了处理 ExpressionType.Convert
类型的(Linq)表达式。在我的情况下,这是必需的,例如对于 Guid
和其他对象属性。这些属性被“包装”在 Convert() 中,因此无法被 System.Web.Mvc.ExpressionHelper.GetExpressionText
处理。
( 这里也提供了一个类似的解决方案:https://dev59.com/1FbTa4cB1Zd3GeqP-3V2#5749469)
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();
}
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
Find方法确保已加载的实体不会触发SELECT操作,并在需要时自动附加实体(来自文档):
使用给定的主键值查找实体。如果上下文正在跟踪具有给定主键值的实体,则立即返回该实体,而无需向数据库发送请求。否则,将向数据库查询具有给定主键值的实体,并将找到的此实体附加到上下文并返回。如果未找到实体,则返回null。
我来晚了,但这是我正在做的事情。我花了一段时间寻找一个令我满意的解决方案。这将只为更改的字段生成一个UPDATE
语句,因为您通过“白名单”概念明确定义了它们,这样更安全,可以防止Web表单注入。
以下是我的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 Web API):
if (!session.Update(franchiseViewModel.Franchise, new[]
{
"Name",
"StartDate"
}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
UpdateModel
命令所需的白名单一样),这样可以确保黑客表单注入不会发生,并且他们无法更新不允许更新的字段。但是,如果有人可以将字符串数组转换为某种lambda表达式参数并在Update<T>
中使用它,那就太好了。 - GONealevar entity=_context.Set<T>().Attach(item);
,然后跟着entity.Property(propertyName).IsModified = true;
应该可以工作。 - AuspexEntity framework 通过 DbContext 查询数据库中的对象并跟踪您对这些对象所做的更改。例如,如果您的 DbContext 实例名称为 dbContext。
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}