DbSet.Attach (সত্তা) বনাম DbContext.Entry (সত্তা)। স্টেট = সত্তা স্টেট.মোডিফাইড


115

যখন আমি কোনও বিচ্ছিন্ন দৃশ্যে থাকি এবং ক্লায়েন্টের কাছ থেকে একটি ডিটিও পাই যা আমি এটি সরিয়ে রাখার জন্য কোনও সত্তায় ম্যাপ করি আমি এটি করি:

context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();

তারপর কি জন্য DbSet.Attach(entity)

বা কেন যখন অ্যান্টিটিস্টেট.মোডাফায়েড ইতিমধ্যে সত্তাটি সংযুক্ত করে তখন আমাকে অ্যাটাক পদ্ধতিটি ব্যবহার করা উচিত?


কিছু সংস্করণ তথ্য যুক্ত করা ভাল, এটি আগে জিজ্ঞাসা করা হয়েছিল। এটি কোনও নতুন প্রশ্নের প্রাপ্য কিনা তা আমি পরিষ্কার নয়।
হেন্ক হলটারম্যান

উত্তর:


278

আপনি যখন করেন context.Entry(entity).State = EntityState.Modified;, আপনি কেবল সত্তার সাথে সত্তাটি সংযুক্ত করছেন না DbContext, আপনি পুরো সত্তাকেও মলিন হিসাবে চিহ্নিত করছেন। এর অর্থ হ'ল আপনি যখন করবেন context.SaveChanges(), EF একটি আপডেট স্টেটমেন্ট তৈরি করবে যা সত্তার সমস্ত ক্ষেত্র আপডেট করবে ।

এটি সর্বদা পছন্দসই নয়।

অন্যদিকে, DbSet.Attach(entity)প্রসঙ্গ সত্তা সংযুক্ত ছাড়া এটা মলিন উপলক্ষে। এটা করা সমানcontext.Entry(entity).State = EntityState.Unchanged;

এইভাবে সংযুক্ত করার সময়, আপনি যদি সত্তার উপরে কোনও সম্পত্তি আপডেট না করে, পরবর্তী সময় আপনি কল করবেন context.SaveChanges(), EF এই সত্তার জন্য কোনও ডাটাবেস আপডেট তৈরি করবে না।

এমনকি আপনি যদি কোনও সত্তাকে আপডেট করার পরিকল্পনা করে থাকেন, সত্তার যদি অনেকগুলি সম্পত্তি (ডিবি কলাম) থাকে তবে আপনি কেবল কয়েকটি আপডেট করতে চান তবে আপনাকে এটি করা সুবিধাজনক মনে হতে পারে DbSet.Attach(entity)এবং কেবলমাত্র কয়েকটি বৈশিষ্ট্য আপডেট করুন যে আপডেট করা প্রয়োজন। এটি এভাবে করা ইএফ থেকে আরও কার্যকর আপডেট স্টেটমেন্ট তৈরি করবে। EF কেবলমাত্র আপনার সংশোধিত বৈশিষ্ট্যগুলি আপডেট করবে ( context.Entry(entity).State = EntityState.Modified;যার বিপরীতে সমস্ত বৈশিষ্ট্য / কলামগুলি আপডেট হবে)

প্রাসঙ্গিক ডকুমেন্টেশন: যুক্ত / সংযুক্তি এবং সত্তা রাজ্য

কোড উদাহরণ

ধরা যাক আপনার নিম্নলিখিত সত্তা রয়েছে:

public class Person
{
    public int Id { get; set; } // primary key
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

যদি আপনার কোডটি এমন দেখাচ্ছে:

context.Entry(personEntity).State = EntityState.Modified;
context.SaveChanges();

উত্পাদিত এসকিউএল এর মতো কিছু দেখবে:

UPDATE person
SET FirstName = 'whatever first name is',
    LastName = 'whatever last name is'
WHERE Id = 123; -- whatever Id is.

উপরের আপডেটের বিবৃতিটি সমস্ত কলাম কীভাবে আপডেট করবে তা নির্ধারণ করুন, নির্বিশেষে বা আপনি প্রকৃত মানগুলি পরিবর্তন করেছেন কিনা।

বিপরীতে, যদি আপনার কোডটি "স্বাভাবিক" এর মতো সংযুক্তি ব্যবহার করে:

context.People.Attach(personEntity); // State = Unchanged
personEntity.FirstName = "John"; // State = Modified, and only the FirstName property is dirty.
context.SaveChanges();

তারপরে উত্পন্ন আপডেটের বিবৃতিটি আলাদা:

UPDATE person
SET FirstName = 'John'
WHERE Id = 123; -- whatever Id is.

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

এখন, কোন বিকল্পটি আপনার পক্ষে ভাল তা সম্পূর্ণরূপে আপনি কী করার চেষ্টা করছেন তার উপর নির্ভর করে।


1
EF এইভাবে যেখানে ক্লিউজ তৈরি করে না। আপনি যদি নতুন (যেমন নতুন সত্তা ()) দিয়ে তৈরি একটি সত্তা সংযুক্ত করে থাকেন এবং এটি পরিবর্তিত করে সেট করেন তবে আশাবাদী লকের কারণে আপনাকে সমস্ত মূল ক্ষেত্র সেট করতে হবে। আপডেটের ক্যোয়ারিতে উত্পন্ন শর্তটিতে সাধারণত সমস্ত আসল ক্ষেত্র থাকে (কেবল আইড নয়) তাই আপনি যদি এটি না করেন তবে ইএফ সম্মতিযুক্ত ব্যতিক্রম ছুঁড়ে ফেলবে।
বুবি

3
@ বুডি: আপনার মতামতের জন্য আপনাকে ধন্যবাদ আমি নিশ্চিত হওয়ার জন্য পুনরায় পরীক্ষা করেছি এবং একটি মৌলিক সত্তার জন্য, আমি যে বর্ণনাটি বর্ণনা করেছি তার সাথে এটি আচরণ করে, WHEREকেবল প্রাথমিক কী রয়েছে এবং কোনও সম্মতিহীন চেক ছাড়াই। একত্রে চেক করার জন্য, আমাকে একটি কলামকে স্পষ্টভাবে কনক্যুরঞ্জি টোকেন বা রো ভার্সন হিসাবে কনফিগার করতে হবে। সেক্ষেত্রে WHEREক্লজটিতে কেবলমাত্র প্রাথমিক কী এবং একত্রে টোকন কলাম থাকবে, সমস্ত ক্ষেত্র নয়। যদি আপনার পরীক্ষাগুলি অন্যথায় প্রদর্শিত হয় তবে আমি এটি সম্পর্কে শুনতে আগ্রহী।
সানস্ট

আমি কীভাবে গতিশীলভাবে ডাইনির সম্পত্তি সংশোধন করতে পারি?
নাভিদ_পিডিপি 11

2
@ নাভিদ_পিডিপি 11 DbContext.Entry(person).CurrentValuesএবং DbContext.Entry(person).OriginalValues
শিমি ওয়েটজ্যান্ডলার

কিছুটা সামান্য বিষয় হতে পারে, তবে আমি যদি একটি সংগ্রহস্থল প্যাটার্ন ব্যবহার করি তবে আমাকে প্রতিটি মডেলের জন্য একটি সংগ্রহশালা তৈরি করতে হবে কারণ প্রত্যেকটি মডেলের কিছু সত্তা রয়েছে যা ডিবিতে একটি নতুন রেকর্ড সন্নিবেশ করাকালীন আনট্র্যাকড অবস্থায় থাকা দরকার, তাই আমার থাকতে পারে না জেনেরিক সংগ্রহস্থল যা সন্নিবেশকালে প্রসঙ্গের সাথে সত্ত্বাকে সংযুক্ত করে। আপনি কিভাবে এই সেরা পরিচালনা করবেন?
জয়সূর্য_জ

3

আপনি যখন DbSet.Updateপদ্ধতিটি ব্যবহার করেন , সত্তা ফ্রেমওয়ার্ক আপনার সত্তার সমস্ত বৈশিষ্ট্য হিসাবে চিহ্নিত করে EntityState.Modified, সুতরাং সেগুলি ট্র্যাক করে। আপনি যদি কেবলমাত্র আপনার কয়েকটি সম্পত্তি পরিবর্তন করতে চান তবে সেগুলি সমস্তই ব্যবহার করুন DbSet.Attach। এই পদ্ধতিটি আপনার সমস্ত সম্পত্তি তৈরি করে EntityState.Unchanged, তাই আপনি আপডেট করতে চান এমন আপনার সম্পত্তি অবশ্যই তৈরি করতে হবে EntityState.Modified। সুতরাং যখন অ্যাপ্লিকেশনটিতে আঘাত আসে তখন DbContext.SaveChangesএটি কেবল পরিবর্তিত বৈশিষ্ট্যগুলি পরিচালনা করবে।


0

কেবলমাত্র (চিহ্নিত উত্তরে) এর মধ্যে এবং (ইএফ কোর) এর মধ্যে একটি গুরুত্বপূর্ণ পার্থক্য রয়েছে :context.Entry(entity).State = EntityState.Unchangedcontext.Attach(entity)

আমি নিজে আরও এটি বুঝতে আরও কিছু টেস্ট করেছি (অতএব এটিতে কিছু সাধারণ রেফারেন্স পরীক্ষাও অন্তর্ভুক্ত), সুতরাং এটি আমার পরীক্ষা-পরিস্থিতি:

  • আমি EF কোর 3.1.3 ব্যবহার করেছি
  • আমি ব্যবহার করতাম QueryTrackingBehavior.NoTracking
  • আমি ম্যাপিংয়ের জন্য কেবলমাত্র বৈশিষ্ট্য ব্যবহার করেছি (নীচে দেখুন)
  • অর্ডার পেতে এবং অর্ডার আপডেট করার জন্য আমি বিভিন্ন প্রসঙ্গ ব্যবহার করেছি
  • আমি প্রতিটি পরীক্ষার জন্য পুরো ডিবি মুছলাম

এই মডেলগুলি:

public class Order
{
    public int Id { get; set; }
    public string Comment { get; set; }
    public string ShippingAddress { get; set; }
    public DateTime? OrderDate { get; set; }
    public List<OrderPos> OrderPositions { get; set; }
    [ForeignKey("OrderedByUserId")]
    public User OrderedByUser { get; set; }
    public int? OrderedByUserId { get; set; }
}

public class OrderPos
{
    public int Id { get; set; }
    public string ArticleNo { get; set; }
    public int Quantity { get; set; }
    [ForeignKey("OrderId")]
    public Order Order { get; set; }
    public int? OrderId { get; set; }
}

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

এটি ডাটাবেসের (মূল) পরীক্ষার ডেটা: এখানে চিত্র বর্ণনা লিখুন

অর্ডার পেতে:

order = db.Orders.Include(o => o.OrderPositions).Include(o => o.OrderedByUser).FirstOrDefault();

এখন পরীক্ষা:

সত্তা স্টেট সহ সহজ আপডেট :

db.Entry(order).State = EntityState.Unchanged;
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be IGNORED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be IGNORED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 2 Calls:
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555', 1, 5)
// UPDATE [Orders] SET [ShippingAddress] = 'Germany' WHERE [Id] = 1

সংযুক্তি সহ সাধারণ আপডেট :

db.Attach(order);
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be UPDATED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be UPDATED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 1 Call:
// UPDATE [OrderPositions] SET [ArticleNo] = 'K-1234' WHERE [Id] = 1
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555 (NEW)', 1, 5)
// UPDATE [Orders] SET [ShippingAddress] = 'Germany' WHERE [Id] = 1
// UPDATE [Users] SET [FirstName] = 'William (CHANGED)' WHERE [Id] = 1

সত্তা স্টেটের সাথে চাইল্ড- আইডিগুলি পরিবর্তন করে আপডেট করুন :

db.Entry(order).State = EntityState.Unchanged;
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUser.Id = 3; // will be IGNORED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be IGNORED
order.OrderPositions[0].Id = 3; // will be IGNORED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be IGNORED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 2 Calls:
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555', 1, 5)
// UPDATE [Orders] SET [ShippingAddress] = 'Germany' WHERE [Id] = 1

সংযুক্তি সহ চাইল্ড-আইডিগুলি পরিবর্তন করে আপডেট করুন :

db.Attach(order);
order.ShippingAddress = "Germany"; // would be UPDATED
order.OrderedByUser.Id = 3; // will throw EXCEPTION
order.OrderedByUser.FirstName = "William (CHANGED)"; // would be UPDATED
order.OrderPositions[0].Id = 3; // will throw EXCEPTION
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // would be UPDATED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // would be INSERTED
db.SaveChanges();
// Throws Exception: The property 'Id' on entity type 'User' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.)

দ্রষ্টব্য: এটি ব্যতিক্রম ছোঁড়ে, আইডি পরিবর্তন করা হয়েছে বা মূল মান হিসাবে সেট করা থাকলেও আইডির অবস্থা "পরিবর্তিত" হিসাবে সেট করা আছে এবং এটি অনুমোদিত নয় (কারণ এটি প্রাথমিক কী)

নতুন-হিসাবে চাইল্ড-আইডিগুলি পরিবর্তনের সাথে আপডেট করুন (সত্তা স্ট্যাট এবং সংযুক্তির মধ্যে কোনও পার্থক্য নেই):

db.Attach(order); // or db.Entry(order).State = EntityState.Unchanged;
order.OrderedByUser = new User();
order.OrderedByUser.Id = 3; // // Reference will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be UPDATED (on User 3)
db.SaveChanges();
// Will generate SQL in 2 Calls:
// UPDATE [Orders] SET [OrderedByUserId] = 3, [ShippingAddress] = 'Germany' WHERE [Id] = 1
// UPDATE [Users] SET [FirstName] = 'William (CHANGED)' WHERE [Id] = 3

দ্রষ্টব্য: নতুন (উপরে) ছাড়াই সত্তা স্টেট সহ আপডেটের সাথে পার্থক্যটি দেখুন। নতুন ব্যবহারকারী উদাহরণের কারণে এবার নামটি আপডেট করা হবে।

সত্ত্বা -স্টেট সহ রেফারেন্স- আইডিগুলি পরিবর্তন করে আপডেট করুন :

db.Entry(order).State = EntityState.Unchanged;
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUserId = 3; // will be UPDATED
order.OrderedByUser.Id = 2; // will be IGNORED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be IGNORED
order.OrderPositions[0].Id = 3; // will be IGNORED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be IGNORED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 2 Calls:
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555', 1, 5)
// UPDATE [Orders] SET [OrderedByUserId] = 3, [ShippingAddress] = 'Germany' WHERE [Id] = 1

সংযুক্তি সহ রেফারেন্স-আইডিগুলি পরিবর্তন করে আপডেট করুন :

db.Attach(order);
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUserId = 3; // will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be UPDATED (on FIRST User!)
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be UPDATED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 1 Call:
// UPDATE [OrderPositions] SET [ArticleNo] = 'K-1234' WHERE [Id] = 1
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555 (NEW)', 1, 5)
// UPDATE [Orders] SET [OrderedByUserId] = 3, [ShippingAddress] = 'Germany' WHERE [Id] = 1
// UPDATE [Users] SET [FirstName] = 'William (CHANGED)' WHERE [Id] = 1

দ্রষ্টব্য: রেফারেন্স ব্যবহারকারী থেকে 3 পরিবর্তিত করা হবে, কিন্তু এছাড়াও 1 জন ব্যবহারকারী আপডেট করা হবে, আমি অনুমান এই কারণ order.OrderedByUser.Idঅপরিবর্তিত (এটি এখনও 1 এর)।

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

আমার জন্য সেরা কেস অর্ডারডবাইজারের মতো বস্তুগুলি বাতিল করতে এবং কেবল অর্ডার সেট করে।

আশা করি এটি সহায়তা করে, আমি জানি এটি অনেকগুলি পাঠ্য: ডি

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