回答:
DbContextを使用するように更新されたLadislavの回答(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();
}
}
基本的に2つのオプションがあります。
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();
この問題の解決策を探しているときに、パトリックデジャルダンのブログで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();
}
「ご覧のとおり、2番目のパラメーターとして関数の式を受け取ります。これにより、更新するプロパティを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
変更されたフィールドに対してのみステートメントが生成されます。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
コマンドに必要なホワイトリストのように)更新を許可するプロパティを明示的に指定する必要があります。これにより、ハッカーフォームインジェクションが発生せず、更新が許可されていないフィールドを更新できなくなります。ただし、誰かが文字列配列をある種のラムダ式パラメーターに変換し、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の質問は、提供されたソリューションを使用してエラーが発生していたことです。@ Doku-soのソリューションに小さな変更を加えて、他の誰かがこの投稿に遭遇し、@ Doku-soのソリューションを使用することにした場合に備えて、このエラーを修正したいと思いました。
問題は、Updateメソッドの2番目の引数です。
public int Update(T entity, Expression<Func<T, object>>[] properties).
提供された構文を使用してこのメソッドを呼び出すには...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
そのため、2番目のラゲットの前に「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の例では、彼は式の配列を指定しているため、配列で更新するプロパティを渡す必要があります。配列の場合、配列のサイズも指定する必要があるためです。これを回避するために、配列の代わりにIEnumerableを使用するように式の引数を変更することもできます。
これが@ 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はジェネリックを使用したクールなアプローチを提供しました、私は問題を解決するためにコンセプトを使用しましたが、@ 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
Findは、既にロードされているエンティティが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
、ハッシュ化されたパスワードを意味しますよね?:-)