ইএফ-তে কোনও প্যারেন্ট সত্তাকে আপডেট করার সময় কীভাবে শিশু সত্তা যুক্ত করতে / আপডেট করতে হয়


151

দুটি সত্তা হ'ল একের সাথে একাধিক সম্পর্ক (কোড ফার্স্ট ফ্লুন্ট এপিআই দ্বারা নির্মিত)।

public class Parent
{
    public Parent()
    {
        this.Children = new List<Child>();
    }

    public int Id { get; set; }

    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }

    public int ParentId { get; set; }

    public string Data { get; set; }
}

আমার ওয়েবএপিআই কন্ট্রোলারে আমার প্যারেন্ট সত্তা তৈরি করার ক্রিয়া রয়েছে (যা ভাল কাজ করছে) এবং পিতামাতার সত্তাকে আপডেট করতে (যা কিছু সমস্যা আছে)। আপডেট ক্রিয়াটি দেখে মনে হচ্ছে:

public void Update(UpdateParentModel model)
{
    //what should be done here?
}

বর্তমানে আমার দুটি ধারণা রয়েছে:

  1. একটি ট্র্যাক পিতা বা মাতা স্বত্ত্বার নাম পান existingদ্বারা model.Id, এবং বরাদ্দ মান modelসত্তা একের পর এক। এটি বোকা লাগছে। এবং model.Childrenআমি জানি না কোন শিশুটি নতুন, কোন শিশুটি সংশোধিত হয়েছে (বা এমনকি মুছে ফেলা হয়েছে)।

  2. এর মাধ্যমে একটি নতুন প্যারেন্ট সত্তা তৈরি করুন modelএবং এটি DbContext এর সাথে সংযুক্ত করুন এবং এটি সংরক্ষণ করুন। কিন্তু ডিবি কনটেক্সট কীভাবে বাচ্চাদের অবস্থা জানতে পারে (নতুন যুক্ত / মুছা / সংশোধিত)?

এই বৈশিষ্ট্যটি বাস্তবায়নের সঠিক উপায় কী?


এছাড়াও GraphDiff সঙ্গে উদাহরণ দেখুন সদৃশ প্রশ্নে stackoverflow.com/questions/29351401/...
মাইকেল Freidgeim

উত্তর:


219

যেহেতু ওয়েবএপিআই নিয়ন্ত্রকের কাছে পোস্ট করা মডেলটি কোনও সত্তা-কাঠামো (ইএফ) প্রসঙ্গ থেকে আলাদা করা হয়েছে, কেবলমাত্র বিকল্পটি হ'ল অবজেক্ট গ্রাফ (তার বাচ্চাদের সহ অভিভাবক) ডাটাবেস থেকে লোড করা এবং কোন শিশুদের যুক্ত করা হয়েছে, মুছে ফেলা হয়েছে বা তুলনা করতে হবে আপডেট করা হয়েছে। (যদি না আপনি বিচ্ছিন্ন অবস্থায় (ব্রাউজারে বা যেখানেই থাকাকালীন) নিজের নিজস্ব ট্র্যাকিং ব্যবস্থার সাথে পরিবর্তনগুলি অনুসরণ না করেন যা আমার মতে নিম্নলিখিতগুলির চেয়ে জটিল)) এটি দেখতে দেখতে এটি দেখতে পারা যায়:

public void Update(UpdateParentModel model)
{
    var existingParent = _dbContext.Parents
        .Where(p => p.Id == model.Id)
        .Include(p => p.Children)
        .SingleOrDefault();

    if (existingParent != null)
    {
        // Update parent
        _dbContext.Entry(existingParent).CurrentValues.SetValues(model);

        // Delete children
        foreach (var existingChild in existingParent.Children.ToList())
        {
            if (!model.Children.Any(c => c.Id == existingChild.Id))
                _dbContext.Children.Remove(existingChild);
        }

        // Update and Insert children
        foreach (var childModel in model.Children)
        {
            var existingChild = existingParent.Children
                .Where(c => c.Id == childModel.Id)
                .SingleOrDefault();

            if (existingChild != null)
                // Update child
                _dbContext.Entry(existingChild).CurrentValues.SetValues(childModel);
            else
            {
                // Insert child
                var newChild = new Child
                {
                    Data = childModel.Data,
                    //...
                };
                existingParent.Children.Add(newChild);
            }
        }

        _dbContext.SaveChanges();
    }
}

...CurrentValues.SetValuesসম্পত্তির নামের ভিত্তিতে সংযুক্ত সত্তায় কোনও বস্তু এবং মানচিত্রের মান মানিয়ে নিতে পারে। যদি আপনার মডেলের সম্পত্তির নামগুলি সত্তার নামগুলির চেয়ে আলাদা হয় তবে আপনি এই পদ্ধতিটি ব্যবহার করতে পারবেন না এবং অবশ্যই একে একে মান নির্ধারণ করতে হবে।


35
তবে কেন ইফের আরও "উজ্জ্বল" উপায় নেই? আমি মনে করি ইএফ সনাক্ত করতে পারে যদি শিশুটি সংশোধিত / মুছে ফেলা / যুক্ত করা হয়, তবে উপরে আইএমও আপনার কোড EF কাঠামোর অংশ হতে পারে এবং আরও জেনেরিক সমাধান হতে পারে।
চেং চেন

7
@ ড্যানিচেন: প্রকৃতপক্ষে এটি একটি দীর্ঘ অনুরোধ যে সংযোগ বিচ্ছিন্ন সংস্থাগুলি হালনাগাদ করা আরও আরামদায়ক উপায়ে ( এনটিফ্রেমেওয়ার্ক.কম্প্লেক্স / ওয়ার্কেম / ৮8৪ ) ইএফ দ্বারা সমর্থন করা উচিত তবে এটি এখনও কাঠামোর অংশ নয়। বর্তমানে আপনি কেবলমাত্র তৃতীয় পক্ষের লাইব "গ্রাফডিফ" ব্যবহার করতে পারেন যা সেই কোডপ্লেক্স ওয়ার্কাইটেমে উল্লিখিত হয়েছে বা উপরে আমার উত্তরের মতো ম্যানুয়াল কোড লিখতে পারেন।
স্লুমা

7
যোগ করার জন্য একটি জিনিস: আপডেটের existingParent.Children.Add(newChild)পূর্বাভাসের মধ্যে বাচ্চাদের সন্নিবেশ করানোর মাধ্যমে আপনি এটি করতে পারবেন না কারণ বিদ্যমান চিল্ড লিনক অনুসন্ধানটি সম্প্রতি যুক্ত হওয়া সত্তাকে ফিরিয়ে দেবে, এবং সেই সত্তাটি আপডেট করা হবে। আপনাকে কেবল একটি অস্থায়ী তালিকায় sertোকানো এবং তারপরে যুক্ত করতে হবে।
এরে এফি 27'16

3
@ র্যান্ডল্ফআরঙ্কনফাদুল আমি কেবল এই সমস্যাটি নিয়ে এসেছি। আমার ফিক্স যা কিছুটা কম চেষ্টা করা existingChildলিনকিউ ক্যোয়ারিতে যেখানে ধারাটি পরিবর্তন করা হয়েছে :.Where(c => c.ID == childModel.ID && c.ID != default(int))
গ্যাভিন ওয়ার্ড

2
@ রাল্ফউইলগোস ২.২-এর ঠিক কী আপনি বলছেন?
জান পাওলো

11

আমি এই জাতীয় কিছু নিয়ে গণ্ডগোল করছি ...

protected void UpdateChildCollection<Tparent, Tid , Tchild>(Tparent dbItem, Tparent newItem, Func<Tparent, IEnumerable<Tchild>> selector, Func<Tchild, Tid> idSelector) where Tchild : class
    {
        var dbItems = selector(dbItem).ToList();
        var newItems = selector(newItem).ToList();

        if (dbItems == null && newItems == null)
            return;

        var original = dbItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();
        var updated = newItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();

        var toRemove = original.Where(i => !updated.ContainsKey(i.Key)).ToArray();
        var removed = toRemove.Select(i => DbContext.Entry(i.Value).State = EntityState.Deleted).ToArray();

        var toUpdate = original.Where(i => updated.ContainsKey(i.Key)).ToList();
        toUpdate.ForEach(i => DbContext.Entry(i.Value).CurrentValues.SetValues(updated[i.Key]));

        var toAdd = updated.Where(i => !original.ContainsKey(i.Key)).ToList();
        toAdd.ForEach(i => DbContext.Set<Tchild>().Add(i.Value));
    }

যা আপনি যেমন কিছু সঙ্গে কল করতে পারেন:

UpdateChildCollection(dbCopy, detached, p => p.MyCollectionProp, collectionItem => collectionItem.Id)

দুর্ভাগ্যক্রমে, বাচ্চাদের ধরণের সংগ্রহের বৈশিষ্ট্য রয়েছে যা আপডেট করার দরকার আছে যদি এই ধরণের উপর পড়ে যায়। একটি আইআরপোসিটরি (বেসিক সিআরইউডি পদ্ধতি সহ) পাস করার মাধ্যমে এটি সমাধানের চেষ্টা করার বিষয়টি বিবেচনা করে যা নিজেই আপডেটডিল্ডক্লিকেশন কল করার জন্য দায়ী। DbContext.Entry এ সরাসরি কলের পরিবর্তে রেপো কল করবে।

এগুলি কীভাবে স্কেলগুলি সম্পাদন করবে তা সম্পর্কে কোনও ধারণা নেই তবে এই সমস্যাটি নিয়ে আর কী করবেন তা নিশ্চিত নন।


1
দুর্দান্ত সমাধান! তবে যদি একাধিক নতুন আইটেম যুক্ত হয় তবে ব্যর্থ হয়, আপডেট করা অভিধানে দু'বার শূন্য আইডি থাকতে পারে। কিছুটা কাজ দরকার এবং যদি সম্পর্ক N -> N হয় তবে ব্যর্থ হয়, আসলে, আইটেমটি ডাটাবেসে যুক্ত করা হয়, তবে এন -> এন টেবিলটি পরিবর্তন করা হয় না।
রেনানস্ট্রি

1
toAdd.ForEach(i => (selector(dbItem) as ICollection<Tchild>).Add(i.Value));এন -> এন সমস্যার সমাধান করা উচিত।
রেনানস্ট্রি

10

ঠিক আছে ছেলেরা। আমার কাছে এই উত্তরটি একবার ছিল তবে এটি হারিয়ে ফেললাম। পরম নির্যাতন যখন আপনি জানেন যে আরও ভাল উপায় আছে তবে এটি মনে রাখতে বা এটি খুঁজে পেতে পারেন না! এটা খুবই সাধারণ. আমি এটি একাধিক উপায়ে পরীক্ষা করেছি।

var parent = _dbContext.Parents
  .Where(p => p.Id == model.Id)
  .Include(p => p.Children)
  .FirstOrDefault();

parent.Children = _dbContext.Children.Where(c => <Query for New List Here>);
_dbContext.Entry(parent).State = EntityState.Modified;

_dbContext.SaveChanges();

আপনি একটি নতুন একটি সঙ্গে পুরো তালিকা প্রতিস্থাপন করতে পারেন! এসকিউএল কোড সরিয়ে ফেলবে এবং প্রয়োজন অনুসারে সত্তা যুক্ত করবে। নিজেকে নিয়ে উদ্বেগের দরকার নেই। শিশু সংগ্রহ বা কোনও পাশা অন্তর্ভুক্ত করার বিষয়ে নিশ্চিত হন। শুভকামনা!


আমার যা প্রয়োজন কেবল তাই, যেমন আমার মডেলের বাচ্চাদের সংখ্যা সাধারণত খুব ছোট, তাই ধরে নিলাম লিনক প্রাথমিকভাবে সমস্ত মূল শিশুদের টেবিল থেকে মুছে ফেলবে এবং তারপরে নতুন সমস্তগুলি যুক্ত করবে পারফরম্যান্স ইফেক্ট কোনও সমস্যা নয়।
উইলিয়াম টি। ম্যালার্ড

পছন্দ করেছেন আপনি প্রাথমিক কোয়েরিতে অন্তর্ভুক্ত করার সময় কেন আপনি আবার বাচ্চাদের সেট করলেন আমি তা অনাবৃত করতে চাই না?
প্যানটোনিস

1
@ প্যান্টোনিস আমি শিশু সংগ্রহটি অন্তর্ভুক্ত করি যাতে এটি সম্পাদনার জন্য লোড করা যায়। আমি যদি এটি নির্ধারণ করতে অলস লোডিংয়ের উপর নির্ভর করি তবে এটি কাজ করে না। আমি বাচ্চাদের সেট করেছি (একবার) কারণ ম্যানুয়ালি মুছে ফেলা এবং সংগ্রহের আইটেমগুলি যুক্ত করার পরিবর্তে আমি কেবল তালিকাটি প্রতিস্থাপন করতে পারি এবং সত্তা ফ্রেমওয়ার্কটি আমার জন্য আইটেম যুক্ত এবং মুছবে। কীটি সত্তার স্থিতিটি সংশোধন করতে এবং অ্যান্টিফ্রেমওয়ার্ককে ভারী উত্তোলন করার অনুমতি দিচ্ছে।
চার্লস

@ চার্লসমিসি ইন্টোষ আমি এখনও বাচ্চাদের সাথে কী অর্জন করতে চাইছেন তা বুঝতে পারি না। আপনি এটি প্রথম অনুরোধে অন্তর্ভুক্ত করেছেন (অন্তর্ভুক্ত (p => পি। শিশুদের) আপনি কেন আবার অনুরোধ করবেন?
প্যান্টোনিস

@ প্যানটোনিস, আমাকে। অন্তর্ভুক্ত () ব্যবহার করে পুরানো তালিকাটি টানতে হয়েছিল তাই এটি ডাটাবেস থেকে সংগ্রহ হিসাবে লোড হয়ে যায় এবং সংযুক্ত হয়ে যায়। এটি কীভাবে অলস লোডিংয়ের অনুরোধ জানানো হয়। এটি ছাড়া, তালিকার কোনও পরিবর্তন ট্র্যাক করা হবে না যখন আমি সত্তা ব্যবহার করেছি। পুনরাবৃত্তি করতে, আমি যা করছি তা হ'ল বর্তমান শিশু সংগ্রহকে একটি ভিন্ন শিশু সংগ্রহের সাথে সেট করে। যেমন যদি কোনও পরিচালক নতুন কর্মচারীদের একটি গোছা পেয়ে থাকেন বা কয়েকজন হারিয়েছেন। আমি সেই নতুন কর্মীদের অন্তর্ভুক্ত বা বাদ দেওয়ার জন্য একটি কোয়েরি ব্যবহার করব এবং পুরাতন তালিকাটিকে কেবল নতুন তালিকার সাথে প্রতিস্থাপন করব তবে EF কে ডেটাবেস দিক থেকে প্রয়োজন অনুসারে যুক্ত বা মুছতে দিন।
চার্লস ম্যাকইনটোশ

9

আপনি যদি এনটিটি ফ্রেমওয়ার্ক কোর ব্যবহার করছেন তবে আপনি নিজের কন্ট্রোলার পোস্ট ক্রিয়ায় নিম্নলিখিতটি করতে পারেন ( সংযুক্তি পদ্ধতিটি সংগ্রহ সহ পুনরুদ্ধার করে নেভিগেশন বৈশিষ্ট্যগুলিকে সংযুক্ত করে):

_context.Attach(modelPostedToController);

IEnumerable<EntityEntry> unchangedEntities = _context.ChangeTracker.Entries().Where(x => x.State == EntityState.Unchanged);

foreach(EntityEntry ee in unchangedEntities){
     ee.State = EntityState.Modified;
}

await _context.SaveChangesAsync();

ধারণা করা হয় যে প্রতিটি সত্তা আপডেট হয়েছিল তার সমস্ত ক্লায়েন্টের পোস্ট ডেটাতে সেট করা এবং সরবরাহ করা আছে (যেমন, কোনও সত্তার আংশিক আপডেটের জন্য কাজ করবে না)।

আপনি এই অপারেশনের জন্য একটি নতুন / ডেডিকেটেড সত্তা ফ্রেমওয়ার্ক ডাটাবেস প্রসঙ্গ ব্যবহার করছেন তাও আপনাকে নিশ্চিত করতে হবে।


5
public async Task<IHttpActionResult> PutParent(int id, Parent parent)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != parent.Id)
            {
                return BadRequest();
            }

            db.Entry(parent).State = EntityState.Modified;

            foreach (Child child in parent.Children)
            {
                db.Entry(child).State = child.Id == 0 ? EntityState.Added : EntityState.Modified;
            }

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ParentExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Ok(db.Parents.Find(id));
        }

এইভাবেই আমি এই সমস্যার সমাধান করেছি। এইভাবে, EF জানে যে কোনটি আপডেট করতে হবে।


মোহন মত কাজ! ধন্যবাদ।
ইঙ্কটকিলার

2

এখানে কয়েকটি প্রকল্প রয়েছে যা ক্লায়েন্ট এবং সার্ভারের মধ্যে পারস্পরিক মিথস্ক্রিয়াটিকে আরও সহজ করে তোলে যতক্ষণ না এটি পুরো অবজেক্ট গ্রাফটি সংরক্ষণের বিষয় হিসাবে বিবেচনা করে।

এখানে দুটি আপনি দেখতে চাইছেন:

উপরের দুটি প্রকল্পই সংযোগ বিচ্ছিন্ন সংস্থাগুলি যখন সার্ভারে ফিরে আসে, পরিবর্তনগুলি সনাক্ত করে এবং সংরক্ষণ করে এবং ক্লায়েন্ট প্রভাবিত ডেটাতে ফিরে আসে তখন তা গ্রহণ করে।


1

কেবল ধারণার প্রমাণ Controler.UpdateModel সঠিকভাবে কাজ করবে না।

এখানে সম্পূর্ণ ক্লাস :

const string PK = "Id";
protected Models.Entities con;
protected System.Data.Entity.DbSet<T> model;

private void TestUpdate(object item)
{
    var props = item.GetType().GetProperties();
    foreach (var prop in props)
    {
        object value = prop.GetValue(item);
        if (prop.PropertyType.IsInterface && value != null)
        {
            foreach (var iItem in (System.Collections.IEnumerable)value)
            {
                TestUpdate(iItem);
            }
        }
    }

    int id = (int)item.GetType().GetProperty(PK).GetValue(item);
    if (id == 0)
    {
        con.Entry(item).State = System.Data.Entity.EntityState.Added;
    }
    else
    {
        con.Entry(item).State = System.Data.Entity.EntityState.Modified;
    }

}

0

@ চার্লস ম্যাকআইনটোস সত্যিই আমাকে আমার পরিস্থিতিটির উত্তর দিয়েছেন যাতে মডেল পাস করা আলাদা করা হয়েছিল। আমার জন্য শেষ পর্যন্ত যা কাজ করা হয়েছিল তা হ'ল প্রথমে পাস করা মডেলটিকে সংরক্ষণ করা ... তারপরে আমি আগে যেমন ছিলাম তেমন বাচ্চাদের যুক্ত করা চালিয়ে যাচ্ছি:

public async Task<IHttpActionResult> GetUPSFreight(PartsExpressOrder order)
{
    db.Entry(order).State = EntityState.Modified;
    db.SaveChanges();
  ...
}

0

ভিবি.এনইটি বিকাশকারীদের জন্য, শিশুটিকে সহজে ব্যবহার করার মতো অবস্থা হিসাবে চিহ্নিত করার জন্য এই জেনেরিক সাবটি ব্যবহার করুন

মন্তব্য:

  • প্রোম্যাটকন: সত্তা অবজেক্ট
  • amList: এমন বাচ্চার তালিকা যা আপনি যুক্ত করতে বা সংশোধন করতে চান
  • rList: আপনি মুছে ফেলতে চান এমন শিশু তালিকা
updatechild(objCas.ECC_Decision, PromatCon.ECC_Decision.Where(Function(c) c.rid = objCas.rid And Not objCas.ECC_Decision.Select(Function(x) x.dcid).Contains(c.dcid)).toList)
Sub updatechild(Of Ety)(amList As ICollection(Of Ety), rList As ICollection(Of Ety))
        If amList IsNot Nothing Then
            For Each obj In amList
                Dim x = PromatCon.Entry(obj).GetDatabaseValues()
                If x Is Nothing Then
                    PromatCon.Entry(obj).State = EntityState.Added
                Else
                    PromatCon.Entry(obj).State = EntityState.Modified
                End If
            Next
        End If

        If rList IsNot Nothing Then
            For Each obj In rList.ToList
                PromatCon.Entry(obj).State = EntityState.Deleted
            Next
        End If
End Sub
PromatCon.SaveChanges()


0

এখানে আমার কোডটি ঠিক কাজ করে।

public async Task<bool> UpdateDeviceShutdownAsync(Guid id, DateTime shutdownAtTime, int areaID, decimal mileage,
        decimal motohours, int driverID, List<int> commission,
        string shutdownPlaceDescr, int deviceShutdownTypeID, string deviceShutdownDesc,
        bool isTransportation, string violationConditions, DateTime shutdownStartTime,
        DateTime shutdownEndTime, string notes, List<Guid> faultIDs )
        {
            try
            {
                using (var db = new GJobEntities())
                {
                    var isExisting = await db.DeviceShutdowns.FirstOrDefaultAsync(x => x.ID == id);

                    if (isExisting != null)
                    {
                        isExisting.AreaID = areaID;
                        isExisting.DriverID = driverID;
                        isExisting.IsTransportation = isTransportation;
                        isExisting.Mileage = mileage;
                        isExisting.Motohours = motohours;
                        isExisting.Notes = notes;                    
                        isExisting.DeviceShutdownDesc = deviceShutdownDesc;
                        isExisting.DeviceShutdownTypeID = deviceShutdownTypeID;
                        isExisting.ShutdownAtTime = shutdownAtTime;
                        isExisting.ShutdownEndTime = shutdownEndTime;
                        isExisting.ShutdownStartTime = shutdownStartTime;
                        isExisting.ShutdownPlaceDescr = shutdownPlaceDescr;
                        isExisting.ViolationConditions = violationConditions;

                        // Delete children
                        foreach (var existingChild in isExisting.DeviceShutdownFaults.ToList())
                        {
                            db.DeviceShutdownFaults.Remove(existingChild);
                        }

                        if (faultIDs != null && faultIDs.Any())
                        {
                            foreach (var faultItem in faultIDs)
                            {
                                var newChild = new DeviceShutdownFault
                                {
                                    ID = Guid.NewGuid(),
                                    DDFaultID = faultItem,
                                    DeviceShutdownID = isExisting.ID,
                                };

                                isExisting.DeviceShutdownFaults.Add(newChild);
                            }
                        }

                        // Delete all children
                        foreach (var existingChild in isExisting.DeviceShutdownComissions.ToList())
                        {
                            db.DeviceShutdownComissions.Remove(existingChild);
                        }

                        // Add all new children
                        if (commission != null && commission.Any())
                        {
                            foreach (var cItem in commission)
                            {
                                var newChild = new DeviceShutdownComission
                                {
                                    ID = Guid.NewGuid(),
                                    PersonalID = cItem,
                                    DeviceShutdownID = isExisting.ID,
                                };

                                isExisting.DeviceShutdownComissions.Add(newChild);
                            }
                        }

                        await db.SaveChangesAsync();

                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex);
            }

            return false;
        }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.