سؤال

ها هي الجدول

المستخدمون

UserId
UserName
Password
EmailAddress

والرمز ..

public void ChangePassword(int userId, string password){
//code to update the password..
}
هل كانت مفيدة؟

المحلول

تم تحديث إجابة 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();
  }
}

نصائح أخرى

يمكنك إخبار 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();
  }
}

لديك خياران أساسا:

  • اذهب إلى طريق EF على طول الطريق ، في هذه الحالة ، هل ستفعل
    • قم بتحميل الكائن بناءً على userId المقدمة - يتم تحميل الكائن بأكمله
    • تحديث password مجال
    • احفظ الكائن مرة أخرى باستخدام السياق .SaveChanges() طريقة

في هذه الحالة ، يعود الأمر إلى EF كيفية التعامل مع هذا بالتفصيل. لقد اختبرت هذا للتو ، وفي حالة تغيير حقل واحد فقط من كائن ما ، فإن ما تنشئه EF هو إلى حد كبير ما تقوم بإنشائه يدويًا أيضًا - شيء مثل:

`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`

لذا فإن EF ذكي بما يكفي لمعرفة الأعمدة التي تغيرت بالفعل ، وستقوم بإنشاء عبارة T-SQL للتعامل مع تلك التحديثات الضرورية في الواقع.

  • يمكنك تحديد الإجراء المخزن الذي يقوم بالضبط بما تحتاجه ، في رمز 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();
}

"كما ترون ، يستغرق الأمر كمعلمة ثانية تعبيرًا عن وظيفة. سيسمح ذلك باستخدام هذه الطريقة من خلال تحديد تعبير 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 وغيرها من خصائص الكائن. كانت "ملفوفة" في تحويل () وبالتالي لم يتم التعامل معها بواسطة 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();
}

في Contity Framework Core ، Attach إرجاع الإدخال ، لذلك كل ما تحتاجه هو:

var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.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;
}

يمكن أن يكون هذا ملفوفًا في محاولة .. احرص على ذلك إذا كنت ترغب في ذلك ، لكني شخصياً أحب المتصل لمعرفة الاستثناءات الواردة في هذا السيناريو.

سيتم استدعاؤه في شيء مثل هذه الموضة (بالنسبة لي ، كان هذا عبر ASP.NET Web API):

if (!session.Update(franchiseViewModel.Franchise, new[]
    {
      "Name",
      "StartDate"
  }))
  throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));

أعلم أن هذا موضوع قديم لكنني كنت أبحث أيضًا عن حل مماثل وقررت الذهاب مع الحل @doku-so. أنا أعلق للإجابة على السؤال الذي طرحته Imran Rizvi ، لقد تابعت رابط @doku-so الذي يعرض تطبيقًا مشابهًا. كان سؤال Imran Rizvi هو أنه كان يحصل على خطأ باستخدام الحل المقدم "لا يمكن تحويل تعبير Lambda إلى كتابة" التعبير> [] "لأنه ليس نوعًا مندوبًا". أردت أن أقدم تعديلًا صغيرًا قمت به إلى حل @Doku-so الذي يحدد هذا الخطأ في حالة ظهور أي شخص آخر عبر هذا المنشور ويقرر استخدام حل @doku-so.

المشكلة هي الوسيطة الثانية في طريقة التحديث ،

public int Update(T entity, Expression<Func<T, object>>[] properties). 

لاستدعاء هذه الطريقة باستخدام بناء الجملة المتوفر ...

Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn); 

يجب عليك إضافة الكلمة الرئيسية "params" أمام الجثث الثاني كما هو الحال.

public int Update(T entity, params Expression<Func<T, object>>[] properties)

أو إذا كنت لا ترغب في تغيير توقيع الطريقة ، فعليك استدعاء طريقة التحديث ، فأنت تحتاج إلى إضافة ''الجديد"الكلمة الرئيسية ، حدد حجم المصفوفة ، ثم استخدم أخيرًا بناء جملة كائن المجموعة لكل خاصية لتحديثه كما هو موضح أدناه.

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 نهجًا رائعًا باستخدام Generic's ، لقد استخدمت المفهوم لحل مشكلتي ولكن لا يمكنك استخدام حل @doku-so كما هو وفي كل من هذا المنشور والمشاركة المرتبطة ، لم يرد أحد على أسئلة خطأ الاستخدام.

يتتبع إطار عمل الكيان التغييرات الخاصة بك على الكائنات التي استفسرت منها من قاعدة البيانات عبر DBContext. على سبيل المثال ، إذا كنت اسم مثيل DBContext هو dbContext

public void ChangePassword(int userId, string password){
     var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
     user.password = password;
     dbContext.SaveChanges();
}

في 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 وتوصيفه:

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

يضمن العثور على أن الكيانات التي تم تحميلها بالفعل لا تؤدي إلى تحديد وأيضًا يرفع الكيان تلقائيًا إذا لزم الأمر (من المستندات):

    ///     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.

أنا أستعمل 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();
}

لاحظ استخدام الاتفاقية المخصصة التي لا تحديث الخصائص إذا كانت خالية من الخادم.

ValueInjecter V3+

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);

حقن القيمة V2

ابحث عن هذا الجواب

تنبيه قضائي

لن تعرف ما إذا كانت العقار قد تم مسحه عمداً إلى NULL أم أنه لم يكن له أي قيمة. بمعنى آخر ، لا يمكن استبدال قيمة الخاصية إلا بقيمة أخرى ولكن لم يتم مسحها.

كنت أبحث عن نفسه وأخيراً وجدت الحل

using (CString conn = new CString())
{
    USER user = conn.USERs.Find(CMN.CurrentUser.ID);
    user.PASSWORD = txtPass.Text;
    conn.SaveChanges();
}

صدقني إنه يعمل بالنسبة لي مثل سحر.

الجمع بين العديد من الاقتراحات أقترح ما يلي:

    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;
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();
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top